我有一个非常大的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初学者,所以我很确定我的代码效率不高.无论如何我可以更快地完成整个过程吗?
您可以使用以下脚本.我没有使用,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.小心的时候在哪里
tail -n +2 input.csv | LC_ALL=C sort -t, -k2 > sorted.csv.该tail部分删除标题.结论:即使您的输入未排序,排序然后运行第二个脚本也会更快.
| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |