使用 AWK 进行块求和(当模式改变时重新开始求和)

使用 AWK 进行块求和(当模式改变时重新开始求和)

我有一个这样的文件:

A 100
A 200
A 300 #sum=600
B 400
B 500 #sum=900
A 600
A 700
A 800 #sum=2100

我希望输出是:

A 600
B 900
A 2100
C sum_of_C
D sum_of_D

我可以用forsedgrep和 来做到这一点awk

不过因为我正在学习awk,所以我想写一个awk脚本。到目前为止我有:

if (${NR {print $1}} == ${NR-1 {print $1}}) 
  sum+=$2
  print $0"\t"sum
else
  sum=$2
  print $0"\t"sum

awk -f awkscript file没有成功。解决办法是什么?

答案1

我不完全确定你if想在那里做什么。NR是记录数;用于NF字段数量(如果这是您的目标)。你不能把{}方块放在这样的东西中间。

我认为您的目标是将这一行中的字段值与前一行中的字段值进行比较,当我们到达新的数据“组”时打印出总和。如果是这样的话,这个脚本将执行您想要的操作,并且我认为与您的目标几乎相同:

{
    if (last && $1 != last) {
        print last, sum
        sum = 0
    }
    sum = sum + $2
    last = $1
}
END {
    print last, sum
}

我们创建一个新变量来保存上一行last第一个字段 ( ) 的值。$1我们将用它来跟踪我们正在查看的组。

  • 对于每一行(因为我们{ ... }在顶层),我们首先测试 a) 是否last设置(因为我们不想在第一行打印任何内容),并且 b) 第一个字段的值不同于last。如果是,我们打印出 的值last、一个空格(因为,)以及sum我们计算出的 。 (如果您想要一个制表符,请"\t"像您一样在引号中使用)
  • 打印后,我们重置sum为零。
  • $2无论哪种方式,我们都将第二个字段 ( )的值添加到sum
  • 对于每一行,我们将第一个字段(我们的组)保存到 中last,以便我们可以使用它在下一行进行比较。
  • 最后,我们还想打印最后一组。为此,我们使用一个END { ... }块。当我们用完数据时,它会在程序结束时运行。我们像以前一样打印出总和以及我们正在合作的组。

如果我运行:

awk -f sum.awk < data

通过您的数据文件,我得到以下输出:

A 600
B 900
A 2100

如预期的。


有更简单的方法可以做到这一点,无论是在 awk 中还是其他方式。特别是,我们可以将上面的主体替换为:

last && $1 != last {
    print last, sum
    sum = 0
}
{
    sum = sum + $2
    last = $1
}

这里我们使用 awk 的条件块语法而不是显式if测试:该程序的行为与上面的相同,但更惯用。在这个例子中并没有太大的不同,但是了解您是否正在学习 awk 是很有用的。


#sum=如果您给出的文件示例确实是带有行(或类似内容)的文件示例,那么您可以使用以下脚本:

{
    sum = sum + $2
    if (NF == 3) {
        print $1, sum
        sum = 0
    }
}

对于每一行,这会将第二个字段的值添加到sum变量中。在恰好包含三个字段 ( NF == 3) 的行上,我们打印出总计,并重置sum为零。

答案2

如果您的文件足够小,可以将所有总和放入内存中,您可以执行以下简单操作:

$ awk '{sum[$1]+=$2}END{for(pat in sum){print pat,sum[pat]}}' file 
A 2700
B 900

这与注释脚本相同awk

#!/usr/bin/awk -f

{
    ## Here, we use $1 as the key of an associative array
    ## and increment its current value by $2. The result of 
    ## this will be an array element for each different $1 in 
    ## the file whose value will be the sum of all associated $2s.
    sum[$1]+=$2
}

## The END{} block is exacuted after the entire file
## has been processed.
END{
    ## Iterate through the keys of the array (the $1s),
    ## saving each as 'pat'. Then, print the current value of
    ## 'pat' as well as the associated value (the sum) from
    ## the array.
    for(pat in sum){
        print pat,sum[pat]
    }
}

这种方法唯一可能出现的问题是,如果您有太多行,则保留 s 数组$1将导致内存不足。这在现代系统上不太可能发生。另一方面,当文件的行不按顺序排列时,此方法也适用,因为它可以处理未排序的文件。

相关内容