Произвольно взвешенное скользящее среднее (фильтры низких и высоких частот)
Заданный входной сигнал x
(например, напряжение, измеренное тысячу раз в секунду в течение пары минут), я хотел бы рассчитать, например,
/ this is not q
y[3] = -3*x[0] - x[1] + x[2] + 3*x[3]
y[4] = -3*x[1] - x[2] + x[3] + 3*x[4]
. . .
Я стремлюсь к переменной длине окна и весовым коэффициентам. Как я могу сделать это в д? Я знаю о mavg и обработке сигналов в q и скользящей сумме qidiom
В мире DSP это называется применением ядра фильтра путем выполнения свертки. Весовые коэффициенты определяют ядро, которое создает фильтр верхних или нижних частот. Приведенный выше пример вычисляет наклон из последних четырех точек, размещая прямую линию методом наименьших квадратов.
2 ответа
Если ваш список ввода невелик, вы можете использовать метод, упомянутый здесь: https://code.kx.com/q/cookbook/programming-idioms/
Это использует "сканирование" наречие. Поскольку этот процесс создает несколько списков, которые могут быть неэффективными для больших списков.
Другое решение с использованием сканирования:
q)f:{sum y*next\[z;x]} / x-input list, y-weights, z-window size-1
q)f[x;-3 -1 1 3;3]
Эта функция также создает несколько списков, поэтому, опять же, может быть не очень эффективным для больших списков.
Другой вариант - использовать индексы для извлечения целевых элементов из списка ввода и выполнения расчета. Это будет работать только в списке ввода.
q) f:{[l;w;i]sum w*l i+til 4} / w- weight, l- input list, i-current index
q) f[x;-3 -1 1 3]@'til count x
Это очень основная функция. Вы можете добавить больше переменных к нему в соответствии с вашими требованиями.
Примерно так будет работать для параметризуемых коэффициентов:
q)x:10+sums -1+1000?2f
q)f:{sum x*til[count x]xprev\:y}
q)f[3 1 -1 -3] x
0n 0n 0n -2.385585 1.423811 2.771659 2.065391 -0.951051 -1.323334 -0.8614857 ..
Конкретные случаи можно сделать немного быстрее (запуск 0 xprev - не самая лучшая вещь)
q)g:{prev[deltas x]+3*x-3 xprev x}
q)g[x]~f[3 1 -1 -3]x
1b
q)\t:100000 f[3 1 1 -3] x
4612
q)\t:100000 g x
1791
Если в этой области вас интересует, есть kx технический документ по обработке сигналов в q: https://code.kx.com/q/wp/signal-processing/
Возможно, это немного устарело, но я подумал, что смогу взвесить. В прошлом году я написал статью об обработке сигналов, которая может иметь определенную ценность. Работая исключительно в KDB, в зависимости от используемых вами размеров сигналов, вы увидите гораздо лучшую производительность благодаря свертке на основе FFT между ядром / окном и сигналом.
Тем не менее, я написал только простое БПФ для radix-2, хотя в моем репозитории github у меня есть непроверенная работа для более гибкого алгоритма Блюштейна, который позволит использовать переменную длину сигнала. https://github.com/callumjbiggs/q-signals/blob/master/signal.q
Если вы хотите пойти по пути выполнения полной ручной свертки с помощью движущейся суммы, то лучшим способом будет разбить ее на блоки, равные размеру ядра / окна (основанный на некоторой работе, которую Артур В. делал много лет тому назад)
q)vec:10000?100.0
q)weights:30?1.0
q)wsize:count weights
q)(weights$(((wsize-1)#0.0),vec)til[wsize]+) each til count v
32.5931 75.54583 100.4159 124.0514 105.3138 117.532 179.2236 200.5387 232.168.