【问题标题】:Compute sum for column 2 and average for all other columns in multiple files without considering missing values计算第 2 列的总和,计算多个文件中所有其他列的平均值,而不考虑缺失值
【发布时间】:2017-06-28 21:40:24
【问题描述】:

我想计算第 2 列的总和以及 15 个文件中所有其他列的平均值:- ifile1.txt、ifile2.txt、.....、ifile15.txt。每个文件的列数和行数相同。但其中一些是缺失值。部分数据看起来像

 ifile1.txt      ifile2.txt       ifile3.txt
 3  ?  ?  ? .    1  2  1  3 .    4  ?  ?  ? .
 1  ?  ?  ? .    1  ?  ?  ? .    5  ?  ?  ? .
 4  6  5  2 .    2  5  5  1 .    3  4  3  1 .
 5  5  7  1 .    0  0  1  1 .    4  3  4  0 .
 .  .  .  . .    .  .  .  . .    .  .  .  . .  

我想找到一个新文件,该文件将显示这 15 个文件中第 2 列的总和和所有其他列的平均值,而不考虑缺失值。

 ofile.txt
 2.66   2     1    3      . (i.e. average of 3 1 4, sum of ? 2 ?, average of ? 1 ?, average of ? 3 ?, and so on)
 2.33   ?     ?    ?      .
 3      15    4.33 1.33   .
 3      8     4    0.66   .
 .      .     .    .      .

这个问题与我之前的问题Average of multiple files without considering missing values 相似,其中脚本是为所有列的平均值编写的。

awk '
   {
   for (i = 1;i <= NF;i++) {
      Sum[FNR,i]+=$i
      Count[FNR,i]+=$i!="?"
      }
   }
END {
   for( i = 1; i <= FNR; i++){
      for( j = 1; j <= NF; j++) printf "%s ", Count[i,j] != 0 ? Sum[i,j]/Count[i,j] : "?"
      print ""
      }
   }
' ifile*

但我无法将其修改为我想要的输出。

【问题讨论】:

  • 我注意到平均值没有四舍五入,这是你想要的吗?
  • @CWLiu 。是的.. 我需要 %.2f
  • 不要将...s 添加到您提供的任何示例输入/输出中 - 它不会增加任何价值,只会混淆您的问题,这意味着我们需要在测试可能的解决方案之前手动删除它. %.2f 向上取整,而您发布的输出显示向下取整,因此 %.2f 不是您需要的(或者您发布的预期输出错误)。

标签: linux shell unix awk


【解决方案1】:

根据你之前的awk脚本,我修改如下,

$ cat awk_script
{
  for (i = 1;i <= NF;i++) {
    Sum[FNR,i]+=$i
    Count[FNR,i]+=$i!="?"                                                                                                                                                                                    
  }
}
END {
  for( i = 1; i <= FNR; i++){
    for( j = 1; j <= NF; j++) 
      if(j==2) { printf "%s\t" ,Count[i,j] != 0 ? Sum[i,j] : "?" }                                                                                                                                           
      else { 
        if (Count[i,j] != 0){ 
          val=Sum[i,j]/Count[i,j]
          printf "%s%s\t",int(val),match(val,/\.[0-9]/)!=0 ? "."substr(val,RSTART+1,2):""
        } else printf "?\t" 
      }   
    print ""
  }
}

输出将是:

$ awk -f awk_script ifile*
2.66    2       1       3       0
2.33    ?       ?       ?       0
3       15      4.33    1.33    0
3       8       4       0.66    0
0       0       0       0       0

简要说明,

  • if(j==2):打印每个文件中值的总和
  • 对于平均值,我注意到这些值没有四舍五入,所以使用substr(val,RSTART+1,2)提取小数部分,使用int(val)提取整数部分

【讨论】:

  • 非常感谢。但是,边界点“。”让我很困惑。我只是把它们放了,因为有更多的列和行。我试图修改/\.[0-9]/),但出现错误。请你把那些点去掉。
  • @Kay,我已经为你修改了答案。检查一下。
  • 非常感谢...我今天没有投票权.. 40 票都结束了.. 明天我会投票..
【解决方案2】:
$ cat tst.awk
BEGIN { dfltVal="?"; OFS="\t" }
{
    for (colNr=1; colNr<=NF; colNr++) {
        if ($colNr != dfltVal) {
            sum[FNR,colNr] += $colNr
            cnt[FNR,colNr]++
        }
    }
}
END {
    for (rowNr=1; rowNr<=FNR; rowNr++) {
        for (colNr=1; colNr<=NF; colNr++) {
            val = dfltVal
            if ( cnt[rowNr,colNr] != 0 ) {
                val = int(100 * sum[rowNr,colNr] / (colNr==2 ? 1 : cnt[rowNr,colNr])) / 100
            }
            printf "%s%s", val, (colNr<NF ? OFS : ORS)
        }
    }
}

.

$ awk -f tst.awk file1 file2 file3
2.66    2       1       3
2.33    ?       ?       ?
3       15      4.33    1.33
3       8       4       0.66

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多