Чтение полей в предыдущих строках для скользящей средней - PullRequest
0 голосов
/ 09 декабря 2018

Основной вопрос

Каков правильный синтаксис для рекурсивного вызова AWK внутри другой программы AWK, а затем сохранения вывода в (числовую) переменную?

Я хочу вызвать AWK, используя2/3 переменных:

  • 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 </em>.

  • Для строк 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 , я хочу добавить его кнакопительная сумма, но также начните вычитать первое значение, которое мне не нужно (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 голосов
/ 09 декабря 2018

Если вы хотите получить скользящее среднее для одного столбца, вы можете сделать это следующим образом:

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
}

Однако вы упомянули, что вам нужно добавить миллионы записей.Вот где это становится немного сложнее.Суммирование большого количества значений приведет к появлению числовых ошибок, так как вы теряете точность по крупицам (когда добавляете числа с плавающей запятой).Поэтому в этом случае я бы предложил реализовать суммирование Кахана .

. Для одного столбца вы получите:

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) }

Для задачи с несколькими столбцами теперь легко настроить вышеуказанный скрипт.Все, что вам нужно знать, это то, что y и t являются временными значениями, а k - это срок компенсации, который необходимо сохранить в памяти.

...