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