【问题标题】:How to Add Column with Percentage如何添加百分比列
【发布时间】:2018-05-10 12:45:54
【问题描述】:

我想计算所有行中每行的值百分比并将其添加为另一列。 输入(分隔符为 \t):

1   10      
2   10
3   20
4   40

添加了第三列的所需输出显示基于第二列中的值计算的百分比:

1   10   12.50   
2   10   12.50
3   20   25.00
4   40   50.00

我自己尝试过,但是当我计算所有行的总数时,我不知道如何保持其余行不变。非常感谢您的帮助!

【问题讨论】:

    标签: awk


    【解决方案1】:

    给你,一个 pass 步 awk 解决方案 -

    awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file

    [jaypal:~/Temp] cat file
    1   10      
    2   10
    3   20
    4   40
    [jaypal:~/Temp] awk 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
    1 10 12.5
    2 10 12.5
    3 20 25
    4 40 50
    

    更新:如果输出中需要选项卡,则只需将 OFS 变量设置为“\t”。

    [jaypal:~/Temp] awk -v OFS="\t" 'NR==FNR{a = a + $2;next} {c = ($2/a)*100;print $1,$2,c }' file file
    1   10  12.5
    2   10  12.5
    3   20  25
    4   40  50
    

    模式{action}语句的突破:

    • 第一个模式是NR==FNR。 FNR 是 awk 的内置变量,用于跟踪给定文件中的记录数(默认情况下由新行分隔)。所以在我们的例子中,FNR 为 4。NR 类似于 FNR,但它不会重置为 0。它会继续增长。所以在我们的例子中,NR 是 8。

    • 这种模式只适用于前 4 条记录,这正是我们想要的。在仔细阅读这 4 条记录后,我们将总数分配给变量a。请注意,我们没有初始化它。在awk 我们不必这样做。但是,如果整个第 2 列为 0,这将中断。因此,您可以通过在第二个操作语句中放置一个 if 语句来处理它,即仅当 a > 0 else 表示除以 0 或其他内容时才进行除法。

    • next 是必需的,因为我们真的不希望执行第二个模式 {action} 语句。 next 告诉 awk 停止进一步的操作并移动到下一条记录。

    • 一旦四个记录被解析,下一个模式{action}就开始了,这非常简单。执行百分比并打印第 1 列和第 2 列以及它们旁边的百分比。

    注意: 正如@lhf 在评论中提到的那样,只要您将数据集保存在文件中,这种单行就可以工作。如果您通过管道传递数据,它将无法工作。

    在 cmets 中,正在讨论如何让 awk one-linerpipe 而非 file 获取输入。那么我能想到的唯一方法是将列值存储在array 中,然后使用for loop 将每个值连同它们的百分比一起吐出。

    现在awk 中的arraysassociative 并且永远不会按顺序排列,即从数组中取出值的顺序与它们进入时的顺序不同。所以如果没问题,那么下面的一个-班轮应该工作。

    [jaypal:~/Temp] cat file
    1   10      
    2   10
    3   20
    4   40
    
    [jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}'
    2 10 12.5
    3 20 25
    4 40 50
    1 10 12.5
    

    为了让它们按顺序排列,您可以将结果通过管道传送到sort

    [jaypal:~/Temp] cat file | awk '{b[$1]=$2;sum=sum+$2} END{for (i in b) print i,b[i],(b[i]/sum)*100}' | sort -n
    1 10 12.5
    2 10 12.5
    3 20 25
    4 40 50
    

    【讨论】:

    • 没问题。 :) 我会做一些解释以供参考。
    • 不错,但不是真正的一次性。特别是不能用作过滤器,即从标准输入读取。
    • +1,但是当您读取文件两次时,这不是一次通过?
    • Lhf,你知道如何通过只从标准输入读取输入并将输出管道输出到标准输出,而不需要任何临时文件来完成这项工作吗?这对我的用例来说会好得多。
    • @Martin,我能想到的唯一方法是存储所有输入行,计算第2列的总和,最后输出结果
    【解决方案2】:

    你可以在几遍中完成

    #!/bin/bash
    
    total=$(awk '{total=total+$2}END{print total}' file)
    awk -v total=$total '{ printf ("%s\t%s\t%.2f\n", $1, $2, ($2/total)*100)}' file
    

    【讨论】:

    • 谢谢。这也有效,但是我发现 Jaypal 建议的解决方案更易于使用,因此我选择了他的解决方案作为答案。
    【解决方案3】:

    您需要将其转义为%%。例如:

    printf("%s\t%s\t%s%%\n", $1, $2, $3)
    

    【讨论】:

    • 谢谢,抱歉,如果没有在问题中正确解释 - 我没有 % 符号的问题(我不需要它),我的问题是如何计算值本身.
    【解决方案4】:

    也许有更好的方法,但我会传递文件两次。

    'infile' 的内容:

    1       10 
    2       10
    3       20
    4       40
    

    “script.awk”的内容:

    BEGIN {
            ## Tab as field separator.
            FS = "\t";
    }
    
    ## First pass of input file. Get total from second field.
    ARGIND == 1 {
            total += $2;
            next;
    }
    
    ## Second pass of input file. Print each original line and percentage as third field.
    {
            printf( "%s\t%2.2f\n", $0, $2 * 100 / total );
    }
    

    在我的 linux 机器中运行脚本:

    gawk -f script.awk infile infile
    

    结果:

    1       10      12.50
    2       10      12.50
    3       20      25.00
    4       40      50.00
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-26
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多