基于列值将大文件拆分为较小文件的有效方法

dru*_*ist 2 bash awk

我有一个非常大的csv文件,我想将其拆分成较小的文件,以便大文件中具有相同ID值(csv中的第二列)的所有条目最终都在同一个文件中.但是,我还需要在每个较小的文件中有50个不同的ID.

我有这样做的代码,但对于1 gig文件,它需要大约15 - 20分钟.有没有一种有效的方法呢?

这就是我现在所拥有的:

awk -F, '{if(NR > 1) {print >> $2"_backfill_tmp.csv"; close($2"_backfill_tmp.csv")}}' $input_file
counter=0
for file in *"_backfill_tmp.csv"
do
  file_name=${input_file%.*}"_backfill2_part_"$PART_NUMBER".csv"
  cat "$file" >> "$file_name"
  rm "$file"
  (( counter++ ))
  if (( $counter % 50 == 0 )) ; then
    (( PART_NUMBER++ ))
  fi
done
Run Code Online (Sandbox Code Playgroud)

awk命令根据第2列的值将每一行写入单独的文件中(忽略第一行是标题),以便具有相同ID值的每一行最终都在同一文件中.我每次关闭文件因为我遇到了一个Too many files open error我无法ulimit在机器上设置的文件.但是,这个过程只需要15秒左右,所以不用担心.

然后我遍历每个临时创建的文件,并将它们写入单独的文件,直到$counter达到50(即将50个文件组合在一起).现在这是花费大量时间的地方.我猜是因为有很多个人ID文件,逐个打开它们并合并它们需要很长时间.

我是一个awk初学者,所以我很确定我的代码效率不高.无论如何我可以更快地完成整个过程吗?

Soc*_*owi 5

未排序输入的脚本

您可以使用以下脚本.我没有使用,close因为现在打开文件的数量只是#uniqueIDs/50而不是#uniqueIDs.

awk -F, 'NR > 1 {
  if (!($2 in mapIdToPart)) {
    if (uniqueIds % 50 == 0) {
      maxPart++;
    }
    mapIdToPart[$2] = maxPart;
    uniqueIds++;
  }
  print >> "part"mapIdToPart[$2]".csv";
}' input.csv
Run Code Online (Sandbox Code Playgroud)

这将创建文件part#.csv,其中#是当前零件的编号.输入文件不必排序.具有相同ID的行将转到相同的部分.每个部分中的行顺序对应于输入文件中的行顺序.每个部分有50个(或更少,最后一部分)唯一ID.

排序输入的脚本

当输入文件按ID排序时,您可以加快脚本速度,因为这样您就不需要映射,mapIdToPart并且每个生成的部分都是一次性写入的.

订单可以是字母,数字,......,无关紧要.在这里,我假设已排序的文件不再有标题.如果仍有标题,请NR > 1在awk脚本的开头添加.

awk -F, '{
  if ($2 != lastId) {
    lastId = $2;
    if (uniqueIds % 50 == 0) {
      close("part"maxPart".csv");
      maxPart++;
    }
    uniqueIds++;
  }
  print >> "part"maxPart".csv";
}' sorted.csv
Run Code Online (Sandbox Code Playgroud)

基准

为了测试脚本,我使用了生成样本数据

n=98""000""000; paste -d,
    <(shuf -i 10""000-99""000 -r -n "$n") \
    <(shuf -i 0-9""999 -r -n "$n") \
| cat <(echo data,id) - > input.csv
Run Code Online (Sandbox Code Playgroud)

样本数据有两列,9800万行,其中包含数字.那里有10万个唯一ID.小心的时候在哪里

  • 3m 54s在未排序的输入上运行第一个脚本.
  • 1m 19s用于对输入进行排序tail -n +2 input.csv | LC_ALL=C sort -t, -k2 > sorted.csv.该tail部分删除标题.
  • 1m 48s在排序的输入上运行第二个脚本.
  • 3m 07s用于排序和运行第二个脚本.

结论:即使您的输入未排序,排序然后运行第二个脚本也会更快.