使用 AWK 计算并除以总计

使用 AWK 计算并除以总计

鉴于以下data文件...

foo     10
bar     20
oof     50
rab     20

...我如何将第二列打印为第二列总数的百分比?换句话说,我想要...

foo     10    10%
bar     20    20%
oof     50    50%
rab     20    20%

...当然还有不太明显的数字。我可以很容易地创建一个运行总计,但我不知道如何才能在打印行之前计算总数。我正在 awk 文件中执行此操作totals.awk...

#!/usr/bin/awk -f
BEGIN{
        runningtotal=0
}
{
        runningtotal=runningtotal+$2
        print $1 "\t" $2 "\t" runningtotal "\t" $2/runningtotal
}

所以,运行./totals.awk data收益...

foo     10      10      1
bar     20      30      0.666667
oof     50      80      0.625
rab     20      100     0.2

有没有办法循环两次,一次计算总数,一次打印行?这在 AWK 中可行吗?或者我必须使用其他实用程序?

答案1

要通过一次调用来创建表awk

$ awk 'FNR==NR{s+=$2;next;} {printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s}' data data
foo     10      10%
bar     20      20%
oof     50      50%
rab     20      20%

怎么运行的

该文件data作为参数提供给awk两次。因此,它将被读取两次,第一次获取总计(存储在变量 中)s,第二次打印输出。更详细地查看命令:

  • FNR==NR{s+=$2;next;}

    NR 是已读取的记录(行)总数awk,FNR 是迄今为止从当前文件读取的记录数。因此,当 时FNR==NR,我们正在读取第一个文件。发生这种情况时,变量s会增加第二列中的值。然后,next告诉awk跳过其余命令并从下一条记录开始。

    请注意,不必初始化s为零。在 中awk,所有数值变量默认都初始化为零。

  • printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s

    如果我们到达此命令,那么我们正在处理第二个文件。这意味着s现在保存了第 2 列的总计。因此,我们打印第 1 列、第 2 列和百分比100*$2/s

输出格式选项

使用printf,可以对输出格式进行详细控制。上面的命令使用%s适用于字符串、整数和浮点数的格式说明符。这里可能有用的其他三个选项是:

  • %d将数字格式化为整数。如果数字实际上是浮点数,它将被截断为整数

  • %f将数字格式化为浮点数。还可以指定宽度和小数位,例如%5.2f

  • %e提供指数表示法。如果某些数字特别大或特别小,这将很有用。

制作一个shell函数

如果您要多次使用此命令,那么输入长命令会很不方便。相反,创建一个函数或脚本来执行该命令。

要创建名为 的函数totals,请运行以下命令:

$ totals() { awk 'FNR==NR{s+=$2;next;} {printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s}' "$1" "$1"; }

data定义此函数后,可以通过运行以下命令找到调用的数据文件的百分比:

$ totals data

要使定义totals永久,请将其放入您的~/.bashrc文件中。

制作一个 shell 脚本

如果您更喜欢脚本,请创建一个名为的文件,totals.sh其内容为:

#!/bin/sh
awk 'FNR==NR{s+=$2;next;} {printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s}' "$1" "$1"

要获取名为 的数据文件的百分比data,请运行:

sh totals.sh data

答案2

执行此操作的“简单”方法是调用awk两次:一次获取总数,另一次计算比率。

$ total=$(awk 'BEGIN{ total=0 } { total=total+$2 } END{ printf total }' data)
$ awk -v total=$total '{ print $1 "\t" $2 "\t" 100*$2/total "%" }' data

现在我确信有人会以某种方式想出一句俏皮话......

答案3

awk 方式打开一个文件(为了完整性)

awk '{a[NR]=$0;x+=(b[NR]=$2)}END{while(++i<=NR)print a[i]"\t"100*b[i]/x"%"}' file

foo     10      10%
bar     20      20%
oof     50      50%
rab     20      20%

这将比其他方法使用更多内存,但速度应该更快

这会将行读入 arraya并将字段二读入 array b
然后x按字段 2 中的值递增。

最后,它从 1 迭代到记录数并输出正确的行并计算百分比。

相关内容