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

13
12
11
8
4
3
2
1
1
1

对于这个列的每个值,我需要计算它的PERCENTRANK(数据集中某个值作为数据集的百分比的排名)。
计算数据集中任何值X的百分比秩的公式是
= number of values less than X / (Number of values less than X + Number of Values greater than X)

因此,对于数据集中的每个值X,程序必须遍历所有记录,以查找同一数据集中小于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

2的PERCENTRANK为0.333,因为集合中的三个值小于2,而六个值大于2。百分比秩2=3/(3+6)=3/9=0.3333。
类似地,4的PERCENTRANK为0.5555,因为5个值小于4,4个值大于4。百分位4=5/(5+4)=5/9=0.5555。
我正在避免嵌套的“while..do”循环,因为在包含数百万条记录的文件中循环时速度非常慢。
我对awk在许多其他迭代计算场景中惊人的速度感到满意,例如:计算平均值、标准差、按和分组等等,因此,理想情况下,我也希望使用awk来解决这个用例。

最佳答案

遵循更简单的sortawk方法可能也有同样的帮助(尽管我没有在数百万行上进行测试,因为我没有它)。
解决方案1:在您的示例中,这不会在输出eg-->数字1中显示重复项的秩。

sort -nr Input_file | awk '
function sum(array){
  tot="";
  for(i in array){
    tot+=array[i]};
  return tot}
{
  a[FNR]=$0;
  b[$0]++
}
END{
  for(j=1;j<=FNR;j++){
    if(b[a[j]]){
      val=b[a[j]];
      delete b[a[j]];
      printf("%d %0.4f\n",a[j],sum(b)/(sum(d)+sum(b)));
      d[a[j]]=val;}
}}
'

输出如下。
13 1.0000
12 0.8889
11 0.7778
8 0.6667
4 0.5556
3 0.4444
2 0.3333
1 0.0000

解决方案2:添加解决方案(与第一个不同的小方案),它将在输出中提供甚至重复项的排名,如下所示。
sort -nr Input_file | awk '
function sum(array){
  tot="";
  for(i in array){
    tot+=array[i]};
  return tot}
{
  a[FNR]=$0;
  b[$0]++
}
END{
  for(j=1;j<=FNR;j++){
    if(b[a[j]]){
      val=val1=b[a[j]];
      delete b[a[j]];
      while(val1>0){
      printf("%d %0.4f\n",a[j],sum(b)/(sum(d)+sum(b)));
      val1--}
      d[a[j]]=val;}
}}
'
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

08-05 10:30