【问题标题】:Maximum of multiple Rows and calculating average between them using Awk多行的最大值并使用 Awk 计算它们之间的平均值
【发布时间】:2015-06-03 22:34:43
【问题描述】:

我想分别计算从 1.000、1.35、1.70、......(递增 0.35)......、120(每行 14 行)开始的行之间的第 8 列值的最大值,然后计算使用 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

【问题讨论】:

  • 编辑您的问题以显示精确、可测试、输入和输出。
  • 数据中是否有多余的前导列值,比如1.500?还是第 1 列中的值仅是相关值?如果只存在相关的,我们不需要知道增量;我们只需要将第 1 列中的相似值组合在一起。但你应该真正展示你已经尝试过的东西。
  • 第 1 列中的值是唯一相关的值。基本上,我想要具有相似“第 1 列值”的行中的最大“第 9 列值”,然后获得其中的平均值。希望我已经设法澄清
  • 没有。非常简单 - 编辑您的问题以显示精确、可测试的输入和输出。这将涉及从输入中删除所有...s,并在给定该输入的情况下显示相关的所需输出。
  • 在 Stack Exchange 中,由于大小和格式限制,通常无法呈现要处理的原始数据集。因此,有时需要使用省略的数据进行描述。我会简化这个问题,例如,询问第 3 列,并且只显示很多假设我可以推断出我的第 9 列问题的答案。无论如何,提供一个答案就足够了。

标签: linux shell awk


【解决方案1】:

真的不难。由于示例数据中只有三个有用的列,我在下面的代码中将 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) 保存在由 col1 索引的数组 max 中。同时重置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 进程中对值进行排序。

【讨论】:

    【解决方案2】:

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

    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= 测试设置为文件中的预期总列数。 (此模式只是为了排除其他不包含数据的行)如果未设置该权限,将导致不处理任何行并出现除以零错误。此代码假定您的输入具有按行分组的第 1 列的所有相同值。

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

    【讨论】:

      【解决方案3】:

      输入

      $ 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
      

      【讨论】:

      • 在可能的情况下避免两次读取文件总是值得的。
      猜你喜欢
      • 1970-01-01
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-10
      相关资源
      最近更新 更多