我有一个包含数百万行和数千列/字段的输入文件。有人能向我解释一下,为什么下面两个产生相同输出的awk方法在CPU运行时间方面差异如此之大吗?
175.0秒:
awk 'BEGIN{FS=":| "}NR>1{field1=$1;field2=$2;$1="";$2="";print field1":"field2,field1":"field2,field2,$0}' file_in > file_out
19.7秒:
cat file_in | awk 'BEGIN{FS=":"}NR>1{print $1,$2}' | awk '{print $1":"$2,$1":"$2,$0}' | cut -d " " -f 3 --complement > file_out
这是一个只有数百列/字段的file_in的第2行和第3行(行与行之间没有换行符):
1:1000071 C T 1 0 0 1 0 0
1:1000759 C T 1 0 0 0 1 0
以下是file_out的相应行:
1:1000071 1:1000071 1000071 C T 1 0 0 1 0 0
1:1000759 1:1000759 1000759 C T 1 0 0 0 1 0
这两条语句:
$1="";$2=""
导致awk对每条记录重新编译两次。考虑到数百万行和数千个字段,我预计这将产生影响。
如果您向我们展示几行具有代表性的样本输入和预期输出,我们可以向您展示如何简洁高效地完成这项工作。
看起来你所做的就是转换这样的行:
1:1000071 C T 1 0 ...
1:1000759 C T 1 0 ...
到这样的线路:
1:1000071 1:1000071 1000071 C T 1 0 ...
1:1000759 1:1000759 1000759 C T 1 0 ...
如果是这样,你所需要做的就是:
awk '{x=$1; sub(/[^:]+:/,x" "x" ")}1' file
或者,由于这是一个简单的单行替换,即使是sed也可以处理它:
sed 's/([^:]*:)([^ ]*)/12 12 2/' file
外观:
$ cat file
1:1000071 C T 1 0 ...
1:1000759 C T 1 0 ...
$ awk '{x=$1; sub(/[^:]+:/,x" "x" ")}1' file
1:1000071 1:1000071 1000071 C T 1 0 ...
1:1000759 1:1000759 1000759 C T 1 0 ...
$ sed 's/([^:]*:)([^ ]*)/12 12 2/' file
1:1000071 1:1000071 1000071 C T 1 0 ...
1:1000759 1:1000759 1000759 C T 1 0 ...
啊,但我看到你提到你的样本输入是从第2行开始的,所以我想你有一个标题行或其他东西可以跳过。那就是:
awk 'NR>1{x=$1; sub(/[^:]+:/,x" "x" ");print}' file
sed -n '2,$s/([^:]*:)([^ ]*)/12 12 2/p' file
最后,这里有一个替代的awk解决方案,如果你的行都以"1:"开头,它可能会更有效,如你的示例输入所示:
awk 'NR>1{print $1, $1, substr($0,3)}' file
这仍然是最快的解决方案:
cat file_in | awk 'BEGIN{FS=":"}NR>1{print $1,$2}' | awk '{print $1":"$2,$1":"$2,$0}' | cut -d " " -f 3 --complement > file_out