如何使用bash替换一个文件中基于另一个文件中两列的多列值?

如何使用bash替换一个文件中基于另一个文件中两列的多列值?,bash,awk,Bash,Awk,我正在尝试使用awk替换文件中多列的值。使用awk的原因是文件非常大,无法将其加载到内存中。我试着和熊猫(蟒蛇)打交道 我有一个作为文本文件的大型数据库。我在这里放了一个文件中信息的示例(以制表符分隔): 如果前两列(CHROM,POS)在行中相同,我必须对标题中包含“_00”的列的值求和 因此,预期输出为: CHROM POS REF ALT GT_00 d_GT_00 c_GT_00 de_GT_00 can_GT_00 epi_GT_00 chr1

我正在尝试使用awk替换文件中多列的值。使用awk的原因是文件非常大,无法将其加载到内存中。我试着和熊猫(蟒蛇)打交道

我有一个作为文本文件的大型数据库。我在这里放了一个文件中信息的示例(以制表符分隔):

如果前两列(CHROM,POS)在行中相同,我必须对标题中包含“_00”的列的值求和

因此,预期输出为:

 CHROM   POS    REF     ALT     GT_00   d_GT_00  c_GT_00  de_GT_00  can_GT_00 epi_GT_00
 chr1    10      T       A       21       4       3           6       13       10      
 chr1    10      T       A       21       4       3           6       13       10
 chr1    10      T       G       21       4       3           6       13       10
 chr1    11      None    None     4       1       0           0       7         7
 chr1    11      G       T        4       1       0           0       7         7
我不知道该怎么做,因为我对编程非常陌生,所以,我必须用这个
awk
代码做下面的事情

 awk -F'\t' 'FNR==1{next};
   {keys[$1"\t"$2]
     for (i=5;i<=10;i++)
   {sum[$1"\t"$2, i] += $i}
   }END {for (key in keys) { printf "%s", key
   for (i=5;i<=10;i++) {printf "%s%s", "\t", sum[key,i]}  printf "\n"}} OFS='\t' out.txt
现在,我正在尝试替换
chr1 10
行中第一行的6个值,以及
chr1 11
行中第二行的6个值

我已经完成了使用以下代码更改一列中的值:

 awk -F"\t" 'NR==FNR{h[$1"\t"$2]=$3;next}
 {
   printf $1"\t"$2"\t"$3"\t"$4"\t"h[$1"\t"$2]"\t";
   for (i=6;i<=NF;i++)
   {printf "%s",$i "\t"};
    printf "\n"
  }' OFS="\t" file1 file2
awk-F“\t”'NR==FNR{h[$1”\t“$2]=$3;next}
{
printf$1“\t“$2”\t“$3”\t“$4”\t“h[$1”\t“$2]”\t”;

对于(i=6;i这里您使用了一个内存高效的perl on liner,它应该可以解决您的问题。您可能需要添加正确的输入字段分隔符,例如
-F'\t'
和一个正则表达式来跳过注释行

perl -lane 'if(!$prev || $prev eq "$F[0]:$F[1]"){push @r,[@F[4..$#F]]; push @snp,join"\t",@F[0..3]}else{for $r (@r){$o[$_]+=$$r[$_] for 0..scalar(@$r)-1}; print join"\t",($_,@o) for @snp; @snp=(join"\t",@F[0..3]); @o=(); @r=([@F[4..$#F]])} $prev="$F[0]:$F[1]"; END{for $r (@r){$o[$_]+=$$r[$_] for 0..scalar(@$r)-1}; print join"\t",($_,@o) for @snp;}' < \ 
<(echo -e "chr1 10 A T 1 2 3\nchr1 10 A G 1 2 3\nchr1 11 A T 4 5 6\nchr2 12 G C 7 8 9")

这是一个内存高效的perl on liner,可以解决您的问题。您可能需要添加正确的输入字段分隔符,例如
-F'\t'
和一个正则表达式来跳过注释行

perl -lane 'if(!$prev || $prev eq "$F[0]:$F[1]"){push @r,[@F[4..$#F]]; push @snp,join"\t",@F[0..3]}else{for $r (@r){$o[$_]+=$$r[$_] for 0..scalar(@$r)-1}; print join"\t",($_,@o) for @snp; @snp=(join"\t",@F[0..3]); @o=(); @r=([@F[4..$#F]])} $prev="$F[0]:$F[1]"; END{for $r (@r){$o[$_]+=$$r[$_] for 0..scalar(@$r)-1}; print join"\t",($_,@o) for @snp;}' < \ 
<(echo -e "chr1 10 A T 1 2 3\nchr1 10 A G 1 2 3\nchr1 11 A T 4 5 6\nchr2 12 G C 7 8 9")

好吧,如果你有,试试这个
datamash-H-g1,2 collapse 3,4 sum 5-10@oguzismail谢谢你的回答!我试过了,它似乎只对chr1的三个值中的两个求和10@oguzismail忘了我说过的话吧,当使用一个没有头的文件时,我保留了-H。它工作得很好!有没有办法撤消折叠并用它获取所有行最终的求和值?我不太了解datamash,但您可以使用awk撤消崩溃,只需将datamash的输出传输到
awk'BEGIN{FS=OFS=“\t”}{j=split($3,a,,”);split($4,b,”);for(i=1;iHi@oguzismail!你知道是否有可能对一系列列进行拆分吗?在我的实际数据中,我有大约80列要折叠,而且编写
拆分($n,a,,”)和$n=a[i]非常肮脏且不优雅
80次。我尝试嵌套两个for循环:1.迭代折叠的列,2.迭代数组“a”中的值,但无法得到所需的结果。如果有,请尝试此
datamash-H-g1,2 collapse 3,4 sum 5-10@oguzismail谢谢你的回答!我已经尝试过了,似乎只对三个值中的两个求和对于chr110@oguzismail忘了我说过的话吧,当使用一个没有头的文件时,我保留了-H。它工作得非常好!有没有办法撤消折叠并获得所有具有最终求和值的行?我不太了解datamash,但您可以使用awk撤消折叠,只需将datamash的输出传输到
awk'BEGIN{FS=OFS=“\t”}{j=split($3,a,,”)即可;split($4,b,“,”);for(i=1;iHi再次@oguzismail!您知道是否有可能对一系列列进行拆分吗?在我的实际数据中,我有大约80列要折叠,编写
split($n,a,“,”)和$n=a[i]非常肮脏且不优雅
80次。我试图嵌套两个for循环:1.对折叠的列进行迭代,2.对数组“a”中的值进行迭代,但无法得到所需的结果hanks@pyr0!我会看看。我从未编写过perl代码,我很难理解它。非常感谢您的注释!我必须替换#
在第二行的F之前?
F
是一个内置数组变量,由
-a
开关初始化为
@F
,可通过
$F[0]
$F[1]访问
,类似于awk中的
$1
$2
$#
是一个内置的perlvar,用于获取数组的最后一个索引,例如
$#F
了解更多信息,请参见@pyr0!我会看看。我从未编写过perl代码,很难理解它。非常感谢您的评论!我必须替换
#
在第二行的F之前?
F
是一个内置数组变量,由
-a
开关初始化为
@F
,可通过
$F[0]
$F[1]访问
,类似于awk中的
$1
$2
$#
是一个内置的perlvar,用于获取数组的最后一个索引,例如
$#F
,有关详细信息,请参阅
perl -lane 'if(!$prev || $prev eq "$F[0]:$F[1]"){push @r,[@F[4..$#F]]; push @snp,join"\t",@F[0..3]}else{for $r (@r){$o[$_]+=$$r[$_] for 0..scalar(@$r)-1}; print join"\t",($_,@o) for @snp; @snp=(join"\t",@F[0..3]); @o=(); @r=([@F[4..$#F]])} $prev="$F[0]:$F[1]"; END{for $r (@r){$o[$_]+=$$r[$_] for 0..scalar(@$r)-1}; print join"\t",($_,@o) for @snp;}' < \ 
<(echo -e "chr1 10 A T 1 2 3\nchr1 10 A G 1 2 3\nchr1 11 A T 4 5 6\nchr2 12 G C 7 8 9")
if(!$prev || $prev eq "$F[0]:$F[1]"){ # CHROM:POS compare to previous line
    push @r,[@F[4..$#F]]; # store values in array of array reference
    push @snp,join"\t",@F[0..3] # store CHROM,POS,REF,ALT
}else{
    for $r (@r){ # CHROM:POS is new
        $o[$_]+=$$r[$_] for 0..scalar(@$r)-1 # sum up values in array references
    };
    print join"\t",($_,@o) for @snp; # join CHROM,POS,REF,ALT with summed values
    @snp=(join"\t",@F[0..3]); # re-initialize
    @o=();
    @r=([@F[4..$#F]])
} 
$prev="$F[0]:$F[1]"; # store CHROM:POS info
END{ # print final lines
    for $r (@r){
        $o[$_]+=$$r[$_] for 0..scalar(@$r)-1
    };
    print join"\t",($_,@o) for @snp;
}