GNU parallel 和 awk 的冲突(拆分一列并过滤一些行)
Conflict between GNU parallel and awk (split a column and filter some rows)
我正在处理许多大型 gz 文件,如下例所示(此处仅显示前 5 行)。
gene_id variant_id tss_distance ma_samples ma_count maf pval_nominal slope slope_se
ENSG00000223972.4 1_13417_C_CGAGA_b37 1548 50 50 0.0766871 0.735446 -0.0468165 0.138428
ENSG00000223972.4 1_17559_G_C_b37 5690 7 7 0.00964187 0.39765 -0.287573 0.339508
ENSG00000223972.4 1_54421_A_G_b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
ENSG00000223972.4 1_54490_G_A_b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
下面是我想要的输出。
这里我把第二列用“_”分割,根据第二列和第三列(分割后)($2==1 and $3>20000)选行。我将其保存为 txt。下面的命令完美运行。
zcat InputData.txt.gz | awk -F "_" '=' | awk '{if (==1 && >20000) {print}}' > OutputData.txt
ENSG00000223972.4 1 54421 A G b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
ENSG00000223972.4 1 54490 G A b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
但是我想使用 GNU parallel 来加速这个过程,因为我有很多大的 gz 文件要处理。但是,GNU parallel 和 awk 之间似乎存在一些冲突,可能是在引用方面?
我尝试如下单独定义 awk 选项,但它没有在输出文件中提供任何内容。
在下面的命令中,我只运行并行处理一个输入文件。但是我想 运行 in 在多个输入文件上,并保存多个输出文件,每个文件对应一个输入文件。
例如,
InputData_1.txt.gz 到 OutputData_1.txt
InputData_2.txt.gz 到 OutputData_2.txt
awk1='{ -F "_" "=" }'
awk2='{if (==1 && >20000) {print}}'
parallel "zcat {} | awk '$awk1' |awk '$awk2' > OutputData.txt" ::: InputData.txt.gz
有人对此任务有什么建议吗?
非常感谢。
根据@karakfa 的建议,这是一个解决方案
chr=1
RegionStart=10000
RegionEnd=50000
zcat InputData.txt.gz | awk -v chr=$chr -v start=$RegionStart -v end=$RegionEnd '{split(,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",) ; print > ("OutputData.txt")}'
#This also works using parallel
awkbody='{split(,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",) ; print > ("{}_OutputData.txt")}'
parallel "zcat {} | awk -v chr=$chr -v start=$RegionStart -v end=$RegionEnd '$awkbody' " ::: InputData_*.txt.gz
输入文件 InputData_1.txt.gz
的输出文件名将是 InputData_1.txt.gz_OutputData.txt
一种方法是使用 split
$ awk '{split(,f2,"_")}
f2[1]==1 && f2[2]>20000 {gsub("_"," ",); print > (FILENAME".output")}' file
但是,如果您通过标准输入提供数据,awk
将不会捕获要写入的文件名。您可能需要将其作为变量传递给脚本...
简单的解决方案是将过滤器合并到单个 awk
脚本中,并且只有并行才能工作。
这是一个示例解决方案,它只扫描整个 input.txt
一次(性能的两倍):
awk 'BEGIN{FS="[ ]*[_]?"}==1 && > 20000 {print}' input.txt
解释:
BEGIN{FS="[ ]*[_]?"}
使字段分隔符多个" " 或 "_"
==1 && > 20000 {print}
仅打印第 2 个字段 == 1 且第 7 个字段 > 2000 的行
示例调试脚本:
BEGIN{FS="[ ]*[_]?"}
{
for(i = 1; i <= NF; i++) printf("$%d=%s%s",i, $i, OFS);
print "";
}
==1 && > 20000 {print}
生产:
=gene =id =variant =id =tss =distance =ma =samples =ma =count =maf =pval =nominal =slope =slope =se
=ENSG00000223972.4 =1 =13417 =C =CGAGA =b37 =1548 =50 =50 =0.0766871 =0.735446 =-0.0468165 =0.138428
=ENSG00000223972.4 =1 =17559 =G =C =b37 =5690 =7 =7 =0.00964187 =0.39765 =-0.287573 =0.339508
=ENSG00000223972.4 =1 =54421 =A =G =b37 =42552 =28 =28 =0.039548 =0.680357 =0.0741142 =0.179725
ENSG00000223972.4 1_54421_A_G_b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
=ENSG00000223972.4 =1 =54490 =G =A =b37 =42621 =112 =120 =0.176471 =0.00824733 =0.247533 =0.093081
ENSG00000223972.4 1_54490_G_A_b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
https://www.gnu.org/software/parallel/man.html#QUOTING总结:
Conclusion: To avoid dealing with the quoting problems it may be easier just to write a small script or a function (remember to export -f the function) and have GNU parallel call that.
所以:
doit() {
zcat "" |
awk -F "_" '=' |
awk '{if (==1 && >20000) {print}}'
}
export -f doit
parallel 'doit {} > {=s/In/Out/; s/.gz//=}' ::: InputData*.txt.gz
我正在处理许多大型 gz 文件,如下例所示(此处仅显示前 5 行)。
gene_id variant_id tss_distance ma_samples ma_count maf pval_nominal slope slope_se
ENSG00000223972.4 1_13417_C_CGAGA_b37 1548 50 50 0.0766871 0.735446 -0.0468165 0.138428
ENSG00000223972.4 1_17559_G_C_b37 5690 7 7 0.00964187 0.39765 -0.287573 0.339508
ENSG00000223972.4 1_54421_A_G_b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
ENSG00000223972.4 1_54490_G_A_b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
下面是我想要的输出。
这里我把第二列用“_”分割,根据第二列和第三列(分割后)($2==1 and $3>20000)选行。我将其保存为 txt。下面的命令完美运行。
zcat InputData.txt.gz | awk -F "_" '=' | awk '{if (==1 && >20000) {print}}' > OutputData.txt
ENSG00000223972.4 1 54421 A G b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
ENSG00000223972.4 1 54490 G A b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
但是我想使用 GNU parallel 来加速这个过程,因为我有很多大的 gz 文件要处理。但是,GNU parallel 和 awk 之间似乎存在一些冲突,可能是在引用方面?
我尝试如下单独定义 awk 选项,但它没有在输出文件中提供任何内容。
在下面的命令中,我只运行并行处理一个输入文件。但是我想 运行 in 在多个输入文件上,并保存多个输出文件,每个文件对应一个输入文件。
例如,
InputData_1.txt.gz 到 OutputData_1.txt
InputData_2.txt.gz 到 OutputData_2.txt
awk1='{ -F "_" "=" }'
awk2='{if (==1 && >20000) {print}}'
parallel "zcat {} | awk '$awk1' |awk '$awk2' > OutputData.txt" ::: InputData.txt.gz
有人对此任务有什么建议吗? 非常感谢。
根据@karakfa 的建议,这是一个解决方案
chr=1
RegionStart=10000
RegionEnd=50000
zcat InputData.txt.gz | awk -v chr=$chr -v start=$RegionStart -v end=$RegionEnd '{split(,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",) ; print > ("OutputData.txt")}'
#This also works using parallel
awkbody='{split(,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",) ; print > ("{}_OutputData.txt")}'
parallel "zcat {} | awk -v chr=$chr -v start=$RegionStart -v end=$RegionEnd '$awkbody' " ::: InputData_*.txt.gz
输入文件 InputData_1.txt.gz
的输出文件名将是 InputData_1.txt.gz_OutputData.txt
一种方法是使用 split
$ awk '{split(,f2,"_")}
f2[1]==1 && f2[2]>20000 {gsub("_"," ",); print > (FILENAME".output")}' file
但是,如果您通过标准输入提供数据,awk
将不会捕获要写入的文件名。您可能需要将其作为变量传递给脚本...
简单的解决方案是将过滤器合并到单个 awk
脚本中,并且只有并行才能工作。
这是一个示例解决方案,它只扫描整个 input.txt
一次(性能的两倍):
awk 'BEGIN{FS="[ ]*[_]?"}==1 && > 20000 {print}' input.txt
解释:
BEGIN{FS="[ ]*[_]?"}
使字段分隔符多个" " 或 "_"
==1 && > 20000 {print}
仅打印第 2 个字段 == 1 且第 7 个字段 > 2000 的行
示例调试脚本:
BEGIN{FS="[ ]*[_]?"}
{
for(i = 1; i <= NF; i++) printf("$%d=%s%s",i, $i, OFS);
print "";
}
==1 && > 20000 {print}
生产:
=gene =id =variant =id =tss =distance =ma =samples =ma =count =maf =pval =nominal =slope =slope =se
=ENSG00000223972.4 =1 =13417 =C =CGAGA =b37 =1548 =50 =50 =0.0766871 =0.735446 =-0.0468165 =0.138428
=ENSG00000223972.4 =1 =17559 =G =C =b37 =5690 =7 =7 =0.00964187 =0.39765 =-0.287573 =0.339508
=ENSG00000223972.4 =1 =54421 =A =G =b37 =42552 =28 =28 =0.039548 =0.680357 =0.0741142 =0.179725
ENSG00000223972.4 1_54421_A_G_b37 42552 28 28 0.039548 0.680357 0.0741142 0.179725
=ENSG00000223972.4 =1 =54490 =G =A =b37 =42621 =112 =120 =0.176471 =0.00824733 =0.247533 =0.093081
ENSG00000223972.4 1_54490_G_A_b37 42621 112 120 0.176471 0.00824733 0.247533 0.093081
https://www.gnu.org/software/parallel/man.html#QUOTING总结:
Conclusion: To avoid dealing with the quoting problems it may be easier just to write a small script or a function (remember to export -f the function) and have GNU parallel call that.
所以:
doit() {
zcat "" |
awk -F "_" '=' |
awk '{if (==1 && >20000) {print}}'
}
export -f doit
parallel 'doit {} > {=s/In/Out/; s/.gz//=}' ::: InputData*.txt.gz