计算第2列的总和和多个文件中所有其他列的平均值,而不考虑缺失值

时间:2017-06-28 03:34:49

标签: linux shell unix awk

我想计算第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 .
 .  .  .  . .    .  .  .  . .    .  .  .  . .  

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

 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*

但我无法将其修改为我的欲望输出。

2 个答案:

答案 0 :(得分: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)提取整数部分

答案 1 :(得分:1)

$ 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