首页 文章

读取前一行中的字段以获得移动平均值

提问于
浏览
2

主要问题

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

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

  • N - >可以从Bash或容器AWK脚本中读取 .

  • Linenum - >从容器AWK程序中读取

  • J - >我想阅读的字段

这是我的尝试 .

Container 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中,我想将它添加到累积和,但也开始减去我不需要的第一个值(k-n) .

我不想为最后的步骤创建一个累积总和数组,因为我觉得它可能会影响性能 . 我更喜欢直接选择我想要减去的值 .

为此,我需要再次呼叫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 回答

  • 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 是需要存储在内存中的补偿项 .

相关问题