我想使用5的窗口(连续五行)计算数据文件(data.txt)中第2列($ 2)的移动/滚动平均值,然后计算第一个和最后一个值之间的差每个窗口都使用AWK。请在下面查看我的数据:
> cat data.txt
2001 100
2002 110
2003 120
2004 130
2005 140
2006 900
2007 910
2008 920
2009 930
2010 940
> awk 'BEGIN{size=5} {mod=NR%size; if(NR<=size){count++}else{sum-=array[mod]};sum+=$2;array[mod]=$2;print $1"\t", $2"\t", sum/count}' data.txt
2001 100 100
2002 110 105
2003 120 110
2004 130 115
2005 140 120
2006 900 280
2007 910 440
2008 920 600
2009 930 760
2010 940 920
从上面的输出中可以看到,我可以使用上面显示的AWK脚本对$ 2进行移动平均(输出在$ 3中)。但是,如何修改上面的AWK脚本,以便还可以打印出每个窗口的第一个值和最后一个值$ 2之间$ 4的差额?例如,我希望上面的输出看起来像这样:
2001 100 100
2002 110 105
2003 120 110
2004 130 115
2005 140 120 120-100
2006 900 280 280-105
2007 910 440 440-110
2008 920 600 600-115
2009 930 760 760-120
2010 940 920 920-280
答案 0 :(得分:1)
在替换array[mod]
之前,请先将差异另存为变量,以便将其打印在新列中。
awk 'BEGIN{size=5}
{ mod=NR%size;
if(NR<=size){count++;}
else{sum-=array[mod]};
sum+=$2;
if (NR >= size) {diff = $2"-"array[mod]}
array[mod]=$2;
print $1"\t", $2"\t", sum/count"\t", diff
}' data.txt
答案 1 :(得分:1)
您还需要记住第三列的最后一个值:
awk 'BEGIN{size=5}
{
mod=NR%size;
if(NR<=size) {
count++
} else {
sum-=array[mod]
};
sum+=$2;
avg=sum/count;
if (NR>=size) {
diff=avg"-"array2[(mod+1)%size]; # remove quotes to display result as a number
}
array[mod]=$2;
array2[mod]=avg;
print $1"\t", $2"\t", avg, diff
}
' data.txt
编辑:您当前的描述与问题中的示例不匹配($ 4应该与$ 2或$ 3有区别吗?)。我的答案解决了您的示例,Barmars了您的描述:)
答案 2 :(得分:0)
如果您不依赖于awk,那么perl会很整洁。假设您的意思是,对于第4列,窗口的第一个平均值和最后一个平均值之间的差异。
perl -MList::Util=sum0 -slane '
unshift @values, $F[1]; # add current value to window of values
splice @values, $size; # trim window to desired size
$avg = sum0(@values) / scalar(@values);
unshift @averages, $avg;
splice @averages, $size;
push @F, $avg;
# remove quotes below if you want the actual difference
push @F, "$averages[0]-$averages[-1]" if scalar(@values) == $size;
print join "\t", @F
' -- -size=5 data.txt
2001 100 100
2002 110 105
2003 120 110
2004 130 115
2005 140 120 120-100
2006 900 280 280-105
2007 910 440 440-110
2008 920 600 600-115
2009 930 760 760-120
2010 940 920 920-280