读取前几行中的移动平均值

时间:2018-12-09 12:22:29

标签: awk moving-average

主要问题

在另一个AWK程序中递归调用AWK,然后将输出保存到(数字)变量中的正确语法是什么?

我想使用2/3变量调用AWK:

  • N->可以从Bash或容器AWK脚本中读取。
  • Linenum->从容器AWK程序读取
  • J->我想阅读的字段

这是我的尝试。

容器AWk程序:

BEGIN {}
{
...
# Loop in j 
...
k=NR

# Call to other instance of AWK 
var=(awk -f -v n="$n_steps" linenum=k input-file 'linenum-n {printf "%5.4E", $j}'
...
}
END{}

更多常规问题的背景:

我有一个文件,要为其计算 n (例如2280)步的移动平均值。

  • 理想情况下,对于前n行,平均值为1到 k 的值, 其中 k <= n

  • 对于 k> n 行,平均值将是最后 n 个值。

我最终将在许多大型文件中执行代码,这些文件具有几列以及数千到几百万行,因此我对尽可能简化代码很感兴趣。

代码摘录和说明

我要开发的代码看起来像这样:

    NR>1
{
    # Loop over fields 
    for (j in columns)
    {
        # Rows before full moving average is done
        if ( $1 <= n )
        {
            cumsum[j]=cumsum[j]+$j #Cumulative sum 
            $j=cumsum[j]/$1        # Average
        }
        #moving average
        if ( $1 > n )
        {
            k=NR
            last[j]=(awk -f -v n="$n_steps" ln=k input-file 'ln-n {printf "%5.4E", $j}') # Obtain value that will get ubstracted from moving average
            cumsum[j]=cumsum[j]+$j-last[j] # Cumulative sum adds last step and deleted unwanted value
            $j=cumsum[j]/n  # Moving average
        }
    }
}

我的输入文件包含几列。第一列包含行号,其他列包含值。

对于移动平均线的累计和:如果我在行 k 中,我想将其添加到累计和中,还要开始减去我不需要的第一个值< em>(kn)。

我不想在最后一步中创建一个累加和的数组,因为我认为这可能会影响性能。我更喜欢直接选择要减去的值。

为此,我需要再次致电AWK(但在另一行)。我尝试在这一行中这样做:

k=NR
last[j]=(awk -f -v n="$n_steps" ln=k input-file 'ln-n {printf "%5.4E", $j}'

我确定此代码不正确。

讨论问题

获取有关AWK正在处理的字段的上一行中的字段的信息的最佳方法是什么?然后可以将其保存到变量中吗?

是否允许甚至建议这种AWK递归使用?

如果没有,那么更新累积总和值以使我获得足够有效的代码的最有效方法是什么?

样本输入和输出

这里是输入(第二列)和所需输出(第三列)的样本。我使用3作为平均步数( n

N   VAL AVG_VAL
1   1   1
2   2   1.5
3   3   2
4   4   3
5   5   4
6   6   5
7   7   6
8   8   7
9   9   8
10  10  9
11  11  10
12  12  11
13  13  12
14  14  13
14  15  14 

1 个答案:

答案 0 :(得分:2)

如果要对单个列进行移动平均,可以按以下方式进行操作:

BEGIN{n=2280; c=7}
{ s += $c - a[NR%n]; a[NR%n] = $c }
{ print $0, s /(NR < n : NR ? n) }

在这里,我们将最后n个值存储在数组a中,并跟踪累积总和s。每次我们更新总和时,我们都会先从中删除最后一个值来进行更正。

如果要在几列中执行此操作,则在跟踪数组时必须方便一些

BEGIN{n=2280; c[0]=7; c[1]=8; c[2]=9}
{ for(i in c) { s[i] += $c[i] - a[n*i + NR%n]; a[n*i + NR%n] = $c[i] } }
{ printf $0
  for(i=0;i<length(c);++i) printf OFS (s[i]/(NR < n : NR ? n))
  printf ORS
}

但是,您提到必须添加数百万个条目。那就是它变得更加棘手的地方。随着您一点一点地失去精度(添加浮点数时),将很多值相加会引入数字错误。因此,在这种情况下,我建议实施Kahan summation

对于单个列,您将获得:

BEGIN{n=2280; c=7}
{ y = $c - a[NR%n] - k; t = s + y; k = (t - s) - y; s = t; a[NR%n] = $c }
{ print $0, s /(NR < n : NR ? n) }

或更扩展为:

BEGIN{n=2280; c=7}
{ y = $c       - k; t = s + y; k = (t - s) - y; s = t; }
{ y = -a[NR%n] - k; t = s + y; k = (t - s) - y; s = t; }
{ a[NR%n] = $c }
{ print $0, s /(NR < n : NR ? n) }

对于多列问题,现在可以直接调整上面的脚本。您只需要知道yt是临时值,而k是需要存储在内存中的补偿项。