根据标题求和列

根据标题求和列

我有一个像这样的制表符分隔的 file1 (显示子集,实数矩阵 60x60000):

rowname   header1 header2 header3 header4 header5 header6 header7 header8
rowname1    1       1        10       2     3       1       10       2 
rowname2    0       7        200      3     37      1        2       1 

还有另一个 file2 像这样:

header1,header2
header3
header4,header5
header6,header8
header7

我想对 file2 的每一行中指定的列进行求和:

rowname    header1 header3 header4 header6 header7 
rowname1    2        10        5     3       10
rowname2    7        200       40    2       2

所以,column1+column2,column3,column4+column5,column6+column8,column7……

有些列必须求和,有些则不需要,并且要求和的列并不总是连续的。

如果对列进行求和,则第一列的标题需要保留在输出文件中。

我想知道你是否有解决方案awk。到目前为止,我只知道如何存储标头条目:

awk '
NR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }

答案1

awk '
  FNR==NR{
    newhdr[FNR]=$1       # new header name
    newhdrcnt++          # number of new header names
    for (i=1;i<=NF;i++)
      hdrnames[FNR]=$0   # save new header names comma-separated
    next
  }
  FNR==1{
    # save column numbers for new header names in array hdrcols
    for (i=1;i<=newhdrcnt;i++){
      n=split(hdrnames[i], oldhdr, ",")
      for(j=1;j<=n;j++){
        for(k=2;k<=NF;k++){
          if ($(k) == oldhdr[j]){
            hdrcols[i]=(j==1 ? "" : hdrcols[i] ",") k;
            if (j==n) break
          }
        }
      }
    }
    # print header
    printf $1
    for (i=1;i<=newhdrcnt;i++)
      printf FS newhdr[i]
    printf ORS
    next
  }
  { # print data
    printf $1
    for (i=1;i<=newhdrcnt;i++){
      n=split(hdrcols[i], cols, ",")
      res=0
      for(j=1;j<=n;j++)
        res=res+$(cols[j])
      printf FS res
    }
    printf ORS
  }
' FS="," file2 FS="\t" file1

输出:

rowname header1 header3 header4 header6 header7
rowname1        2       10      5       3       10
rowname2        7       200     40      2       2

答案2

我想我伤害了我的大脑,但是使用gawk

#!/usr/bin/awk -f

BEGIN{FS="\t"} 
  NR==FNR{H[NR]=","$0","; next}
  FNR==1{for (i=2;i<=NF; i++) T[i]=","$i","; printf $1; 
    for (i=1; i<=length(H); i++){split(H[i],sp,","); printf "\t"sp[2]}; print ""}
  FNR>1{delete S; for (i=2; i<=NF; i++) 
    {for (h in H) {if (H[h] ~ T[i]) S[h]+=$i}} printf $1; 
    for (i=1; i<=length(H); i++) printf "\t"S[i]; print""}

调用为

./script file2 file1 | column -t

输出

rowname   header1  header3  header4  header6  header7
rowname1  2        10       5        3        10
rowname2  7        200      40       2        2

根据 @paul_pedant 对 @freddy 的评论,未指定的列被删除

演练

设置FS

BEGIN{FS="\t"} 

加载要求和到数组中的列列表,H[]在每一端添加“,”(以避免后面的正则表达式不匹配)

  NR==FNR{H[NR]=","$0","; next}

如果它是数据文件的第一行,则加载数组中的列标题,T[]并在标题名称的每一端添加“,”$i

  FNR==1{for (i=2;i<=NF; i++) T[i]=","$i","; 

...并打印提取的标题作为总和指定字符串的第一部分

    printf $1; for (i=1; i<=length(H); i++){split(H[i],sp,","); printf "\t"sp[2]}; print ""}

对于每个数据行,清除求和数组S[],然后遍历字段

  FNR>1{delete S; for (i=2; i<=NF; i++)

检查每个H[]字段名称T[],如果存在,则将字段值添加到索引到匹配项的总和数组中S[h]

    {for (h in H) {if (H[h] ~ T[i]) S[h]+=$i}}

在每行的末尾打印

    printf $1; for (i=1; i<=length(H); i++) printf "\t"S[i]; print""}

相关内容