【问题标题】:How to replace the value of multiple columns in a file based on two columns in another file with bash?如何用bash替换基于另一个文件中的两列的文件中多列的值?
【发布时间】:2019-08-27 11:43:31
【问题描述】:

我正在尝试使用 awk 替换文件中多个列的值。使用 awk 的原因是文件非常大,无法将其加载到内存中。我试过用熊猫(python)做。

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

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       7       1       1        2           5       7
chr1    10      T       A       7       1       1        3           0       1
chr1    10      T       G       7       2       1        1           8       2
chr1    11      None    None    2       0       0        0           5       4
chr1    11      G       T       2       1       0        0           2       3

如果行中的前两列 (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

使用此代码,并将第一个文本文件用作“out.txt”,我得到:

 chr1    10      21      4       3       6       13      10
 chr1    11      4       1       0       0       7       7

现在,我正在尝试替换带有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

但需要对所有列执行相同操作。

我怎样才能使用类似的代码来做到这一点?

注意:我有更多列的标题名称中没有“_00”

【问题讨论】:

  • 好吧,如果你有datamash,试试这个datamash -H -g1,2 collapse 3,4 sum 5-10 &lt;your_file
  • @oguzismail 感谢您的回答!我已经尝试过了,它似乎只将 chr1 10 的三个值中的两个相加
  • @oguzismail 忘记我说过的话,我在使用没有标题的文件时保留了 -H。它完美地工作!有没有办法撤消折叠并获得具有最终总和值的所有行?
  • 我不太了解datamash,但您可以使用awk 撤消折叠,只需将datamash 的输出通过管道传输到awk 'BEGIN{FS=OFS="\t"} {j=split($3,a,",");split($4,b,",");for(i=1;i&lt;=j;++i){$3=a[i];$4=b[i];print}}'
  • 你好@oguzismail!您知道是否可以对一系列列进行拆分?在我的真实数据中,我有大约 80 列要折叠,而且写 split($n,a,",") and $n=a[i] 80 次非常肮脏且不优雅。我试图嵌套两个for循环:1.迭代折叠的列,2.将值迭代到数组'a'中,但无法获得期望的结果

标签: bash awk


【解决方案1】:

在这里,您可以使用内存高效的 p​​erl 联机,它应该可以解决您的问题。您可能需要添加正确的输入字段分隔符,例如-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")

为您提供带有 cmets 的格式化版本 :)

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;
}

【讨论】:

  • 谢谢@pyr0!我会看看。我从来没有写过 perl 代码,我很难理解它
  • 非常感谢 cmets!我必须在第二行的 F 之前替换 # 吗?
  • F 是一个内置数组变量,由-a 开关初始化为@F,可通过$F[0]$F[1] 访问,类似于$1$2、..在 awk。 $# 是一个内置 perlvar,用于获取数组的最后一个索引,例如$#F 更多信息见perldoc.perl.org/perlvar.html
猜你喜欢
  • 2013-06-26
  • 1970-01-01
  • 2016-07-12
  • 2018-12-27
  • 2023-03-07
  • 1970-01-01
  • 2018-09-14
  • 2012-08-25
  • 2016-11-20
相关资源
最近更新 更多