计算任意列数的百分比

时间:2014-12-23 15:49:02

标签: awk bsd

鉴于此示例输入:

ID     Sample1     Sample2      Sample3
One      10          0            5
Two      3           6            8
Three    3           4            7

我需要使用AWK生成此输出:

ID    Sample1 Sample2 Sample3
One   62.50   0.00    25.00
Two   18.75   60.00   40.00
Three 18.75   40.00   35.00

这就是我解决它的方法:

function percent(value, total) {
    return sprintf("%.2f", 100 * value / total)
}
{
    label[NR] = $1
    for (i = 2; i <= NF; ++i) {
        sum[i] += col[i][NR] = $i
    }
}
END {
    title = label[1]
    for (i = 2; i <= length(col) + 1; ++i) {
        title = title "\t" col[i][1]
    }
    print title
    for (j = 2; j <= NR; ++j) {
        line = label[j]
        for (i = 2; i <= length(col) + 1; ++i) {
            line = line "\t" percent(col[i][j], sum[i])
        }
        print line
    }
}

这在GNU AWK中很好用(Linux中为awk,BSD中为gawk), 但不是在BSD AWK中,我收到此错误:

$ awk -f script.awk sample.txt
awk: syntax error at source line 7 source file script.awk
 context is
          sum[i] += >>>  col[i][ <<<
awk: illegal statement at source line 7 source file script.awk
awk: illegal statement at source line 7 source file script.awk

似乎问题在于多维数组。 我想让这个脚本也在BSD AWK中运行, 所以它更便携。

有没有办法更改它以使其在BSD AWK中工作?

2 个答案:

答案 0 :(得分:4)

尝试使用伪二维形式。而不是

col[i][NR]

使用

col[i,NR]

这是一维数组,关键是连接字符串:i SUBSEP NR

答案 1 :(得分:3)

@glenn的回答让我走上了正确的道路。虽然花了更多的工作:

  • 使用col[i, NR]处理列标题很麻烦。它有助于删除列标题的缓冲并在阅读后立即打印它们
  • length(col) + 1在最终循环条件中不再可用,因为使用col[i, j]使循环无限。作为解决方法,我可以使用length(col) + 1
  • 替换NF

这是最终的实现,现在可以在AWK的GNU和BSD版本中使用:

function percent(value, total) {
    return sprintf("%.2f", 100 * value / total)
}
BEGIN { OFS = "\t" }
NR == 1 { gsub(/ +/, OFS); print }
NR != 1 {
    label[NR] = $1
    for (i = 2; i <= NF; ++i) {
        sum[i] += col[i, NR] = $i
    }
}
END {
    for (j = 2; j <= NR; ++j) {
        line = label[j]
        for (i = 2; i <= NF; ++i) {
            line = line OFS percent(col[i, j], sum[i])
        }
        print line
    }
}
相关问题