{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Detecting and correcting artefacts in peaks vector\n\nThis example describes artefacts correction peaks vectors.\n\nThe function `correct_rr()` automatically detect artefacts using the method proposed\nby Lipponen & Tarvainen (2019) [#]_. At each iteration, extra and missed \npeaks are corrected replacement or removal of peaks. The detection procedure is run \nagain using cleaned intervals. When using this method, the signal length stays constant,\nwhich makes it more appropriate for event-related designs where the occurrence of \ncertain events must be controlled.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Author: Nicolas Legrand \n# Licence: Apache-2.0" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\nimport pandas as pd\nfrom systole import import_dataset1\nfrom systole.detection import ecg_peaks\nfrom systole.correction import correct_peaks\nfrom systole.plots import plot_rr, plot_evoked\nimport matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ecg_df = import_dataset1(modalities=['ECG', 'Stim'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "signal, peaks = ecg_peaks(ecg_df.ecg, method='pan-tompkins', sfreq=1000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "are using Matplotlib as plotting backend.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plot_rr(peaks, input_type='peaks', figsize=(13, 5))\nplt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "np.random.seed(123) # For result reproductibility\n\ncorrupted_peaks = peaks.copy() # Create a new RR intervals vector\n\n# Randomly select 50 peaks in the peask vector and set it to 0 (missed peaks)\ncorrupted_peaks[np.random.choice(np.where(corrupted_peaks)[0], 50)] = 0\n\n# Randomly add 50 intervals in the peaks vector (extra peaks)\ncorrupted_peaks[np.random.choice(len(corrupted_peaks), 50)] = 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "using `show_artefacts=True` so the artefacts detection runs automatically and shows\nin the plot.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plot_rr(\n corrupted_peaks, input_type='peaks', \n show_artefacts=True, line=False, figsize=(13, 5)\n )\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "correction method. This function will automatically detect possible artefacts in the \npeaks vector and reconstruct the most coherent values using time series interpolation. \nThe number of iteration is set to `2` by default, we add it here for clarity. Here, \nthe `correct_peaks` function only correct for extra and missed peaks. This feature is \nintentional and reflects the notion that only artefacts in R peaks detection should \nbe corrected, but \"true\" intervals that are anomaly shorter or longer should not be \ncorrected.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "peaks_correction = correct_peaks(corrupted_peaks)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plot_rr(peaks_correction[\"clean_peaks\"], input_type=\"peaks\", \n show_artefacts=True, line=False, figsize=(13, 5))\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "event-related analysis, where the evolution of the instantaneous heart rate is \nassessed after some experimental manipulation (see Tutorial 5). One way to control \nfor the quality of the artefacts correction is to compare the evoked responses \nmeasured under corrupted, corrected and baseline recording. Here, we will use the \n`plot_evoked` function, which simply take the indexes of events as input together \nwith the recording (here the peaks vector), and produce the evoked plots.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Merge the two conditions together.\n# The events of interest are all data points that are not 0.\ntriggers_idx = [np.where(ecg_df.stim.to_numpy() != 0)[0]]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "_, axs = plt.subplots(1, 3, figsize=(18, 6), sharey=True)\nplot_evoked(rr=corrupted_peaks, triggers_idx=triggers_idx, ci=68,\n input_type=\"peaks\", decim=100, apply_baseline=(-1.0, 0.0), figsize=(8, 8),\n labels=\"Uncorrected\", palette=[\"#c44e52\"], ax=axs[0])\nplot_evoked(rr=peaks_correction[\"clean_peaks\"], triggers_idx=triggers_idx, ci=68,\n input_type=\"peaks\", decim=100, apply_baseline=(-1.0, 0.0), figsize=(8, 8),\n labels=\"Corrected\", ax=axs[1])\nplot_evoked(rr=peaks, triggers_idx=triggers_idx, ci=68, palette=[\"#55a868\"],\n input_type=\"peaks\", decim=100, apply_baseline=(-1.0, 0.0), figsize=(8, 8),\n labels=\"Initial recording\", ax=axs[2])\nplt.ylim(-20, 20);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## References\n.. [#] Lipponen, J. A., & Tarvainen, M. P. (2019). A robust algorithm for\n heart rate variability time series artefact correction using novel\n beat classification. Journal of Medical Engineering & Technology,\n 43(3), 173\u2013181. https://doi.org/10.1080/03091902.2019.1640306\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.16" } }, "nbformat": 4, "nbformat_minor": 0 }