shell中多个文件的平均值

时间:2015-07-27 05:43:16

标签: shell unix awk mean multiple-files

我想计算15个文件的平均值: - ifile1.txt,ifile2.txt,.....,ifile15.txt。每个文件的列数和行数相同。部分数据显示为

ifile1.txt      ifile2.txt       ifile3.txt
3  5  2  2 .    1  2  1  3 .    4  3  4  1 .
1  4  2  1 .    1  3  0  2 .    5  3  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个文件的平均值。

ofile.txt
2.66   3.33  2.33 2      . (i.e. average of 3 1 4, average of 5 2 3 and so on)
2.33   3.33  1    2.66   .
3      5     4.33 1.33   .
3      2.33  4    0.66   .
.      .     .    .      .

我正在尝试跟随,但收到错误

awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END 
{for (i=1; i<=rows; i++){for (j=1; j<=cols; j++) 
s+=$i;print $0,s/NF;s=0}}' ifile* > ofile.txt

3 个答案:

答案 0 :(得分:5)

正如所写:

awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END
…

你找不到命令&#39;作为错误,因为您必须在awk和引号内的脚本之间留一个空格。当你解决这个问题时,你会开始遇到问题,因为在脚本的第一行有两个}和只有一个{

当您解决问题时,您将需要一个2D数组,按行号和列号索引,对文件中的值求和。您还需要知道处理的文件数和列数。然后,您可以安排迭代END块中的2D数组。

awk 'FNR == 1 { nfiles++; ncols = NF }
     { for (i = 1; i < NF; i++) sum[FNR,i] += $i
       if (FNR > maxnr) maxnr = FNR
     }
     END {
         for (line = 1; line <= maxnr; line++)
         {
             for (col = 1; col < ncols; col++)
                  printf "  %f", sum[line,col]/nfiles;
             printf "\n"
         }
     }' ifile*.txt

给出问题中的三个数据文件:

<强> ifile1.txt

3 5 2 2
1 4 2 1
4 6 5 2
5 5 7 1

<强> ifile2.txt

1 2 1 3
1 3 0 2
2 5 5 1
0 0 1 1

<强> ifile3.txt

4 3 4 1
5 3 1 5
3 4 3 1
4 3 4 0

我展示的剧本产生:

  2.666667  3.333333  2.333333
  2.333333  3.333333  1.000000
  3.000000  5.000000  4.333333
  3.000000  2.666667  4.000000

如果您想将小数位数控制为2,请使用%.2f代替%f

答案 1 :(得分:4)

$ { head -n1 ifile1.txt; paste ifile*.txt;} | awk 'NR==1{d=NF; next;} {for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}}'
2.67    3.33    2.33    2.00
2.33    3.33    1.00    2.67
3.00    5.00    4.33    1.33
3.00    2.67    4.00    0.67

此脚本计算每一行并打印结果,然后再转到下一行。因此,脚本不需要立即将所有数据保存在内存中。如果数据文件很大,这很重要。

如何运作

  • { head -n1 ifile1.txt; paste ifile*.txt;}

    这只打印ifile1.txt的第一行。然后,paste命令使它打印合并的所有文件的第一行,然后合并第二行,依此类推:

    $ paste ifile*.txt
    3  5  2  2      1  2  1  3      4  3  4  1
    1  4  2  1      1  3  0  2      5  3  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
    
  • |

    管道符号使上述命令的输出作为输入发送到awk。依次解决每个awk命令:

  • NR==1{d=NF; next;}

    对于第一行,我们保存变量d中的列数。然后,我们跳过其余的命令,重新开始next输入行。

  • for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}

    这会将相应文件中的数字相加并打印平均值。

作为多行脚本:

{
    head -n1 ifile1.txt
    paste ifile*.txt
} | 
awk '
    NR==1 {d=NF; next;}

    {
        for (i=1;i<=d;i++)
        {
            s=0; for (j=i;j<=NF;j+=d)
                s+=$j;
            printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";
        }
    }

答案 2 :(得分:3)

当您阅读原始文件时,需要将字段的总和保存到数组中。您无法访问$0块中的iEND,因为那时没有输入行。

awk '{rows=FNR; cols=NF; for (i = 1; i <= NF; i++) { total[FNR, i] += $i }}
     FILENAME != lastfn { count++; lastfn = FILENAME }
     END { for (i = 1; i <= rows; i++) { 
                for (j =  1; j <= cols; j++) {
                    printf("%s ", total[i, j]/count)
                }
                printf("\n")
            }
        }' ifile* > ofile.txt