Signal Preprocessing
This notebook demonstrates noise reduction, baseline correction, bandpass filtering, and respiratory extraction for physiological signals using vitalDSP.
Setup
import numpy as np
import plotly.io as pio
pio.renderers.default = "sphinx_gallery"
from plotly import graph_objects as go
from vitalDSP.notebooks import load_sample_ecg_small, load_sample_ppg, plot_trace
fs = 128
signal_col, date_col = load_sample_ecg_small()
signal_col = np.array(signal_col)
ppg_col, ppg_date = load_sample_ppg()
ppg_col = np.array(ppg_col)
print(f"ECG signal: {len(signal_col)} samples at {fs} Hz")
plot_trace(signal_col)
ECG signal: 82176 samples at 128 Hz
Wavelet Denoising
from vitalDSP.preprocess.noise_reduction import wavelet_denoising
# Soft-threshold wavelet denoising
wavelet_denoised = wavelet_denoising(signal_col, wavelet_name='db4', level=4)
plot_trace(signal_col, wavelet_denoised)
Median, Moving Average, and Gaussian Denoising
from vitalDSP.preprocess.noise_reduction import median_denoising, moving_average_denoising, gaussian_denoising
median_denoised = median_denoising(signal_col, kernel_size=5)
ma_denoised = moving_average_denoising(signal_col, window_size=5)
gauss_denoised = gaussian_denoising(signal_col, sigma=1.0)
fig = go.Figure()
fig.add_trace(go.Scatter(y=signal_col[:512], mode='lines', name='Raw ECG', opacity=0.5))
fig.add_trace(go.Scatter(y=median_denoised[:512], mode='lines', name='Median Filter'))
fig.add_trace(go.Scatter(y=ma_denoised[:512], mode='lines', name='Moving Average'))
fig.add_trace(go.Scatter(y=gauss_denoised[:512], mode='lines', name='Gaussian'))
fig.update_layout(title='Noise Reduction Comparison', xaxis_title='Sample', yaxis_title='Amplitude')
fig.show()
Savitzky-Golay Smoothing
from vitalDSP.preprocess.noise_reduction import savgol_denoising
savgol_denoised = savgol_denoising(signal_col, window_length=11, polyorder=3)
plot_trace(signal_col, savgol_denoised)
Full Preprocessing Pipeline
from vitalDSP.preprocess.preprocess_operations import preprocess_signal
# Apply bandpass filter + wavelet denoising in one call
preprocessed = preprocess_signal(
signal_col,
sampling_rate=fs,
filter_type='bandpass',
lowcut=0.5,
highcut=40.0,
order=4,
noise_reduction_method='wavelet',
wavelet_name='db4',
level=3
)
plot_trace(signal_col, preprocessed)
Baseline Estimation
from vitalDSP.preprocess.preprocess_operations import estimate_baseline
# Estimate slow baseline drift via moving average
baseline = estimate_baseline(signal_col, fs=fs, method='moving_average', window_size=64)
corrected = signal_col - baseline
fig = go.Figure()
fig.add_trace(go.Scatter(y=signal_col[:512], mode='lines', name='Raw ECG', opacity=0.5))
fig.add_trace(go.Scatter(y=baseline[:512], mode='lines', name='Baseline', line=dict(dash='dash')))
fig.add_trace(go.Scatter(y=corrected[:512], mode='lines', name='Baseline Corrected'))
fig.update_layout(title='Baseline Estimation and Correction', xaxis_title='Sample', yaxis_title='Amplitude')
fig.show()
Respiratory Component Extraction
from vitalDSP.preprocess.preprocess_operations import respiratory_filtering
# Bandpass filter to isolate the respiratory frequency band (0.1–0.5 Hz)
resp_signal = respiratory_filtering(signal_col, sampling_rate=fs, lowcut=0.1, highcut=0.5)
t = np.arange(len(signal_col)) / fs
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=signal_col, mode='lines', name='ECG', opacity=0.4))
fig.add_trace(go.Scatter(x=t, y=resp_signal, mode='lines', name='Respiratory Component (0.1–0.5 Hz)'))
fig.update_layout(
title='Respiratory Component Extraction from ECG',
xaxis_title='Time (s)', yaxis_title='Amplitude'
)
fig.show()
PPG Preprocessing
# Chain denoising and bandpass filtering for PPG
ppg_denoised = wavelet_denoising(ppg_col, wavelet_name='db4', level=3)
ppg_preprocessed = preprocess_signal(
ppg_denoised,
sampling_rate=fs,
filter_type='bandpass',
lowcut=0.5,
highcut=8.0,
order=4,
noise_reduction_method='moving_average'
)
plot_trace(ppg_col, ppg_preprocessed)