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

时间:2018-01-21 13:06:52

标签: bash unix awk gawk

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

13
12
11
8
4
3
2
1
1
1

对于此列的每个值,我需要计算其PERCENTRANK(数据集中值的等级作为数据集的百分比)。

计算数据集中任何值X的PERCENTRANK的公式是

= number of values less than X / (Number of values less than X + Number of Values greater than 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,6个大于2. PERCENTRANK OF 2 = 3 /(3 + 6)= 3/9 = 0.3333。

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

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

我很高兴awk在许多其他迭代计算场景中的惊人速度,例如:计算平均值,标准差,按总和等等,所以,我最好使用'awk'来解决这个问题用例也是如此。

2 个答案:

答案 0 :(得分:4)

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
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

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

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

答案 1 :(得分:1)

更简单的sortawk方法也可以帮助你(尽管我没有在数百万行上测试它,因为我没有它)。

解决方案1: 这不会在输出中显示重复项目的排名,例如 - &gt;你的例子中的数字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

解决方案第二: 添加解决方案(与第一个不同的一个小问题),它将在输出中提供甚至重复项目的RANK,如下所示。

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