使用Awk计算多个行的最大值并计算它们之间的平均值

时间:2015-06-03 22:34:43

标签: linux shell awk

我想分别计算第8列中以1.000,1.35,1.70,......(递增0.35)......,120(每行14行)的行之间的最大值,然后计算使用Awk它们之间的平均值(即最大值)。非常感谢您的帮助

1.000 8 .... 0.017947838827838864
1.000 8 .... 0.029306373626373672 
1.000 8 .... 0.018125164835164853
...
...
1.350 27 ... 0.0014171428571428946 
1.350 27 ... 0.0017828571428571971 
1.350 27 ... 0.0017828571428571971 
...
...
120.000 28 ... 0.49277503924646787
120.000 28 ... 0.41021689560439561
120.000 29 ... 0.38946329670329682

3 个答案:

答案 0 :(得分:0)

为了测试,假设以下输入文件:

1.000 8  0.017947838827838864
1.000 8  0.029306373626373672
1.000 8  0.018125164835164853
1.350 27  0.0014171428571428946
1.350 27  0.0017828571428571971
1.350 27  0.0017828571428571971
120.000 28  0.49277503924646787
120.000 28  0.41021689560439561
120.000 29  0.38946329670329682

使用以下awk文件:

BEGIN { initialize(); }
NF==3 { processline($1,$3);}
END { printmax(); printavg(); }

function initialize()
{
    lastselector=-1
    count=0
    sum=0
}

function processline(selector,value)
{
    if(selector!=lastselector) {
    if(lastselector!=-1) {
        printmax()
    }
    lastselector=selector
    max=value
    }
    else {
    if(value>max) {
        max=value
    }
    }
}

function printmax()
{
    print "selector=" lastselector "  max=" max
    sum=sum+max
    count=count+1
}

function printavg()
{
    avg=sum/count
    print "avgmax=" avg
}

结果是:

awk -f test.awk test.dat
selector=1.000  max=0.029306373626373672
selector=1.350  max=0.0017828571428571971
selector=120.000  max=0.49277503924646787
avgmax=0.174621

要适应您的问题,请将$ 3修改为$ 8(或您想要的任何列),并将NF = test设置为文件中预期的列总数。 (此模式只是为了排除不包含数据的其他行)未能设置该权限将导致不处理任何行并且除以零错误。此代码假定您的输入具有按行组合在一起的第1列的所有相同值。

希望这会有所帮助。顺便说一句,你的例子已经足够了。

答案 1 :(得分:0)

<强>输入

$ cat file
1.000 8  0.017947838827838864
1.000 8  0.029306373626373672
1.000 8  0.018125164835164853
1.350 27  0.0014171428571428946
1.350 27  0.0017828571428571971
1.350 27  0.0017828571428571971
120.000 28  0.49277503924646787
120.000 28  0.41021689560439561
120.000 29  0.38946329670329682

<强>输出

$ awk 'FNR==NR{A[$1] = $3 > A[$1] ? $3 : A[$1]; next }$1 in A{ print "selector = " $1 " max = "A[$1];sum+=A[$1]; c++; delete A[$1]  }END{print "Average  = ", sum/c}' file file
selector = 1.000 max = 0.029306373626373672
selector = 1.350 max = 0.0017828571428571971
selector = 120.000 max = 0.49277503924646787
Average  =  0.174621

更易读的版本:

 awk 'FNR==NR{
               # If filed3 ($3) is greater than array A element where index being field1,
               # then A[$1] = $3, otherwise array A value will not change
               A[$1] = $3 > A[$1] ? $3 : A[$1]

               # Stop processing go to next line
               next 
             }

              # Here we read same file once again
              # if index key $1 exists in array A
      $1 in A{ 
               # print field1 and max value
               print "selector = " $1 " max = "A[$1]

               # sum of max 
               sum+=A[$1] 

               # Count
               c++

               # Delete element of array
               delete A[$1]  
             }

          END{ 
                # Print Average finally
                print "Average  = ", sum/c
             }
     ' file file

其他方式 - 在END块处理,如果输出顺序无关紧要

 awk '{
            A[$1] = $3 > A[$1] ? $3 : A[$1]
      }
   END{
            for(i in A)
            {
               print "selector = " i " max = "A[i]
               sum+=A[i]  
            }
               print "Average  = ", sum/length(A)
      }
     ' file

答案 2 :(得分:0)

这真的不是很难。由于示例数据中只有三个有用的列,因此我在下面的代码中将8更改为3:

awk '$1 != col1 { if (col1 != "") max[col1] = max3; max3 = $3; col1 = $1 }
                { if ($3 > max3) max3 = $3 }
     END        { if (col1 != "") max[col1] = max3;
                  for (i in max) { sum += max[i]; num++ }
                  if (num > 0) print sum / num
                }'

第一行处理第1列中的更改。如果第1列之前有值(col1),则将最大值(max3)保存在索引的数组max中按col1。同时重置col1的当前值,并将最大值设置为$3中的当前值。

下一行是'每一行'处理;如果第3列中的值大于先前的最大值,则记录新的最大值。

END块处理'第1列中的更改',与第一个块一样。它不需要重置值,因为没有更多的输入行。下一行计算值的总和。如果要处理的值至少为1,则最后一行打印平均值。

根据样本数据,它会产生答案:

0.174621

显然,对于包含8列的数据,您需要将所有三个星形映射到八个。

此代码假定数据在第1列中分组,因此相关条目在一起。有可能避免这种假设,如:

awk '{ if (!$1 in max) max[$1] = $3;
       if ($3 > max[$1]) max[$1] = $3 }
 END { 
       for (i in max) { sum += max[i]; num++ }
       if (num > 0) print sum / num
     }'

这实际上比以前的版本更简单;它只是查看$3(或您的版本中的$8)中的值是否大于与$1关联的最大值,如果是,则存储它。如果之前没有看到$1,则将最大值设置为当前值;这避免了“最大值的安全价值是什么 - 值是否为负值”的问题。

在这两种解决方案中,如果您希望打印最大值,可以在END块中轻松执行此操作,例如:

for (i in max) print i, max[i]

或者您可以使用更适合您的华丽打印格式。请注意,键(i值)的显示顺序是不确定的。如果订单很重要,您必须在awk或单独的sort流程中对值进行排序。