我有一个非常大的文本文件(大约20 GB和3亿行),其中包含由制表符分隔的三列:
word1 word2 word3
word1 word2 word3
word1 word2 word3
word1 word2 word3
Run Code Online (Sandbox Code Playgroud)
word1,word2和word3在每一行中都不同.word3指定行的类,并经常为不同的行重复(具有数千个不同的值).目标是通过行类(word3)分隔文件.即word1和word2应该存储在一个名为word3的文件中,用于所有行.例如,对于该行:
a b c
Run Code Online (Sandbox Code Playgroud)
字符串"a b"应该附加到名为c的文件中.
现在我知道如何使用while循环,逐行读取文件,并为每一行附加适当的文件:
while IFS='' read -r line || [[ -n "$line" ]]; do
# Variables
read -a line_array <<< ${line}
word1=${line_array[0]}
word2=${line_array[1]}
word3=${line_array[2]}
# Adding word1 and word2 to file word3
echo "${word1} ${word2}" >> ${word3}
done < "inputfile"
Run Code Online (Sandbox Code Playgroud)
它工作,但速度很慢(即使我有一个带SSD的快速工作站).怎么加速?我已经尝试在/ dev/shm中执行此过程,并将文件拆分为10个并且为每个文件并行运行上面的脚本.但它仍然很慢.有没有办法进一步加快速度?
让我们生成一个示例文件:
$ seq -f "%.0f" 3000000 | awk -F $'\t' '{print $1 FS "Col_B" FS int(2000*rand())}' >file
Run Code Online (Sandbox Code Playgroud)
这会生成一个 300 万行文件,第 3 列中有 2,000 个不同的值,类似于以下内容:
$ head -n 3 file; echo "..."; tail -n 3 file
1 Col_B 1680
2 Col_B 788
3 Col_B 1566
...
2999998 Col_B 1562
2999999 Col_B 1803
3000000 Col_B 1252
Run Code Online (Sandbox Code Playgroud)
通过简单的操作,awk您可以生成您描述的文件:
$ time awk -F $'\t' '{ print $1 " " $2 >> $3; close($3) }' file
real 3m31.011s
user 0m25.260s
sys 3m0.994s
Run Code Online (Sandbox Code Playgroud)
这样 awk 将在大约 3 分 31 秒内生成 2,000 个组文件。当然比 Bash 更快,但是通过按第三列对文件进行预排序并一次性写入每个组文件可以更快。
sort您可以在管道中使用 Unix实用程序,并将输出提供给脚本,该脚本可以将排序后的组分隔到不同的文件中。使用-s选项 with sort,第三个字段的值将是唯一会更改行顺序的字段。
由于我们可以假设sort已根据文件的第 3 列将文件分区为组,因此脚本只需要检测该值何时发生变化:
$ time sort -s -k3 file | awk -F $'\t' 'fn != ($3 "") { close(fn); fn = $3 } { print $1 " " $2 > fn }'
real 0m4.727s
user 0m5.495s
sys 0m0.541s
Run Code Online (Sandbox Code Playgroud)
由于预分选提高了效率,相同的净过程只需 5 秒即可完成。
如果您确定第 3 列中的“单词”仅是 ascii(即,您不需要处理 UTF-8),您可以设置LC_ALL=C额外的速度:
$ time LC_ALL=C sort -s -k3 file | awk -F $'\t' 'fn != ($3 "") { close(fn); fn = $3 } { print $1 " " $2 > fn }'
real 0m3.801s
user 0m3.796s
sys 0m0.479s
Run Code Online (Sandbox Code Playgroud)
来自评论:
1)请添加一行来解释为什么我们需要括号中的表达式fn != ($3 ""):
awk的结构是使用您认为最具可读性的结构fn != ($3 "") {action}的有效快捷方式fn != $3 || fn=="" {action}。
2)不确定如果文件大于可用内存,这是否也有效,所以这可能是一个限制因素。:
我运行了第一个和最后一个 awk,包含 3 亿条记录和 20,000 个输出文件。最后一个使用 sort 的人在 12 分钟内完成了这项任务。第一个花了10个小时...
排序版本实际上可能具有更好的扩展性,因为打开、附加和关闭 20,000 个文件 3 亿次需要一段时间。将相似的数据组合起来并进行流式传输会更有效。
3)我之前考虑过排序,但后来觉得它可能不是最快的,因为我们必须用这种方法读取整个文件两次。:
这是纯随机数据的情况;如果实际数据有些排序,则需要权衡读取文件两次。第一个 awk 在随机数据较少的情况下会明显更快。但随后还需要时间来确定文件是否已排序。如果您知道文件大部分已排序,请使用第一个;如果可能有些混乱,请使用最后一个。
| 归档时间: |
|
| 查看次数: |
181 次 |
| 最近记录: |