【问题标题】:Calculate sequential average and median from file using awk使用 awk 从文件中计算顺序平均值和中位数
【发布时间】:2023-03-28 19:45:01
【问题描述】:

这是我的输入文件(有数千行):

$ cat file.txt
1 495.03
2 503.76
3 512.28
4 520.75
5 529.17

我想使用awk 来计算第一列在 X(比如说 1-100)行数上的中位数以及第二列相应值的平均值。 awk 然后将移动下一组行 (101-201) 并执行相同的操作,即第一列的中位数和第二列的平均值,依此类推。不用说,我正在努力学习 awk 并尝试了几个以前的解决方案,但无法完全发挥作用。

从之前的post,我发现我可以这样计算平均值:

awk '{sum+=$1} NR%3==0 {print sum/3; sum=0}'

这究竟是如何工作的(即 {sum+=$1} 表达式是什么意思?),我如何将其调整为中位数?顺便说一句,第一列将始终被排序。

提前致谢, TP

【问题讨论】:

    标签: awk


    【解决方案1】:

    如果对记录进行排序,则中位数将只是 50th 和 51st 值的平均值。

    $ awk      '{r=NR%100; sum+=$2} 
          r==50 {m=$1} 
          r==51 {m=(m+$1)/2} 
          r==0  {print m, sum/100; sum=0}' file
    

    如果记录数是 100 的倍数,这将起作用,否则您需要处理最后一个具有不同大小的组。

    对于偶数记录,“中位数”还有其他定义,但您应该指定。

    解释定义r为模100的余数,本质上是100条记录的每个块中的相对位置。对于中位数,我们取第 50 条和第 51 条记录的平均值,sum 聚合每 100 个块的第二个字段值。当余数为 0 时,我们完成每个块,打印中位数和平均值(sum/100)值;为下一个块重置sum

    【讨论】:

    • 谢谢,介意评论您的代码吗?谢谢,它有效。介意评论您的代码,以便我可以学习一些东西吗? '{r=NR%100; sum+=$2} # define variable r as the sum of 100 lines?r==50 {m=$1} # take the 50th value from the first column and store it as m variable? 还有其他的吗?
    【解决方案2】:

    注意:这包含更多关于未排序数据的运行均值和中位数的信息。这应该被视为对原始问题的补充。

    如果您想计算最后 n 项的运行平均值(假设 n = 100),那么您必须注意如何处理第一个 m 个带有 m 的记录。处理这个问题的一种方法是将值放在一个数组中,其中索引是 n 的模数。这样,您的数组中总是有最后的 n 个术语:

    $i 的运行平均值:

    awk '{a[NR%100] = $i; s=0; for(j in a) { s+=a[j] }; print "avg:" s/length(a) }'
    

    但是,您可以通过跟踪 s 来删除 for-loop:

    awk '{s+=$i; if (NR%100 in a) s-=a[NR%100]; a[NR%100]=$i; print "avg:" s/length(a) }'
    

    $i 的运行中位数:

    一种计算中位数的方法可以使用gawk 完成,其中我们假设数组按值排序以进行数组遍历

    awk 'BEGIN{ PROCINFO["sorted_in"]="@val_num_asc" }
         { a[NR%100] = $i }
         { k=0; m=0;
           for(j in a) { k++
               if (k >= length(a)/2  ) m+=a[j]
               if (k <= length(a)/2+1) {m+=a[j]; break }
           }
           print "med:", m/2
         }'
    

    或者如果你想让 if 条件更轻松

    awk 'BEGIN{ PROCINFO["sorted_in"]="@val_num_asc" }
         { a[NR%100] = $i }
         { k=0; m=0;
           for(j in a) { k++
               if (k < length(a)/2  ) continue
               if (k > length(a)/2+1) break
               m+=a[j]
           }
           print "med:", (length(a)%2==0 ? m/2 : m)
         }'
    

    如果您不想使用预先排序的概念,那么中位数的计算就会变得更加困难。一种可能的方法是使用selection algorithm,如here 所述。

    【讨论】:

    • 谢谢,但我还是很困惑;当我在包含顺序值(1,2,3,4,5,6...100)的文件上运行您的平均代码时。我得到 avg:1, avg:1.5, avg:2... 不太清楚它是如何工作的,我们在这里实际计算的是什么。
    • @ThePresident 计算的是运行平均值。 IE。第一行是{1}的平均值,第二行是{1,2}的平均值,第三行是{1,2,3} ... {1,...,100} 的第 100 个,然后是 {2,...,101} 的第 101 个,等等只有最后 100 个条款
    • 哦,我明白了,现在说得通了。
    • @ThePresident 我给出这个答案的原因是为了更深入地了解运行平均值和中位数,还因为您的标题可以被解释为适合这个答案。
    猜你喜欢
    • 1970-01-01
    • 2022-01-24
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    相关资源
    最近更新 更多