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”来解决这个用例出色地。
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
:按数组索引排序,按数字降序。由于我们强制执行该顺序,因此我们可以简单地统计有多少记录大于我们当前正在查看的记录。