GNU并行和awk之间的冲突(拆分一列并过滤一些行)



我正在处理许多大型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和$3>20000(。我把它保存为txt。下面的命令非常有效。

zcat InputData.txt.gz | awk -F "_"  '$1=$1' | awk '{if ($2==1 && $3>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并行来加快这个过程,因为我有很多大的gz文件要处理。然而,GNU并行和awk之间似乎存在一些冲突,可能是在引用方面?

我尝试如下单独定义awk选项,但它在输出文件中没有给我任何信息。

在下面的命令中,我只在一个输入文件上运行并行。但我想在多个输入文件上运行,并保存多个输出文件,每个输出文件对应一个输入文件。

例如,

将Data_1.txt.gz输入到OutputData_1.txt

将Data_2.txt.gz输入到OutputData_2.txt

awk1='{ -F "_"  "$1=$1" }'
awk2='{if ($2==1 && $3>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($2,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",$2) ; print > ("OutputData.txt")}' 
#This also works using parallel
awkbody='{split($2,NewDF,"_")} NewDF[1]==chr && NewDF[2]>start && NewDF[2]<end {gsub("_"," ",$2) ; 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

https://www.gnu.org/software/parallel/man.html#QUOTING结论:

结论:为了避免处理引用问题,只需编写一个小脚本或函数(记住导出函数-f(并让GNU并行调用它可能会更容易。

所以:

doit() {
zcat "$1" |
awk -F "_"  '$1=$1' |
awk '{if ($2==1 && $3>20000) {print}}'
}
export -f doit
parallel 'doit {} > {=s/In/Out/; s/.gz//=}' ::: InputData*.txt.gz

一种方法是使用split

$ awk '{split($2,f2,"_")} 
f2[1]==1 && f2[2]>20000 {gsub("_"," ",$2); print > (FILENAME".output")}' file

但是,如果您通过stdin提供数据,awk将不会捕获要写入的文件名。您可能需要将其作为变量传递给脚本。。。

简单的解决方案是将过滤器组合到单个awk脚本中,而只有并行才能工作。

以下是一个仅扫描整个input.txt一次(性能的两倍(的示例解决方案:

awk 'BEGIN{FS="[ ]*[_]?"}$2==1 && $7 > 20000 {print}' input.txt

说明:

BEGIN{FS="[ ]*[_]?"}使字段分隔符为多个">"_">

$2==1 && $7 > 20000 {print}仅打印第2个字段==1且第7个字段>2000 的行

示例调试脚本:

BEGIN{FS="[ ]*[_]?"}
{
for(i = 1; i <= NF; i++) printf("$%d=%s%s",i, $i, OFS);
print "";
}
$2==1 && $7 > 20000 {print}

产品:

$1=gene $2=id $3=variant $4=id $5=tss $6=distance $7=ma $8=samples $9=ma $10=count $11=maf $12=pval $13=nominal $14=slope $15=slope $16=se 
$1=ENSG00000223972.4 $2=1 $3=13417 $4=C $5=CGAGA $6=b37 $7=1548 $8=50 $9=50 $10=0.0766871 $11=0.735446 $12=-0.0468165 $13=0.138428 
$1=ENSG00000223972.4 $2=1 $3=17559 $4=G $5=C $6=b37 $7=5690 $8=7 $9=7 $10=0.00964187 $11=0.39765 $12=-0.287573 $13=0.339508 
$1=ENSG00000223972.4 $2=1 $3=54421 $4=A $5=G $6=b37 $7=42552 $8=28 $9=28 $10=0.039548 $11=0.680357 $12=0.0741142 $13=0.179725 
ENSG00000223972.4   1_54421_A_G_b37 42552   28  28  0.039548    0.680357    0.0741142   0.179725
$1=ENSG00000223972.4 $2=1 $3=54490 $4=G $5=A $6=b37 $7=42621 $8=112 $9=120 $10=0.176471 $11=0.00824733 $12=0.247533 $13=0.093081 
ENSG00000223972.4   1_54490_G_A_b37 42621   112 120 0.176471    0.00824733  0.247533    0.093081

最新更新