.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/dss/plot_02_artifact_correction.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_dss_plot_02_artifact_correction.py: Artifact Correction with DSS. ============================= DSS is a powerful tool for removing artifacts (ECG, EOG) from data. The core idea is: **Artifacts are repetitive**. If we can define when artifacts happen (e.g., using EOG/ECG channels), we can use **Trial Average Bias** to find the artifact source and remove it. This tutorial demonstrates two artifact-correction workflows with DSS: blink correction based on EOG epochs and heartbeat correction based on ECG epochs. Authors: Sina Esmaeili (sina.esmaeili@umontreal.ca) Hamza Abdelhedi (hamza.abdelhedi@umontreal.ca) .. GENERATED FROM PYTHON SOURCE LINES 19-21 Imports ------- .. GENERATED FROM PYTHON SOURCE LINES 21-40 .. code-block:: Python import contextlib import os import matplotlib.pyplot as plt import mne import numpy as np from mne.datasets import sample from mne.preprocessing import create_ecg_epochs, create_eog_epochs from mne_denoise.dss import DSS, AverageBias, CycleAverageBias from mne_denoise.viz import ( plot_component_patterns, plot_component_score_curve, plot_component_summary, plot_component_time_series, plot_evoked_gfp_comparison, plot_psd_comparison, ) .. GENERATED FROM PYTHON SOURCE LINES 41-44 Load Data --------- We use the MNE sample dataset which contains clear ECG and EOG artifacts. .. GENERATED FROM PYTHON SOURCE LINES 44-60 .. code-block:: Python print("Loading MNE Sample data...") # Ensure MNE_DATA directory exists home = os.path.expanduser("~") mne_data_path = os.path.join(home, "mne_data") if not os.path.exists(mne_data_path): with contextlib.suppress(OSError): os.makedirs(mne_data_path) data_path = sample.data_path() raw_fname = data_path / "MEG" / "sample" / "sample_audvis_raw.fif" raw = mne.io.read_raw_fif(raw_fname, preload=True, verbose=False) raw.crop(0, 60) # Keep full duration but no picking yet print(f"Data: {len(raw.ch_names)} channels (MEG, EEG, EOG, ECG), 60s duration") .. rst-class:: sphx-glr-script-out .. code-block:: none Loading MNE Sample data... Data: 376 channels (MEG, EEG, EOG, ECG), 60s duration .. GENERATED FROM PYTHON SOURCE LINES 61-65 Part 1: EOG (Blink) Correction ------------------------------ The goal is to isolate eye blinks. We epoch the data around blink events from the EOG channel, which turns blink reproducibility into the DSS bias. .. GENERATED FROM PYTHON SOURCE LINES 65-88 .. code-block:: Python print("\n--- Part 1: EOG (Blink) Correction ---") # 1. Create EOG Epochs eog_epochs = create_eog_epochs( raw, ch_name="EOG 061", baseline=(-0.5, -0.2), tmin=-0.5, tmax=0.5, verbose=False ) # IMPORTANT: DSS should be fitted on the data channels (MEG) we want to clean. # We exclude the EOG channel itself from the model. eog_epochs.pick_types(meg="grad", eog=False, ecg=False) print( f"Found {len(eog_epochs)} blink events. " f"Using {len(eog_epochs.ch_names)} MEG channels." ) eog_sensor_picks = np.arange(len(eog_epochs.ch_names)) # 2. Fit DSS with Trial Average Bias # We use AverageBias(axis='epochs') which works on pre-epoched data. # Note: We'll compare with CycleAverageBias later (artifact-specific approach). dss_eog = DSS(n_components=10, bias=AverageBias(axis="epochs"), return_type="sources") dss_eog.fit(eog_epochs) .. rst-class:: sphx-glr-script-out .. code-block:: none --- Part 1: EOG (Blink) Correction --- NOTE: pick_types() is a legacy function. New code should use inst.pick(...). Found 10 blink events. Using 203 MEG channels. /home/runner/work/mne-denoise/mne-denoise/mne_denoise/dss/linear.py:496: RuntimeWarning: Epochs are not baseline corrected, covariance matrix may be inaccurate baseline_cov = mne.compute_covariance(inst, method=method, **kws) /home/runner/work/mne-denoise/mne-denoise/mne_denoise/dss/linear.py:498: RuntimeWarning: Epochs are not baseline corrected, covariance matrix may be inaccurate biased_cov = mne.compute_covariance(biased_inst, method=method, **kws) .. raw:: html
DSS(bias=<mne_denoise.dss.denoisers.averaging.AverageBias object at 0x7f645b57bd70>,
        n_components=10)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


.. GENERATED FROM PYTHON SOURCE LINES 89-91 Visualize Blink Components -------------------------- .. GENERATED FROM PYTHON SOURCE LINES 91-97 .. code-block:: Python # Score Curve # The first component should dominate the score curve if the blink bias is # captured cleanly. plot_component_score_curve(dss_eog, mode="ratio", show=True) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_001.png :alt: Component Scores :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 98-100 Time Series The first component should show the stereotyped blink waveform. .. GENERATED FROM PYTHON SOURCE LINES 100-102 .. code-block:: Python plot_component_time_series(dss_eog, data=eog_epochs, n_components=10, show=True) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_002.png :alt: Component Time Series :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 103-105 Spatial Patterns The leading blink component should also be spatially frontal. .. GENERATED FROM PYTHON SOURCE LINES 105-113 .. code-block:: Python plot_component_patterns( dss_eog, info=eog_epochs.info, picks=eog_sensor_picks, n_components=10, show=True, ) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_003.png :alt: Component Patterns, Comp 0, Comp 1, Comp 2, Comp 3, Comp 4, Comp 5, Comp 6, Comp 7, Comp 8, Comp 9 :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 114-115 Summary (Topo + Time + PSD) .. GENERATED FROM PYTHON SOURCE LINES 115-124 .. code-block:: Python plot_component_summary( dss_eog, data=eog_epochs, info=eog_epochs.info, picks=eog_sensor_picks, n_components=[0, 1], show=True, ) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_004.png :alt: Comp 0 Pattern, Comp 0 Time Course, PSD, Comp 1 Pattern, Comp 1 Time Course, PSD :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_004.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 125-129 Denoising Comparison -------------------- We project the data into the DSS space, **zero out** the first component (the blink), and project back. .. GENERATED FROM PYTHON SOURCE LINES 129-190 .. code-block:: Python print("Removing blink component...") # Transform continuous data # We must ensure we apply to the same channels used in fit (the gradiometers). raw_meg = raw.copy().pick_types(meg="grad", eeg=False, eog=False, ecg=False) raw_meg_picks = np.arange(len(raw_meg.ch_names)) sources = dss_eog.transform(raw_meg) # Check Correlation with EOG channel # This validates that Comp 0 is indeed the blink artifact. eog_picks = mne.pick_types(raw.info, meg=False, eog=True) if len(eog_picks) > 0: eog_data = raw.get_data(picks=eog_picks[0]).flatten() blink_source = sources[0, :] for i in range(3): comp_source = sources[i, :] corr = np.corrcoef(eog_data, comp_source)[0, 1] print(f"Correlation (Comp {i} vs EOG): {abs(corr):.3f}") else: print("No EOG channel found for correlation check.") # Create dummy data for plot to avoid crash, or skip plot? # We'll skip plot logic if no channel, but for now let's hope it exists. eog_data = np.zeros(len(sources[0])) blink_source = sources[0, :] # Visual Comparison: EOG vs Component 0 # Show a time window with clear blinks, scaled and aligned # Find a window with blinks (sample 5000-10000) start_idx, end_idx = 5000, 10000 t_window = np.arange(start_idx, end_idx) / raw.info["sfreq"] # Get data snippets eog_snippet = eog_data[start_idx:end_idx] comp_snippet = blink_source[start_idx:end_idx] # Flip component if negatively correlated corr_window = np.corrcoef(eog_snippet, comp_snippet)[0, 1] flip = -1 if corr_window < 0 else 1 # Scale component to match EOG amplitude scale = np.max(np.abs(eog_snippet)) / np.max(np.abs(comp_snippet)) plt.figure(figsize=(12, 4)) plt.plot(t_window, eog_snippet, "b", linewidth=1.5, label="EOG Channel") plt.plot( t_window, flip * comp_snippet * scale, "r", linewidth=1.5, label="DSS Comp 0 (aligned & scaled)", alpha=0.8, ) plt.xlabel("Time (s)") plt.ylabel("Amplitude (a.u.)") plt.title(f"TrialAverageBias: Blink Peaks Aligned (r={abs(corr):.3f})") plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_005.png :alt: TrialAverageBias: Blink Peaks Aligned (r=0.045) :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_005.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Removing blink component... NOTE: pick_types() is a legacy function. New code should use inst.pick(...). Correlation (Comp 0 vs EOG): 0.682 Correlation (Comp 1 vs EOG): 0.340 Correlation (Comp 2 vs EOG): 0.045 .. GENERATED FROM PYTHON SOURCE LINES 191-195 Alternative: CycleAverageBias (Continuous Data Approach) ========================================================= CycleAverageBias is artifact-specific and works directly on continuous data. Instead of pre-epoching, we provide event samples and a window. .. GENERATED FROM PYTHON SOURCE LINES 195-217 .. code-block:: Python print("\n--- Comparing with CycleAverageBias ---") # Find blink events from continuous data from mne.preprocessing import find_eog_events blink_events = find_eog_events(raw, ch_name="EOG 061", verbose=False) blink_samples = blink_events[:, 0] print(f"Found {len(blink_samples)} blink events") # Create CycleAverageBias # Window: 100ms before to 100ms after each blink (in samples) window_samples = (-int(0.1 * raw.info["sfreq"]), int(0.1 * raw.info["sfreq"])) bias_cycle = CycleAverageBias(event_samples=blink_samples, window=window_samples) # Fit DSS on continuous MEG data dss_cycle = DSS(n_components=10, bias=bias_cycle, return_type="sources") dss_cycle.fit(raw_meg) print("Fitted DSS with CycleAverageBias") .. rst-class:: sphx-glr-script-out .. code-block:: none --- Comparing with CycleAverageBias --- Found 10 blink events Fitted DSS with CycleAverageBias .. GENERATED FROM PYTHON SOURCE LINES 218-220 Visualize Cycle Average Components ----------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 220-232 .. code-block:: Python plot_component_summary( dss_cycle, data=raw_meg, info=raw_meg.info, picks=raw_meg_picks, n_components=[0, 1], show=False, ) plt.gcf().suptitle("CycleAverageBias Results") plt.show() .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_006.png :alt: CycleAverageBias Results, Comp 0 Pattern, Comp 0 Time Course, PSD, Comp 1 Pattern, Comp 1 Time Course, PSD :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_006.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 233-234 Compare spatial patterns (both bias types) .. GENERATED FROM PYTHON SOURCE LINES 234-245 .. code-block:: Python print("\n--- Comparing Spatial Patterns ---") plot_component_patterns( dss_eog, info=eog_epochs.info, picks=eog_sensor_picks, n_components=1, show=False, ) plt.gcf().suptitle("TrialAverageBias: Blink Component Topography") plt.show() .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_007.png :alt: TrialAverageBias: Blink Component Topography, Comp 0 :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_007.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none --- Comparing Spatial Patterns --- .. GENERATED FROM PYTHON SOURCE LINES 246-260 .. code-block:: Python plot_component_patterns( dss_cycle, info=raw_meg.info, picks=raw_meg_picks, n_components=1, show=False, ) plt.gcf().suptitle("CycleAverageBias: Blink Component Topography") plt.show() print("\nBoth approaches extract the same blink artifact!") print("- TrialAverageBias: Works on MNE Epochs (easier integration)") print("- CycleAverageBias: Works on continuous data + event samples (more direct)") .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_008.png :alt: CycleAverageBias: Blink Component Topography, Comp 0 :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_008.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Both approaches extract the same blink artifact! - TrialAverageBias: Works on MNE Epochs (easier integration) - CycleAverageBias: Works on continuous data + event samples (more direct) .. GENERATED FROM PYTHON SOURCE LINES 261-293 .. code-block:: Python n_samples_plot = int(20 * raw.info["sfreq"]) # Plot 20 seconds scaler_eog = 1.0 / np.std(eog_data[:n_samples_plot]) scaler_dss = 1.0 / np.std(blink_source[:n_samples_plot]) # Flip DSS source if anti-correlated for better visual comparison corr_0 = np.corrcoef(eog_data, blink_source)[0, 1] sign = np.sign(corr_0) if sign == 0: sign = 1 plt.figure(figsize=(10, 4)) times_plot = raw.times[:n_samples_plot] plt.plot( times_plot, eog_data[:n_samples_plot] * scaler_eog, label="EOG Channel (Norm)", color="tab:orange", alpha=0.7, ) plt.plot( times_plot, blink_source[:n_samples_plot] * scaler_dss * sign, label="DSS Comp 0 (Sign-Matched)", color="tab:blue", alpha=0.7, ) plt.title(f"Temporal Comparison: EOG vs DSS Comp 0 (Corr={abs(corr_0):.2f})") plt.xlabel("Time (s)") plt.legend() plt.tight_layout() plt.show() .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_009.png :alt: Temporal Comparison: EOG vs DSS Comp 0 (Corr=0.68) :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_009.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 294-298 Blink-Locked Average Before and After ------------------------------------- The blink-locked average should shrink strongly after removing the dominant artifact component. .. GENERATED FROM PYTHON SOURCE LINES 298-328 .. code-block:: Python # Zero out blink sources[0, :] = 0 # Remove Comp 0 # Reconstruct cleaned_data = dss_eog.inverse_transform(sources) raw_clean_eog = mne.io.RawArray(cleaned_data, raw_meg.info) # Compare Raw vs Cleaned on the Blink Epochs # We verify improvement by looking at the average blink before/after. # (We need to re-epoch the cleaned data to compare apples-to-apples) eog_epochs_clean = mne.Epochs( raw_clean_eog, eog_epochs.events, tmin=-0.5, tmax=0.5, baseline=(-0.5, -0.2), verbose=False, ) print("Plotting correction effect...") plot_evoked_gfp_comparison( eog_epochs, eog_epochs_clean, times=eog_epochs.times, show=False, labels=("Original", "Cleaned"), ) plt.show() .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_010.png :alt: Evoked GFP Comparison :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_010.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Creating RawArray with float64 data, n_channels=203, n_times=36038 Range : 0 ... 36037 = 0.000 ... 60.000 secs Ready. Plotting correction effect... Using data from preloaded Raw for 10 events and 601 original time points ... 6 bad epochs dropped .. GENERATED FROM PYTHON SOURCE LINES 329-333 Spectral Preservation After Blink Removal ----------------------------------------- Blinks are mostly low-frequency (<5 Hz), so the alpha and beta structure should remain broadly stable after denoising. .. GENERATED FROM PYTHON SOURCE LINES 333-337 .. code-block:: Python plot_psd_comparison(eog_epochs, eog_epochs_clean, fmax=40, show=True) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_011.png :alt: PSD Comparison :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_011.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Using multitaper spectrum estimation with 7 DPSS windows Using data from preloaded Raw for 4 events and 601 original time points ... Using multitaper spectrum estimation with 7 DPSS windows
.. GENERATED FROM PYTHON SOURCE LINES 338-342 Part 2: ECG (Heartbeat) Correction ---------------------------------- The same idea applies to heartbeat contamination: epoch on the artifact, fit DSS on those repeats, and remove the dominant cardiac component. .. GENERATED FROM PYTHON SOURCE LINES 342-361 .. code-block:: Python print("\n--- Part 2: ECG (Heartbeat) Correction ---") # 1. Create ECG Epochs # We let MNE find the ECG channel automatically (looking for type='ecg') ecg_epochs = create_ecg_epochs( raw, ch_name=None, tmin=-0.1, tmax=0.1, baseline=(None, 0), verbose=False ) ecg_epochs.pick_types(meg="grad", eeg=False, eog=False, ecg=False) print( f"Found {len(ecg_epochs)} heartbeats. " f"Using {len(ecg_epochs.ch_names)} MEG channels." ) ecg_sensor_picks = np.arange(len(ecg_epochs.ch_names)) # 2. Fit DSS dss_ecg = DSS(n_components=8, bias=AverageBias(axis="epochs")) dss_ecg.fit(ecg_epochs) .. rst-class:: sphx-glr-script-out .. code-block:: none --- Part 2: ECG (Heartbeat) Correction --- NOTE: pick_types() is a legacy function. New code should use inst.pick(...). Found 59 heartbeats. Using 203 MEG channels. /home/runner/work/mne-denoise/mne-denoise/mne_denoise/dss/linear.py:496: RuntimeWarning: Epochs are not baseline corrected, covariance matrix may be inaccurate baseline_cov = mne.compute_covariance(inst, method=method, **kws) /home/runner/work/mne-denoise/mne-denoise/mne_denoise/dss/linear.py:498: RuntimeWarning: Epochs are not baseline corrected, covariance matrix may be inaccurate biased_cov = mne.compute_covariance(biased_inst, method=method, **kws) .. raw:: html
DSS(bias=<mne_denoise.dss.denoisers.averaging.AverageBias object at 0x7f6458c08ef0>,
        n_components=8)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


.. GENERATED FROM PYTHON SOURCE LINES 362-364 Visualize Cardiac Components ---------------------------- .. GENERATED FROM PYTHON SOURCE LINES 364-368 .. code-block:: Python # Score Curve plot_component_score_curve(dss_ecg, mode="ratio", show=True) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_012.png :alt: Component Scores :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_012.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 369-371 Time Series The dominant cardiac component should follow a QRS-like shape. .. GENERATED FROM PYTHON SOURCE LINES 371-373 .. code-block:: Python plot_component_time_series(dss_ecg, data=ecg_epochs, n_components=8, show=True) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_013.png :alt: Component Time Series :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_013.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 374-377 Spatial Patterns The corresponding field pattern should look broad and deep rather than strictly focal. .. GENERATED FROM PYTHON SOURCE LINES 377-385 .. code-block:: Python plot_component_patterns( dss_ecg, info=ecg_epochs.info, picks=ecg_sensor_picks, n_components=8, show=True, ) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_014.png :alt: Component Patterns, Comp 0, Comp 1, Comp 2, Comp 3, Comp 4, Comp 5, Comp 6, Comp 7 :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_014.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 386-387 Summary .. GENERATED FROM PYTHON SOURCE LINES 387-397 .. code-block:: Python plot_component_summary( dss_ecg, data=ecg_epochs, info=ecg_epochs.info, picks=ecg_sensor_picks, n_components=[0], show=True, ) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_015.png :alt: Comp 0 Pattern, Comp 0 Time Course, PSD :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_015.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none
.. GENERATED FROM PYTHON SOURCE LINES 398-400 Removing the Artifact --------------------- .. GENERATED FROM PYTHON SOURCE LINES 400-407 .. code-block:: Python print("Removing cardiac component...") sources_ecg = dss_ecg.transform(raw_meg) # Apply to continuous data sources_ecg[0, :] = 0 # Zero out heartbeat raw_clean_ecg = mne.io.RawArray(dss_ecg.inverse_transform(sources_ecg), raw_meg.info) .. rst-class:: sphx-glr-script-out .. code-block:: none Removing cardiac component... Creating RawArray with float64 data, n_channels=203, n_times=36038 Range : 0 ... 36037 = 0.000 ... 60.000 secs Ready. .. GENERATED FROM PYTHON SOURCE LINES 408-412 Heartbeat-Locked Average Before and After ----------------------------------------- The heartbeat-locked average should show a much smaller QRS-like transient after removing the dominant cardiac component. .. GENERATED FROM PYTHON SOURCE LINES 412-432 .. code-block:: Python # Verification ecg_epochs_clean = mne.Epochs( raw_clean_ecg, ecg_epochs.events, tmin=-0.1, tmax=0.1, baseline=(None, 0), verbose=False, ) plot_evoked_gfp_comparison( ecg_epochs, ecg_epochs_clean, times=ecg_epochs.times, show=False, labels=("Original", "Cleaned"), ) plt.show() .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_016.png :alt: Evoked GFP Comparison :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_016.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Using data from preloaded Raw for 59 events and 121 original time points ... 42 bad epochs dropped .. GENERATED FROM PYTHON SOURCE LINES 433-436 Spectral Preservation After Heartbeat Removal --------------------------------------------- Cardiac harmonics should be reduced while broader neural rhythms remain. .. GENERATED FROM PYTHON SOURCE LINES 436-438 .. code-block:: Python plot_psd_comparison(ecg_epochs, ecg_epochs_clean, fmax=40, show=True) .. image-sg:: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_017.png :alt: PSD Comparison :srcset: /auto_examples/dss/images/sphx_glr_plot_02_artifact_correction_017.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Using multitaper spectrum estimation with 7 DPSS windows Using data from preloaded Raw for 17 events and 121 original time points ... Using multitaper spectrum estimation with 7 DPSS windows
.. GENERATED FROM PYTHON SOURCE LINES 439-445 Conclusion ---------- We used DSS with **AverageBias** to find and remove stereotypic artifacts. By epoching on the artifact events, we turned the artifact into the most repeatable signal in the data. DSS isolates that repeatable component, and denoising then reduces to zeroing it before projection back to sensor space. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 12.863 seconds) .. _sphx_glr_download_auto_examples_dss_plot_02_artifact_correction.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_artifact_correction.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_artifact_correction.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_artifact_correction.zip `