Как запустить фильтр верхних или нижних частот для точек данных в R? - PullRequest
37 голосов
/ 18 августа 2011

Я новичок в R , и я попытался найти информацию о следующем, не найдя ничего.

Зеленый график на рисунке состоит из красного и желтого графиков.Но давайте скажем, что у меня есть только точки данных чего-то вроде зеленого графика.Как извлечь низкие / высокие частоты (т.е. приблизительно красные / желтые графики), используя фильтр нижних частот / фильтр верхних частот ?

low frequency sinus curve with high frequency sinus curve modulated onto

Обновление: график был сгенерирован с помощью

number_of_cycles = 2
max_y = 40

x = 1:500
a = number_of_cycles * 2*pi/length(x)

y = max_y * sin(x*a)
noise1 = max_y * 1/10 * sin(x*a*10)

plot(x, y, type="l", col="red", ylim=range(-1.5*max_y,1.5*max_y,5))
points(x, y + noise1, col="green", pch=20)
points(x, noise1, col="yellow", pch=20)

Обновление 2: при использовании фильтра Баттерворта в пакете signal предлагается следующее:

Original picture with filtered graphs added

library(signal)

bf <- butter(2, 1/50, type="low")
b <- filter(bf, y+noise1)
points(x, b, col="black", pch=20)

bf <- butter(2, 1/25, type="high")
b <- filter(bf, y+noise1)
points(x, b, col="black", pch=20)

Расчеты были немного трудоемкими, signal.pdf не дал никаких подсказок о том, какие значения W должны иметь, но оригинальная документация по октаве , по крайней мере, упомянула радианы , который заставил меня двигаться.Значения в моем исходном графике не были выбраны с какой-либо конкретной частотой, поэтому я получил следующие не очень простые частоты: f_low = 1/500 * 2 = 1/250, f_high = 1/500 * 2*10 = 1/25 и частоту дискретизации f_s = 500/500 = 1.Затем я выбрал f_c где-то между низкими и высокими частотами для фильтров нижних / верхних частот (1/100 и 1/50 соответственно).

Ответы [ 8 ]

27 голосов
/ 08 апреля 2014

Недавно я столкнулся с подобной проблемой и не нашел ответы здесь особенно полезными.Вот альтернативный подход.

Давайте начнем с определения примеров данных из вопроса:

number_of_cycles = 2
max_y = 40

x = 1:500
a = number_of_cycles * 2*pi/length(x)

y = max_y * sin(x*a)
noise1 = max_y * 1/10 * sin(x*a*10)
y <- y + noise1

plot(x, y, type="l", ylim=range(-1.5*max_y,1.5*max_y,5), lwd = 5, col = "green")

enter image description here

Таким образом, зеленая линия - это набор данных, который мы хотим передать на низком уровне.и фильтр верхних частот.

Примечание: линию в этом случае можно выразить как функцию, используя кубический сплайн (spline(x,y, n = length(x))), но с данными реального мира это будет редко, поэтому давайте предположим, что это невозможночтобы выразить набор данных как функцию.

Самый простой способ сгладить такие данные, с которыми я столкнулся, - это использовать loess или smooth.spline с соответствующими span / spar.По мнению статистиков loess / smooth.spline, вероятно, не является правильным подходом здесь , так как он не представляет определенную модель данных в этом смысле.Альтернативой является использование обобщенных аддитивных моделей (функция gam() из пакета mgcv ).Мой аргумент в пользу использования лёсса или сглаженного сплайна здесь заключается в том, что это проще и не имеет значения, так как нас интересует видимый результирующий паттерн.Наборы данных реального мира более сложны, чем в этом примере, и поиск определенной функции для фильтрации нескольких похожих наборов данных может быть затруднен.Если видимое совпадение хорошее, зачем усложнять его значениями R2 и p?Для меня приложение визуально, для которого лесс / сглаженные сплайны являются подходящими методами.Оба метода предполагают полиномиальные отношения с той разницей, что лесс более гибок, также используя полиномы более высокой степени, в то время как кубический сплайн всегда кубический (x ^ 2).Какой из них использовать, зависит от тенденций в наборе данных.Тем не менее, следующий шаг - применить фильтр нижних частот к набору данных, используя loess() или smooth.spline():

lowpass.spline <- smooth.spline(x,y, spar = 0.6) ## Control spar for amount of smoothing
lowpass.loess <- loess(y ~ x, data = data.frame(x = x, y = y), span = 0.3) ## control span to define the amount of smoothing

lines(predict(lowpass.spline, x), col = "red", lwd = 2)
lines(predict(lowpass.loess, x), col = "blue", lwd = 2)

enter image description here

Красная линия сглаженасплайн-фильтр и синий лесс-фильтр.Как видите, результаты немного отличаются.Я полагаю, что одним из аргументов в пользу использования GAM было бы найти наилучшее соответствие, если бы тренды действительно были такими четкими и последовательными среди наборов данных, но для этого приложения оба эти соответствия достаточно хороши для меня.

После поискаПри установке фильтра нижних частот, фильтрация верхних частот так же проста, как вычитание значений фильтра нижних частот из y:

highpass <- y - predict(lowpass.loess, x)
lines(x, highpass, lwd =  2)

enter image description here

Этот ответ приходит поздно,но я надеюсь, что это поможет кому-то еще бороться с подобной проблемой.

16 голосов
/ 20 октября 2014

Используйте функцию filterfilt вместо фильтра (пакетный сигнал), чтобы избавиться от сдвига сигнала.

library(signal)
bf <- butter(2, 1/50, type="low")
b1 <- filtfilt(bf, y+noise1)
points(x, b1, col="red", pch=20)

Red line shows result of filtfilt

7 голосов
/ 19 августа 2011

По запросу OP:

Пакет сигналов содержит все виды фильтров для обработки сигналов.Большая часть из них сопоставима с функциями обработки сигналов в Matlab / Octave и совместима с ними.

6 голосов
/ 24 июня 2016

Один метод использует fast fourier transform, реализованный в R как fft. Вот пример фильтра верхних частот. Из приведенных выше графиков идея, реализованная в этом примере, состоит в том, чтобы получить серию желтого цвета, начиная с серии зеленым (ваши реальные данные).

# I've changed the data a bit so it's easier to see in the plots
par(mfrow = c(1, 1))
number_of_cycles = 2
max_y = 40
N <- 256

x = 0:(N-1)
a = number_of_cycles * 2 * pi/length(x)

y = max_y * sin(x*a)
noise1 = max_y * 1/10 * sin(x*a*10)
plot(x, y, type="l", col="red", ylim=range(-1.5*max_y,1.5*max_y,5))
points(x, y + noise1, col="green", pch=20)
points(x, noise1, col="yellow", pch=20)

### Apply the fft to the noisy data
y_noise = y + noise1
fft.y_noise = fft(y_noise)


# Plot the series and spectrum
par(mfrow = c(1, 2))
plot(x, y_noise, type='l', main='original serie', col='green4')
plot(Mod(fft.y_noise), type='l', main='Raw serie - fft spectrum')

y-noise and fft spectrum

### The following code removes the first spike in the spectrum
### This would be the high pass filter
inx_filter = 15
FDfilter = rep(1, N)
FDfilter[1:inx_filter] = 0
FDfilter[(N-inx_filter):N] = 0
fft.y_noise_filtered = FDfilter * fft.y_noise

enter image description here

par(mfrow = c(2, 1))
plot(x, noise1, type='l', main='original noise')
plot(x, y=Re( fft( fft.y_noise_filtered, inverse=TRUE) / N ) , type='l', 
     main = 'filtered noise')

enter image description here

3 голосов
/ 18 августа 2011

Проверьте эту ссылку, где есть R-код для фильтрации (медицинские сигналы).Это Мэтт Шотвелл, и сайт полон интересной информации о R / stats с медицинской склонностью:

biostattmat.com

Пакет fftfilt содержит множество алгоритмов фильтрации, которыетоже должно помочь.

2 голосов
/ 18 апреля 2018

Я также изо всех сил пытался выяснить, как параметр W в функции сливочного масла отображается на обрезание фильтра, отчасти потому, что документация для фильтра и фильтрафильта на момент публикации некорректна (предполагается, что W = .1 приведет к в фильтре с частотой 10 Гц при сочетании с фильтрованием при частоте дискретизации сигнала Fs = 100, но на самом деле это всего лишь фильтр с частотой 5 Гц - отсечка половинной амплитуды составляет 5 Гц при использовании фильтрата, но половинная мощность -off равен 5 Гц, когда вы применяете фильтр только один раз, используя функцию фильтра). Я публикую некоторый демонстрационный код, который я написал ниже, который помог мне подтвердить, как все это работает, и что вы можете использовать, чтобы проверить, что фильтр выполняет то, что вы хотите.

#Example usage of butter, filter, and filtfilt functions
#adapted from https://rdrr.io/cran/signal/man/filtfilt.html

library(signal)

Fs <- 100; #sampling rate

bf <- butter(3, 0.1);       
#when apply twice with filtfilt, 
#results in a 0 phase shift 
#5 Hz half-amplitude cut-off LP filter
#
#W * (Fs/2) == half-amplitude cut-off when combined with filtfilt
#
#when apply only one time, using the filter function (non-zero phase shift),
#W * (Fs/2) == half-power cut-off


t <- seq(0, .99, len = 100)   # 1 second sample

#generate a 5 Hz sine wave
x <- sin(2*pi*t*5)

#filter it with filtfilt
y <- filtfilt(bf, x)

#filter it with filter
z <- filter(bf, x)

#plot original and filtered signals
plot(t, x, type='l')
lines(t, y, col="red")
lines(t,z,col="blue")

#estimate signal attenuation (proportional reduction in signal amplitude)
1 - mean(abs(range(y[t > .2 & t < .8]))) #~50% attenuation at 5 Hz using filtfilt

1 - mean(abs(range(z[t > .2 & t < .8]))) #~30% attenuation at 5 Hz using filter

#demonstration that half-amplitude cut-off is 6 Hz when apply filter only once
x6hz <- sin(2*pi*t*6)

z6hz <- filter(bf, x6hz)

1 - mean(abs(range(z6hz[t > .2 & t < .8]))) #~50% attenuation at 6 Hz using filter


#plot the filter attenuation profile (for when apply one time, as with "filter" function):

hf <- freqz(bf, Fs = Fs);

plot(c(0, 20, 20, 0, 0), c(0, 0, 1, 1, 0), type = "l", 
 xlab = "Frequency (Hz)", ylab = "Attenuation (abs)")

lines(hf$f[hf$f<=20], abs(hf$h)[hf$f<=20])

plot(c(0, 20, 20, 0, 0), c(0, 0, -50, -50, 0),
 type = "l", xlab = "Frequency (Hz)", ylab = "Attenuation (dB)")

lines(hf$f[hf$f<=20], 20*log10(abs(hf$h))[hf$f<=20])

hf$f[which(abs(hf$h) - .5 < .001)[1]] #half-amplitude cutoff, around 6 Hz

hf$f[which(20*log10(abs(hf$h))+6 < .2)[1]] #half-amplitude cutoff, around 6 Hz

hf$f[which(20*log10(abs(hf$h))+3 < .2)[1]] #half-power cutoff, around 5 Hz
1 голос
/ 09 ноября 2014

в CRAN есть пакет с именем FastICA, который вычисляет аппроксимацию сигналов независимых источников, однако для вычисления обоих сигналов вам нужна матрица не менее 2xn смешанных наблюдений (для этого примера), этот алгоритм можетне определить два независимых сигнала только с вектором 1xn.Смотрите пример ниже.надеюсь, это поможет вам.

number_of_cycles = 2
max_y = 40

x = 1:500
a = number_of_cycles * 2*pi/length(x)

y = max_y * sin(x*a)
noise1 = max_y * 1/10 * sin(x*a*10)

plot(x, y, type="l", col="red", ylim=range(-1.5*max_y,1.5*max_y,5))
points(x, y + noise1, col="green", pch=20)
points(x, noise1, col="yellow", pch=20)
######################################################
library(fastICA)
S <- cbind(y,noise1)#Assuming that "y" source1 and "noise1" is source2
A <- matrix(c(0.291, 0.6557, -0.5439, 0.5572), 2, 2) #This is a mixing matrix
X <- S %*% A 

a <- fastICA(X, 2, alg.typ = "parallel", fun = "logcosh", alpha = 1,
method = "R", row.norm = FALSE, maxit = 200,
tol = 0.0001, verbose = TRUE)

par(mfcol = c(2, 3))
plot(S[,1 ], type = "l", main = "Original Signals",
xlab = "", ylab = "")
plot(S[,2 ], type = "l", xlab = "", ylab = "")
plot(X[,1 ], type = "l", main = "Mixed Signals",
xlab = "", ylab = "")
plot(X[,2 ], type = "l", xlab = "", ylab = "")
plot(a$S[,1 ], type = "l", main = "ICA source estimates",
xlab = "", ylab = "")
plot(a$S[, 2], type = "l", xlab = "", ylab = "")
1 голос
/ 01 марта 2013

Я не уверен, является ли какой-либо фильтр лучшим способом для Вас.Более полезным инструментом для этой цели является быстрое преобразование Фурье.

...