寻找一种让 awk 迭代循环文件的方法(在 bash 中创建 PERCENTRANK 函数)

CF-*_*-RV 1 unix bash awk gawk

我的文件data.txt包含以下包含数百万行的记录结构:

13
12
11
8
4
3
2
1
1
1
Run Code Online (Sandbox Code Playgroud)

对于该列的每个值,我需要计算其 PERCENTRANK(这是数据集中某个值占数据集百分比的排名)。

计算数据集中任意值 X 的 PERCENTRANK 的公式为

= number of values less than X / (Number of values less than X + Number of Values greater than X)
Run Code Online (Sandbox Code Playgroud)

因此,对于数据集中的每个值 X,程序必须循环遍历所有记录,以查找同一数据集中小于和大于 X 的值的数量。

如何使用“awk”重复循环文件来计算所有 X 值的 PERCENTRANK 来实现这一点?

预期输出:

X    PERCENTRANK
13   1.0000
12   0.8888
11   0.7777
8    0.6666
4    0.5555
3    0.4444
2    0.3333
1    0.0000
1    0.0000
1    0.0000
Run Code Online (Sandbox Code Playgroud)

2 的 PERCENTRANK 为 0.333,因为集合中的三个值小于 2,六个值大于 2。 PERCENTRANK OF 2 = 3 / (3 + 6) = 3/9 = 0.3333。

同样,4 的 PERCENTRANK 为 0.5555,因为五个值小于 4,四个值大于 4。4 的百分比 = 5 / (5 + 4) = 5 / 9 = 0.5555。

我避免使用嵌套的“while..do”循环,因为在循环包含数百万条记录的文件时它非常慢。

我对 awk 在许多其他迭代计算场景中的惊人速度感到满意,例如:计算平均值、标准差、按总和分组等,因此,我理想地希望使用“awk”来解决这个用例出色地。

gle*_*man 5

GNU awk

gawk '
    {count[$1]++}
    END {
        print "X\tPERCENTRANK"
        PROCINFO["sorted_in"] = "@ind_num_desc"
        gt = 0
        total = NR
        for (x in count) {
            lt = total - count[x] - gt
            pr = lt/(gt+lt)
            for (i=1; i<=count[x]; i++)
                printf "%d\t%.4f\n", x, pr
            gt += count[x]
        }
    }
' data.txt
Run Code Online (Sandbox Code Playgroud)
X       PERCENTRANK
13      1.0000
12      0.8889
11      0.7778
8       0.6667
4       0.5556
3       0.4444
2       0.3333
1       0.0000
1       0.0000
1       0.0000
Run Code Online (Sandbox Code Playgroud)

即使对于大型数据集,这也应该非常有效:没有嵌套循环。

这依赖于 GNU awk 来设置遍历数组的顺序count:按数组索引排序,按数字降序。由于我们强制执行该顺序,因此我们可以简单地统计有多少记录大于我们当前正在查看的记录。