awk计算列的中位数

20

如何使用AWK计算数字数据列的中位数?

我可以想到一个简单的算法,但似乎无法编写它:

目前我所拥有的是:

sort | awk 'END{print NR}' 

使用这个命令可以得到列中元素的数量。我想用它来打印某一行(NR / 2)。如果NR/2不是整数,则将其四舍五入到最近的整数,并作为中位数,否则取(NR/2)+1(NR/2)-1的平均值。

5个回答

27

使用 awk,您需要将值存储在数组中,在最后计算中位数,假设我们查看第一列:

sort -n file | awk ' { a[i++]=$1; } END { print a[int(i/2)]; }'

确实,对于真实中位数的计算,请按照问题描述进行四舍五入:

sort -n file | awk ' { a[i++]=$1; }
    END { x=int((i+1)/2); if (x < (i+1)/2) print (a[x-1]+a[x])/2; else print a[x-1]; }'

21

这个 awk 程序假设有一个数字排序后的数据列:

#/usr/bin/env awk
{
    count[NR] = $1;
}
END {
    if (NR % 2) {
        print count[(NR + 1) / 2];
    } else {
        print (count[(NR / 2)] + count[(NR / 2) + 1]) / 2.0;
    }
}

使用示例:

sort -n data_file | awk -f median.awk

3
你也可以在awk中使用asort来对数组进行排序。 - Vatine
2
@Vatine,asort() 是 GNU-awk 特有的函数,会让代码变得更加复杂。 - Ed Morton
1
@RuudvA:如果数组是从零开始的,那么这就是正确的。但是第一次调用count[NR] = $1;时,NR == 1。我相信这段代码是正确的(但是,五年后,我不喜欢count作为变量名)。 - johnsyweb
@Kevin:提醒我六年前写的答案真是太好了!你的输入结果是正确的,因为有11个值,4是第6个位置上的值。如果你的文件按照这样的顺序排列:[4, 4, 4, 4, 1, 12, 2, 3, 5, 6, 4],你会得到一个非常不同的结果。请参见https://en.wikipedia.org/wiki/Median#Medians_for_samples获取更多信息。 - johnsyweb
1
我已经比较了性能和排序,-kn3 比 awk '{print $0|"sort -nk3 "}' 快得多(对于一个有 1 百万行和 3 列的文件,14 秒与 66 秒相比)。在计算中位数之前进行排序是计算结果的更快方法。如此处所讨论的。 - Tom Kelly
显示剩余5条评论

5

好的,我看到这个主题,想分享一下我的意见,因为我以前也在寻找类似的东西。尽管标题中包含awk,但所有答案都使用了sort。使用datamash可以轻松计算数据列的中位数:

> seq 10 | datamash median 1
5.5

请注意,即使您有一个未排序的列,也不需要使用sort函数:
> seq 10 | gshuf | datamash median 1
5.5

这份文档列出了 datamash 能执行的所有功能,还为包含多列的文件提供了好的示例。无论如何,它与 awk 没有任何关系,但我认为在这种情况下,datamash 会非常有帮助,并且也可以与 awk 结合使用。希望对某些人有所帮助!


2

这个基于AWK的答案与Excel计算中位数时得到相同的结果,它是回答unix.stackexchange.com上类似问题的。


1
如果您有一个用于计算中位数的数组(包含Johnsyweb解决方案的一行代码):
array=(5 6 4 2 7 9 3 1 8) # numbers 1-9
IFS=$'\n'
median=$(awk '{arr[NR]=$1} END {if (NR%2==1) print arr[(NR+1)/2]; else print (arr[NR/2]+arr[NR/2+1])/2}' <<< sort <<< "${array[*]}")
unset IFS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接