Как фильтровать / сглаживать с помощью SciPy / Numpy?
Я пытаюсь фильтровать / сглаживать сигнал, полученный от датчика давления с частотой дискретизации 50 кГц. Ниже показан пример сигнала:
Я хотел бы получить гладкий сигнал, полученный лессом в MATLAB (я не строю одни и те же данные, значения разные).
Я рассчитал спектральную плотность мощности с помощью функции psd () matplotlib, а также спектральная плотность мощности ниже:
Я попытался использовать следующий код и получил отфильтрованный сигнал:
import csv
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
from scipy.signal import butter, lfilter, freqz
def butter_lowpass(cutoff, fs, order=5):
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low', analog=False)
return b, a
def butter_lowpass_filter(data, cutoff, fs, order=5):
b, a = butter_lowpass(cutoff, fs, order=order)
y = lfilter(b, a, data)
return y
data = np.loadtxt('data.dat', skiprows=2, delimiter=',', unpack=True).transpose()
time = data[:,0]
pressure = data[:,1]
cutoff = 2000
fs = 50000
pressure_smooth = butter_lowpass_filter(pressure, cutoff, fs)
figure_pressure_trace = plt.figure(figsize=(5.15, 5.15))
figure_pressure_trace.clf()
plot_P_vs_t = plt.subplot(111)
plot_P_vs_t.plot(time, pressure, linewidth=1.0)
plot_P_vs_t.plot(time, pressure_smooth, linewidth=1.0)
plot_P_vs_t.set_ylabel('Pressure (bar)', labelpad=6)
plot_P_vs_t.set_xlabel('Time (ms)', labelpad=6)
plt.show()
plt.close()
вывод, который я получаю:
Мне нужно больше сглаживания, я попытался изменить частоту среза, но по-прежнему удовлетворительные результаты не могут быть получены. Я не могу получить такую же гладкость MATLAB. Я уверен, что это можно сделать в Python, но как?
вы можете найти данные здесь.
обновление
я применил сглаживание lowess из statsmodels, это также не дает удовлетворительных результатов.
2 ответов
вот несколько предложений.
сначала попробуйте . Он применяет фильтр дважды, один раз вперед и один раз назад, что приводит к нулевой фазе задержка.
вот модифицированная версия вашего скрипта. Существенными изменениями являются использование filtfilt
вместо lfilter
, и смены cutoff
от 3000 до 1500. Возможно, вы захотите поэкспериментировать с этим параметром-более высокие значения приводят к лучшему отслеживанию начала увеличения давления, но слишком высокое значение не отфильтровывает колебания 3 кГц (примерно) после увеличения давления.
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, filtfilt
def butter_lowpass(cutoff, fs, order=5):
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low', analog=False)
return b, a
def butter_lowpass_filtfilt(data, cutoff, fs, order=5):
b, a = butter_lowpass(cutoff, fs, order=order)
y = filtfilt(b, a, data)
return y
data = np.loadtxt('data.dat', skiprows=2, delimiter=',', unpack=True).transpose()
time = data[:,0]
pressure = data[:,1]
cutoff = 1500
fs = 50000
pressure_smooth = butter_lowpass_filtfilt(pressure, cutoff, fs)
figure_pressure_trace = plt.figure()
figure_pressure_trace.clf()
plot_P_vs_t = plt.subplot(111)
plot_P_vs_t.plot(time, pressure, 'r', linewidth=1.0)
plot_P_vs_t.plot(time, pressure_smooth, 'b', linewidth=1.0)
plt.show()
plt.close()
вот график результата. Примечание отклонение отфильтрованного сигнала на правом краю. Чтобы справиться с этим, вы можете поэкспериментировать с padtype
и padlen
параметры filtfilt
, или, если вы знаете, что у вас достаточно данных, вы можете отбросить края отфильтрованного сигнала.
пример использования loewess
fit. Обратите внимание, что я удалил заголовок из data.dat
.
из графика кажется, что этот метод хорошо работает на подмножествах данных. Подгонка всех данных сразу не дает разумного результата. Так что, наверное, это не лучший метод.
import pandas as pd
import matplotlib.pylab as plt
from statsmodels.nonparametric.smoothers_lowess import lowess
data = pd.read_table("data.dat", sep=",", names=["time", "pressure"])
sub_data = data[data.time > 21.5]
result = lowess(sub_data.pressure, sub_data.time.values)
x_smooth = result[:,0]
y_smooth = result[:,1]
tot_result = lowess(data.pressure, data.time.values, frac=0.1)
x_tot_smooth = tot_result[:,0]
y_tot_smooth = tot_result[:,1]
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(data.time.values, data.pressure, label="raw")
ax.plot(x_tot_smooth, y_tot_smooth, label="lowess 1%", linewidth=3, color="g")
ax.plot(x_smooth, y_smooth, label="lowess", linewidth=3, color="r")
plt.legend()
Это результат, который я получаю: