### Setup: install and import libraries Source: https://legrandnico.github.io/systole/notebooks/1-PhysiologicalSignals.html Setup for the notebook: installs systole in Google Colab, imports plotting and Systole libraries, and configures Bokeh and seaborn. ```python %%capture import sys if "google.colab" in sys.modules: !pip install systole ``` ```python import matplotlib.pyplot as plt import numpy as np import seaborn as sns from bokeh.io import output_notebook from bokeh.plotting import show from systole.detection import ecg_peaks from systole.plots import plot_raw from systole.utils import input_conversion from systole import import_dataset1, import_ppg output_notebook() sns.set_context("talk") ``` -------------------------------- ### Initialize recording with setup Source: https://legrandnico.github.io/systole/generated/recording/systole.recording.Oximeter.html Use the setup method to find the start byte and ensure data synchronization. Do not wait more than ~10s between setup and recording to avoid buffer overwriting. ```python >>> oximeter.setup() ``` -------------------------------- ### Import libraries and set author/licence Source: https://legrandnico.github.io/systole/auto_examples/Artefacts/plot_RRCorrection.html Imports required libraries and sets the author and licence for the example. This is the initial setup for the script. ```python # Author: Nicolas Legrand # Licence: GPL v3 ``` ```python import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns from systole import import_dataset1 from systole.correction import correct_rr from systole.detection import ecg_peaks from systole.hrv import frequency_domain from systole.plots import plot_frequency, plot_rr from systole.utils import input_conversion ``` -------------------------------- ### Initialize Oximeter and setup Source: https://legrandnico.github.io/systole/_sources/auto_examples/Recording/plot_HeartBeatrpeggios.rst.txt Creates an Oximeter instance with the serial connection, a sampling frequency of 75 Hz, and 4 additional channels, then calls setup(). The output shows 'Reset input buffer'. ```Python oxi = Oximeter(serial=ser, sfreq=75, add_channels=4).setup() ``` ```none Reset input buffer ``` -------------------------------- ### Install dev branch with pip Source: https://legrandnico.github.io/systole/getting_started.html Downloads and installs the dev branch to try the latest features that are under development and possibly unstable. ```bash pip install “git+https://github.com/LegrandNico/systole.git@dev” ``` -------------------------------- ### Example script header Source: https://legrandnico.github.io/systole/auto_examples/Plots/plot_circular.html Header comment for the example script, indicating the author and license. ```python # Author: Nicolas Legrand # Licence: GPL v3 ``` -------------------------------- ### Install stable version with pip Source: https://legrandnico.github.io/systole/getting_started.html Installs the last stable version of Systole using pip. ```bash pip install systole ``` -------------------------------- ### Setup Oximeter recording Source: https://legrandnico.github.io/systole/_modules/systole/recording.html Initialize the recording to synchronize data. Perform this shortly before recording to avoid buffer overwrites. ```python oximeter.setup() ``` -------------------------------- ### Configuring Serial Port Connection Source: https://legrandnico.github.io/systole/_downloads/517fdd79621ce3738da52dd7295d8666/plot_RecordingPPG.ipynb Setup the serial connection using a simulation or a physical USB device. ```python ser = serialSim() # Simulate a device # Use a USB device # import serial # ser = serial.Serial("COM4") # Use this line for USB recording ``` -------------------------------- ### Import libraries and systole functions Source: https://legrandnico.github.io/systole/_downloads/05f87e8486fb7fc51000715bce07f897/plot_RRCorrection.ipynb Imports required libraries and systole functions for the example. ```python import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns from systole import import_dataset1 from systole.correction import correct_rr from systole.detection import ecg_peaks from systole.hrv import frequency_domain from systole.plots import plot_frequency, plot_rr from systole.utils import input_conversion ``` -------------------------------- ### BIDS folder structure example Source: https://legrandnico.github.io/systole/notebooks/6-WorkingWithBIDSFolders.html Visual representation of a valid BIDS directory structure for physiological recordings. ```text └─ BIDS/ ├─ sub-0001/ │ └─ ses-session1/ │ └─ beh/ │ ├─ sub-0001_ses_session1_task-mytask_physio.tsv.gz │ └─ sub-0001_ses_session1_task-mytask_physio.json │ ├─ sub-0002/ ├─ sub-0003/ └─ ... ``` -------------------------------- ### Example: Using Bokeh as plotting backend Source: https://legrandnico.github.io/systole/_modules/systole/plots/plot_ectopic.html Shows how to use the Bokeh backend by calling `output_notebook()` and passing `backend="bokeh"` to `plot_ectopic`, then displaying with `show`. ```python from bokeh.io import output_notebook from bokeh.plotting import show from systole.detection import rr_artefacts output_notebook() # Use the rr_artefacts function to find ectopic beats artefacts = rr_artefacts(rr) show( plot_ectopic(artefacts=artefacts, backend="bokeh") ) ``` -------------------------------- ### Using the Viewer to navigate preprocessed folder Source: https://legrandnico.github.io/systole/_sources/notebooks/6-WorkingWithBIDSFolders.ipynb.txt Setup for using the Viewer to navigate preprocessed BIDS folders. Imports required modules, creates a Viewer instance pointing to the preprocessed folder, and displays the interactive widgets. The Viewer wraps the Editor and allows inspection and editing of preprocessed ECG recordings. ```python from IPython.display import display from systole.interact import Viewer %matplotlib ipympl ``` ```python view = Viewer( figsize=(15, 5), preprocessed_folder="/path/to/BIDS/folder/derivatives/systole/", pattern="task-my_task", # A string long enough to disambiguate in case of mmultiple recordings modality="beh", signal_type="ECG" ) ``` ```python display(view.io_box, view.editor.commands_box, view.output) ``` -------------------------------- ### Install Systole stable and dev branches with pip Source: https://legrandnico.github.io/systole/_sources/getting_started.rst.txt Install the last stable version of Systole with pip, or install the dev branch to try the latest (possibly unstable) features. The dev branch is fetched directly from the GitHub repository. ```shell pip install systole ``` ```shell pip install “git+https://github.com/LegrandNico/systole.git@dev” ``` -------------------------------- ### Import PPG signal and ppg_peaks Source: https://legrandnico.github.io/systole/generated/detection/systole.detection.ppg_peaks.html Imports the necessary modules and loads a sample PPG signal for use in peak detection examples. ```python >>> from systole import import_ppg >>> from systole.detection import ppg_peaks >>> ppg = import_ppg().ppg.to_numpy() # Import PPG signal ``` -------------------------------- ### Output for DataFrame example Source: https://legrandnico.github.io/systole/auto_examples/Plots/plot_circular.html Output of the DataFrame example, showing two UserWarnings about set_ticklabels() and the resulting PolarAxes object. ```output /home/runner/work/systole/systole/src/systole/plots/backends/matplotlib/plot_circular.py:132: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax.set_xticklabels(circle_label) /home/runner/work/systole/systole/src/systole/plots/backends/matplotlib/plot_circular.py:132: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax.set_xticklabels(circle_label) ``` -------------------------------- ### Output for numpy array example Source: https://legrandnico.github.io/systole/auto_examples/Plots/plot_circular.html Output of the numpy array example, showing a UserWarning about set_ticklabels() and the resulting PolarAxes object. ```output /home/runner/work/systole/systole/src/systole/plots/backends/matplotlib/plot_circular.py:132: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. ax.set_xticklabels(circle_label) ``` -------------------------------- ### Install Systole in Google Colab Source: https://legrandnico.github.io/systole/_sources/notebooks/6-WorkingWithBIDSFolders.ipynb.txt Installs the systole package when running in a Google Colab environment. The code checks if 'google.colab' is in sys.modules and then uses pip to install systole. ```python %%capture import sys if "google.colab" in sys.modules: !pip install systole ``` -------------------------------- ### Load ECG dataset with import_dataset1 Source: https://legrandnico.github.io/systole/notebooks/1-PhysiologicalSignals.html Loads the example ECG dataset using Systole's import_dataset1, requesting only the ECG modality to save time. ```python ecg_df = import_dataset1(modalities=["ECG"], disable=True) ``` -------------------------------- ### to_epochs usage examples from docstring Source: https://legrandnico.github.io/systole/_modules/systole/utils.html These examples show how to use the to_epochs function with different inputs: using event indices, using event triggers, and using a rejection vector. They assume a dataset loaded via import_dataset1 with ECG and Stim modalities. ```python >>> ecg_df = import_dataset1(modalities=['ECG', 'Stim']) >>> triggers_idx = [ >>> np.where(ecg_df.stim.to_numpy() == 2)[0], >>> np.where(ecg_df.stim.to_numpy() == 1)[0] >>> ] >>> signal = ecg_df.ecg.to_numpy() ``` ```python >>> epoch, rejected = to_epochs(signal=signal, triggers_idx=triggers_idx) ``` ```python >>> epoch, rejected = to_epochs(signal=signal, triggers=ecg_df.stim.to_numpy(), >>> event_val=2, apply_baseline=(-1.0, 0.0)) ``` ```python >>> reject = np.zeros(len(signal)) >>> reject[768285:] = 1 # Reject the second part of the recording >>> epoch, rejected = to_epochs( >>> signal=signal, triggers=ecg_df.stim.to_numpy(), event_val=2, >>> apply_baseline=(-1.0, 0.0), reject=reject >>> ) ``` -------------------------------- ### Run systole preprocessing via command line Source: https://legrandnico.github.io/systole/_sources/notebooks/6-WorkingWithBIDSFolders.ipynb.txt Executes the systole preprocessing pipeline on a specified BIDS folder for a given task and modality. This example runs 10 parallel jobs and disables HTML report generation. ```bash systole --bids_folder="/path/to/BIDS/folder/" \ --patterns="task-mytask" \ --modality="beh" \ --n_jobs=10 \ --overwrite=True \ --html_reports==False ``` -------------------------------- ### Check system configuration with watermark Source: https://legrandnico.github.io/systole/_sources/notebooks/6-WorkingWithBIDSFolders.ipynb.txt Displays the current environment configuration, including Python version and installed package versions for systole and numba. ```python %load_ext watermark %watermark -n -u -v -iv -w -p systole,numba ``` -------------------------------- ### Example: Using get_valid_segments Source: https://legrandnico.github.io/systole/generated/utils/systole.utils.get_valid_segments.html Demonstrates how to use get_valid_segments with a random signal and a list of bad segments. The function returns a list of valid time series sorted by length. The example prints the lengths of the valid segments. ```python >>> import numpy as np >>> from systole.utils import get_valid_segments >>> signal = np.random.normal(size=1000) >>> bad_segments = [(500, 550), (700, 800)] >>> valids = get_valid_segments(signal=signal, bad_segments=bad_segments) >>> [len(sig) for sig in valids] `[500, 200, 150]` ``` -------------------------------- ### Import libraries for heartbeat evoked arpeggios Source: https://legrandnico.github.io/systole/auto_examples/Recording/plot_HeartBeatrpeggios.html Imports required libraries and modules for the heartbeat evoked arpeggios example, including systole's serialSim, Oximeter, and plotting utilities. ```python # Author: Nicolas Legrand # Licence: GPL v3 import itertools import time import matplotlib.pyplot as plt import numpy as np import seaborn as sns from systole import serialSim from systole.plots import plot_circular, plot_events from systole.recording import Oximeter from systole.utils import norm_triggers, to_angles ``` -------------------------------- ### Nonin3231USB Source: https://legrandnico.github.io/systole/generated/recording/systole.recording.Nonin3231USB.html Documentation for the Nonin3231USB class, including its constructor, methods, and usage example. ```APIDOC ## Nonin3231USB ### Description Class for recording Nonin 3231 USB HR signal through a USB connection. ### Constructor `Nonin3231USB(serial, add_channels: int | None = 1)` ### Parameters - **serial** (pySerial object) - Required - The serial instance interfacing with the USB port. - **add_channels** (int | None) - Optional - Number of additional channels to add. Default is 1. ### Methods #### `__init__(serial, add_channels: int | None = 1)` Initializes the recording instance. #### `close()` Closes serial connections. #### `read(duration)` Reads PPG signal for some amount of time. #### `readInWaiting([bool])` Reads in waiting Nonin data. #### `reset(serial[, add_channels])` Initializes/restarts the recording instance. #### `setup()` Resets the recording instance. ### Example ```python import serial ser = serial.Serial('COM4') from ecg.recording import Nonin3231USB exg = Nonin3231USB(serial).read(30) ``` ### Warning The signals received from the host are appended to a list. This process can require more time at each iteration as the signal length increases in memory. You should always make sure that this will not interfere with other tasks and regularly save intermediate recordings to save resources. ``` -------------------------------- ### Visualize Poincare plot with Bokeh Source: https://legrandnico.github.io/systole/generated/plots/systole.plots.plot_poincare.html Example of generating a Poincare plot using the Bokeh backend for interactive visualization. ```python from bokeh.io import output_notebook from bokeh.plotting import show output_notebook() from systole import import_rr from systole.plots import plot_poincare show( plot_poincare(rr, input_type="rr_ms", backend="bokeh") ) ``` -------------------------------- ### Enabling online data acquisition Source: https://legrandnico.github.io/systole/_downloads/146b12539d7cede3f2dbe2d9515e86a0/plot_InstantaneousHeartRate.ipynb Example code to connect to a physical pulse oximeter via a COM port. ```python import serial ser = serial.Serial('COM4') # Change this value according to your setup ``` -------------------------------- ### Plot evoked heart rate with Bokeh backend Source: https://legrandnico.github.io/systole/_modules/systole/plots/plot_evoked.html This example shows how to use `plot_evoked` with the Bokeh backend. It sets up the notebook output, defines common plotting arguments, and creates three plots using the same three input types as the Matplotlib example. Finally, it displays the plots side by side using `show(row(...))`. ```python from bokeh.io import output_notebook from bokeh.plotting import show from bokeh.layouts import row output_notebook() # We define a common set of plotting arguments here plot_args = { "backend": "bokeh", "figsize": (400, 400), "palette": [sns.xkcd_rgb["denim blue"], sns.xkcd_rgb["pale red"]], "tmin": -1.0, "tmax": 10.0, "apply_baseline": (-1.0, 0.0), "decim": 100 } # Using the raw signal and events triggers raw_plot = plot_evoked( signal=ecg_df.ecg.to_numpy(), triggers_idx=triggers_idx, modality="ecg", **plot_args ) # Using the detected peaks and events triggers peaks_plot = plot_evoked( rr=peaks, triggers_idx=triggers_idx, input_type="peaks", **plot_args ) # Using the list of epochs arrays epochs_plots = plot_evoked(epochs=hr_epochs, **plot_args) # Create a Bokeh layout and plot the figures side by side show(row(raw_plot, peaks_plot, epochs_plots)) ``` -------------------------------- ### Frequency Domain Table Output Source: https://legrandnico.github.io/systole/notebooks/4-HeartRateVariability.html Example output of the frequency domain table generated by the tabulate backend. ```text ================= ============ ============= =========== ============== Frequency Band Peaks (Hz) Power (ms²) Power (%) Power (n.u.) ================= ============ ============= =========== ============== VLF (0-0.04 Hz) 0.0078 1441.6998 56.7621 LF (0.04-0.15 Hz) 0.0938 522.1761 20.5589 47.5484 HF (0.15-0.4 Hz) 0.3555 576.0226 22.6790 52.4516 Total 2539.8985 LF/HF 0.9065 ================= ============ ============= =========== ============== ``` -------------------------------- ### Import ECG and Stim channel data Source: https://legrandnico.github.io/systole/notebooks/5-InstantaneousHeartRate.html Loads the example ECG and Stim channels from the Systole dataset. The stim channel encodes disgusting (2) and neutral (1) image presentations. ```python # Import ECG recording and Stim channel ecg_df = import_dataset1(modalities=["ECG", "Stim"], disable=True) ``` -------------------------------- ### Interpolate clipping artifacts in a PPG signal Source: https://legrandnico.github.io/systole/generated/detection/systole.detection.interpolate_clipping.html This example demonstrates how to import a PPG signal, manually introduce clipping artifacts by capping values, and then use interpolate_clipping to correct the signal and visualize the results with matplotlib. ```python >>> import matplotlib.pyplot as plt >>> from systole import import_ppg >>> from systole.detection import interpolate_clipping >>> df = import_ppg() >>> # Create lower and upper clipping artefacts >>> df.ppg.loc[df.ppg<=50] = 50 >>> df.ppg.loc[df.ppg>=230] = 230 >>> # Correct clipping artefacts >>> clean_signal = interpolate_clipping(df.ppg.to_numpy(), min_threshold=50, max_threshold=230) >>> # Plot >>> plt.plot(df.time, clean_signal, color='#F15854', label="Corrected signal") >>> plt.plot(df.time, df.ppg, color='#5DA5DA', label="Clipping artefacts") >>> plt.axhline(y=50, linestyle='--', color='k') >>> plt.axhline(y=230, linestyle='--', color='k') >>> plt.xlabel('Time (s)') >>> plt.ylabel('PPG level (a.u)') >>> plt.xlim(10, 40) >>> plt.legend() ``` -------------------------------- ### Example usage of rr_artefacts Source: https://legrandnico.github.io/systole/_modules/systole/detection.html Shows how to simulate an RR time series and call `rr_artefacts` to obtain artefact detection results. The output prints the keys of the returned dictionary. ```python >>> from systole import simulate_rr >>> from systole.detection import rr_artefacts >>> rr = simulate_rr() # Simulate RR time series >>> artefacts = rr_artefacts(rr) >>> print(artefacts.keys()) dict_keys(['subspace1', 'subspace2', 'subspace3', 'mRR', 'ectopic', 'long', 'short', 'missed', 'extra', 'threshold1', 'threshold2']) ``` -------------------------------- ### Plot signal and launch Editor for peak correction Source: https://legrandnico.github.io/systole/_modules/systole/interact/interact.html Loads the physiological signal and starts the interactive Editor for peak correction. Clears the output and displays the editor with the signal. ```python def plot_signal(self, change): """Plot physiological signal and peaks.""" # Load the physio files and store parameters in the Viewer class # then load the signal from the physio file and perform peaks detection self = self.load_file().load_signal() self.output.clear_output() with self.output: # Start the interactive editor for peaks correction self.editor = Editor( signal=self.input_signal, sfreq=1000, corrected_json=self.corrected_json, signal_type=self.signal_type_.value, figsize=self.figsize, corrected_peaks=self.corrected_peaks, bad_segments=self.bad_segments, viewer=self, ) plt.show() ``` -------------------------------- ### Using the Editor to inspect raw ECG signal Source: https://legrandnico.github.io/systole/_sources/notebooks/6-WorkingWithBIDSFolders.ipynb.txt Setup for using the Editor to inspect raw ECG signal. Imports required modules, loads an ECG time series, and creates an Editor instance with a corrected JSON path and ECG signal type. The editor automatically applies peak detection and displays a command box for editing. ```python from systole import import_dataset1 from systole.interact import Viewer, Editor from IPython.display import display %matplotlib ipympl ``` ```python # Load a ray ECG time series ``` ```python editor = Editor( signal=ecg, sfreq=1000, corrected_json="./corrected.json", figsize=(15, 5), signal_type="ECG" ) display(editor.commands_box) ``` -------------------------------- ### Initialize Viewer for preprocessed folders Source: https://legrandnico.github.io/systole/notebooks/6-WorkingWithBIDSFolders.html Sets up the Viewer imports and interactive backend for navigating preprocessed BIDS derivatives. ```python from IPython.display import display from systole.interact import Viewer %matplotlib ipympl ``` -------------------------------- ### Plot RR intervals with Bokeh backend Source: https://legrandnico.github.io/systole/_modules/systole/plots/plot_rr.html Use Bokeh as a plotting backend, only show the scatterplot and highlight artefacts in the RR intervals. The example calls `output_notebook()` and then `show()` with `plot_rr` using `backend="bokeh"`, `line=False`, and `show_artefacts=True`. ```python from bokeh.io import output_notebook from bokeh.plotting import show output_notebook() show( plot_rr( rr=rr, input_type="rr_ms", backend="bokeh", line=False, show_artefacts=True ) ) ``` -------------------------------- ### Configure and display the Editor Source: https://legrandnico.github.io/systole/notebooks/6-WorkingWithBIDSFolders.html Initializes the Editor instance with signal parameters and displays the command box for manual interaction. ```python editor = Editor( signal=ecg, sfreq=1000, corrected_json="./corrected.json", figsize=(15, 5), signal_type="ECG" ) display(editor.commands_box) ``` -------------------------------- ### Configure Viewer for preprocessed data Source: https://legrandnico.github.io/systole/notebooks/6-WorkingWithBIDSFolders.html Initializes the Viewer to navigate a specific preprocessed BIDS folder. ```python view = Viewer( figsize=(15, 5), preprocessed_folder="/path/to/BIDS/folder/derivatives/systole/", pattern="task-my_task", # A string long enough to disambiguate in case of mmultiple recordings modality="beh", signal_type="ECG" ) ``` -------------------------------- ### Initialize Viewer with BIDS folder Source: https://legrandnico.github.io/systole/_sources/notebooks/6-WorkingWithBIDSFolders.ipynb.txt Use the Viewer class with the bids_folder argument to inspect raw BIDS data directly. This approach allows for on-the-fly preprocessing without generating full subject or group reports. ```python view = Viewer( figsize=(15, 5), bids_folder="/path/to/BIDS/folder/", pattern="task-my_task", # A string long enough to disambiguate in case of mmultiple recordings modality="beh", signal_type="ECG" ) ``` -------------------------------- ### Matplotlib example for plot_frequency Source: https://legrandnico.github.io/systole/generated/plots/systole.plots.plot_frequency.html Visualizes HRV frequency domain from RR time series using Matplotlib as plotting backend. The example imports RR data and calls plot_frequency with input_type='rr_ms'. ```python from systole import import_rr from systole.plots import plot_frequency # Import PPG recording as numpy array rr = import_rr().rr.to_numpy() plot_frequency(rr, input_type="rr_ms") ``` ```text ``` -------------------------------- ### Initialize Oximeter and Perform Online Peak Detection Source: https://legrandnico.github.io/systole/_downloads/517fdd79621ce3738da52dd7295d8666/plot_RecordingPPG.ipynb This snippet demonstrates how to set up an Oximeter instance and run a loop to detect heartbeats in real-time from the serial port for 10 seconds. ```python # Create an Oxymeter instance and initialize recording oxi = Oximeter(serial=ser, sfreq=75, add_channels=4).setup() # Online peak detection - run for 10 seconds tstart = time.time() while time.time() - tstart < 10: while oxi.serial.inWaiting() >= 5: paquet = list(oxi.serial.read(5)) oxi.add_paquet(paquet[2]) # Add new data point if oxi.peaks[-1] == 1: print("Heartbeat detected") ``` -------------------------------- ### norm_bad_segments with doctest examples Source: https://legrandnico.github.io/systole/_modules/systole/utils.html Defines the norm_bad_segments function to normalize bad segments into merged tuple intervals. Includes two doctest examples: converting a boolean array and merging overlapping tuples. ```python def norm_bad_segments(bad_segments) -> List[Tuple[int, int]]: """Normalize bad segments. Return a list of tuples and merge overlapping intervals. Parameters ---------- bad_segments : The time stamps of the bad segments. Can be a boolean 1d numpy array where `True` is a bad segment (will be rejected), or a list of tuples such as (start_idx, end_idx) in signal samples or milliseconds when intervals are provided. Returns ------- bad_segments_tuples : The merged bad segments as tuples such as (start_idx, end_idx) indicate when each bad segment starts and ends. Raises ------ ValueError : If `bad_segments` is not a tuple or a np.ndarray. Examples -------- Get a list of tuples such as `[(start_idx, end_idx)]` from a boolean vector that is of the same length that the associated signal and where `True` is a bad segment/sample. >>> import numpy as np >>> from systole.utils import norm_bad_segments >>> bool_bad_segments = np.zeros(100, dtype=bool) >>> bool_bad_segments[10:20] = True >>> bool_bad_segments[50:60] = True >>> new_segments = norm_bad_segments(bool_bad_segments) >>> new_segments `[(10, 20), (50, 60)]` From a list of tuples such as `[(start_idx, end_idx)]`, where some intervals are overlapping, get a clean version of these bad segments by merging the overlapping intervals. >>> bad_segments = [(100, 200), (150, 250)] >>> new_segments = norm_bad_segments(bad_segments) >>> assert new_segments `[(100, 250)]` """ if isinstance(bad_segments, list): # Create boolean representation t_max = np.array(bad_segments).max() boolean_segments = np.zeros(t_max, dtype=int) for bads in bad_segments: boolean_segments[bads[0] : bads[1]] = 1 elif isinstance(bad_segments, np.ndarray): boolean_segments = bad_segments.astype(int) else: raise ValueError( "bad_segments should be a list of tuples or a boolean 1d array." f" Got {type(bad_segments)}" ) ``` -------------------------------- ### Import libraries and configure environment Source: https://legrandnico.github.io/systole/notebooks/3-DetectingAndCorrectingArtefacts.html Imports necessary libraries for signal processing, plotting, and data handling, and configures the notebook environment. ```python import matplotlib.pyplot as plt import numpy as np import seaborn as sns from bokeh.io import output_notebook from bokeh.plotting import show from systole import import_dataset1 from systole.correction import correct_peaks, correct_rr from systole.detection import ecg_peaks from systole.plots import plot_evoked, plot_frequency, plot_raw, plot_rr, plot_subspaces from systole.utils import input_conversion output_notebook() sns.set_context("talk") ``` -------------------------------- ### Bokeh example for plot_frequency Source: https://legrandnico.github.io/systole/generated/plots/systole.plots.plot_frequency.html Visualizes HRV frequency domain from RR time series using Bokeh as plotting backend. The example imports RR data, sets up Bokeh notebook output, and calls plot_frequency with backend='bokeh'. ```python from systole import import_rr from systole.plots import plot_frequency from bokeh.io import output_notebook from bokeh.plotting import show output_notebook() show( plot_frequency(rr, input_type="rr_ms", backend="bokeh") ) ``` ```text BokehJS 3.7.2 successfully loaded. ``` -------------------------------- ### Initialize Oximeter instance Source: https://legrandnico.github.io/systole/_downloads/c526ecdb1c1d207cc7bfe76b0e1fd470/plot_HeartBeatrpeggios.ipynb Creates and sets up an Oximeter instance with the specified serial connection and sampling frequency. ```python oxi = Oximeter(serial=ser, sfreq=75, add_channels=4).setup() ``` -------------------------------- ### Import Oximeter and serialSim Source: https://legrandnico.github.io/systole/auto_examples/Recording/plot_RecordingPPG.html Imports the Oximeter class and the serialSim simulation function used in the example. ```python from systole.recording import Oximeter from systole import serialSim ``` -------------------------------- ### Initialize BrainVisionExG and read signal Source: https://legrandnico.github.io/systole/generated/recording/systole.recording.BrainVisionExG.html Create an instance of BrainVisionExG and use the read method to record signal data for a specified duration. ```python >>> from ecg.recording import BrainVisionExG >>> exg = BrainVisionExG(ip='xxx.xxx.xx', sfreq=1000).read(30) ``` -------------------------------- ### Nonin3231USB.setup Source: https://legrandnico.github.io/systole/_sources/generated/recording/systole.recording.Nonin3231USB.rst.txt Sets up the Nonin 3231 USB device for communication. ```APIDOC ## Nonin3231USB.setup ### Description Sets up the Nonin 3231 USB device for communication. ### Method Method ### Signature `setup(self)` ### Parameters None ### Returns None ``` -------------------------------- ### Author and Licence declaration Source: https://legrandnico.github.io/systole/_sources/auto_examples/Artefacts/plot_ArtefactsDetection.rst.txt Initial metadata for the script. ```Python # Author: Nicolas Legrand # Licence: GPL v3 ``` -------------------------------- ### Initialize Editor for raw signal inspection Source: https://legrandnico.github.io/systole/notebooks/6-WorkingWithBIDSFolders.html Imports necessary modules and sets up the interactive Matplotlib backend for the Editor. ```python from systole import import_dataset1 from systole.interact import Viewer, Editor from IPython.display import display %matplotlib ipympl ``` -------------------------------- ### Calculate RMSSD from R-R intervals Source: https://legrandnico.github.io/systole/generated/hrv/systole.hrv.rmssd.html Example showing the calculation of RMSSD using a list of R-R intervals in milliseconds. ```python >>> rr = [800, 850, 810, 720] >>> rmssd(rr) 63.77042156569664 ``` -------------------------------- ### Plot circular with numpy array Source: https://legrandnico.github.io/systole/_downloads/c286efda8a3a9cc58e84395211045501/plot_circular.ipynb Example of using a numpy array of angular values as input for the plot_circular function. ```python import numpy as np from systole.plots import plot_circular x = np.random.normal(np.pi, 0.5, 100) plot_circular(data=x) ``` -------------------------------- ### Simulating data acquisition Source: https://legrandnico.github.io/systole/_downloads/146b12539d7cede3f2dbe2d9515e86a0/plot_InstantaneousHeartRate.ipynb Initializes a simulated serial connection for demonstration purposes. ```python ser = serialSim() ``` -------------------------------- ### Interpolate RR intervals Source: https://legrandnico.github.io/systole/_modules/systole/correction.html Example of creating a time vector and interpolating RR intervals to remove artefacts at specified indices. ```python # Create time vector time = np.arange(0, len(rr)) # Correction of artefacts clean_rr = np.interp(time, np.delete(time, idx), np.delete(rr, idx)) return clean_rr ``` -------------------------------- ### Initialize Oximeter and record data Source: https://legrandnico.github.io/systole/_sources/auto_examples/Recording/plot_InstantaneousHeartRate.rst.txt Creates an Oximeter instance with the serial connection and sample rate of 75 Hz, then reads 20 seconds of data. The output shows the reset buffer message and the Oximeter object. ```Python # Create an Oxymeter instance, initialize recording and record for 30 seconds oxi = Oximeter(serial=ser, sfreq=75).setup() oxi.read(20) ``` ```none Reset input buffer ``` -------------------------------- ### Plot circular with pandas DataFrame Source: https://legrandnico.github.io/systole/_downloads/c286efda8a3a9cc58e84395211045501/plot_circular.ipynb Example of using a pandas DataFrame as input for the plot_circular function, including support for hue grouping. ```python import numpy as np import pandas as pd from systole.plots import plot_circular # Create angular values (two conditions) x = np.random.normal(np.pi, 0.5, 100) y = np.random.uniform(0, np.pi * 2, 100) data = pd.DataFrame(data={"x": x, "y": y}).melt() plot_circular(data=data, y="value", hue="variable") ``` -------------------------------- ### Using Bokeh as plotting backend Source: https://legrandnico.github.io/systole/_downloads/0671fb83e13faf570765c637c488b5b6/plot_raw.ipynb Sets up Bokeh for notebook output and displays an interactive plot of the full PPG signal with heart rate and artefact annotations. ```python output_notebook() show(plot_raw(ppg, backend="bokeh", show_heart_rate=True, show_artefacts=True)) ``` -------------------------------- ### Highlighting bad segments in PPG Source: https://legrandnico.github.io/systole/_modules/systole/plots/plot_raw.html Annotates specific segments of a recording as 'bad' using a list of (start, end) tuples in milliseconds. ```python from systole import import_ppg from systole.plots import plot_raw # Only use the first 60 seconds for demonstration # The bad segments are annotated using a tuple (start, end) in miliseconds plot_raw(ppg[ppg.time<60], sfreq=75, bad_segments=[(15000, 17000)]); ``` -------------------------------- ### Import nonlinear domain tools Source: https://legrandnico.github.io/systole/_sources/notebooks/4-HeartRateVariability.ipynb.txt Imports the necessary functions for nonlinear domain analysis. ```python from systole.plots import plot_poincare from systole.reports import nonlinear_table ``` -------------------------------- ### Nonin3231USB.__init__ Source: https://legrandnico.github.io/systole/_sources/generated/recording/systole.recording.Nonin3231USB.rst.txt Initializes the Nonin3231USB object. This is the constructor for the class. ```APIDOC ## Nonin3231USB.__init__ ### Description Initializes the Nonin3231USB object. ### Method Constructor ### Signature `__init__(self, ...)` ### Parameters No parameters are documented in the source. ### Returns None ``` -------------------------------- ### Normalize boolean bad segments Source: https://legrandnico.github.io/systole/generated/utils/systole.utils.norm_bad_segments.html Convert a boolean vector where True indicates a bad segment into a list of start and end index tuples. ```python >>> import numpy as np >>> from systole.utils import norm_bad_segments >>> bool_bad_segments = np.zeros(100, dtype=bool) >>> bool_bad_segments[10:20] = True >>> bool_bad_segments[50:60] = True >>> new_segments = norm_bad_segments(bool_bad_segments) >>> new_segments [(10, 20), (50, 60)] ``` -------------------------------- ### Load dataset Source: https://legrandnico.github.io/systole/generated/utils/systole.utils.to_epochs.html Initial step to load the dataset containing ECG and Stim modalities. ```python >>> ecg_df = import_dataset1(modalities=['ECG', 'Stim']) ``` -------------------------------- ### Create interactive widgets for BIDS data Source: https://legrandnico.github.io/systole/_modules/systole/interact/interact.html Initializes various ipywidgets for configuring BIDS folder paths, session, modality, signal type, and output directories. ```python self.bids_folder_ = widgets.Textarea( value=str(self.bids_folder), placeholder="Type something", description="BIDS folder:", disabled=False, layout=widgets.Layout(width="250px"), ) self.preprocessed_folder_ = widgets.Textarea( value=str(self.preprocessed_folder), placeholder="Type something", description="Preprocessed folder:", disabled=False, layout=widgets.Layout(width="250px"), ) self.session_ = widgets.Textarea( value=session, placeholder="Type something", description="Session:", disabled=False, layout=widgets.Layout(width="250px"), ) self.modality_ = widgets.Textarea( value=modality, placeholder="Type something", description="Modality:", disabled=False, layout=widgets.Layout(width="250px"), ) self.pattern_ = widgets.Textarea( value=pattern, placeholder="Type something", description="Pattern:", disabled=False, layout=widgets.Layout(width="250px"), ) self.signal_type_ = widgets.Dropdown( options=["PPG", "ECG", "RESP"], value=signal_type, description="Signal:", layout=widgets.Layout(width="250px"), ) self.output_folder_ = widgets.Textarea( value=output_folder, placeholder="Type something", description="Output:", disabled=False, layout=widgets.Layout(width="250px"), ) ``` -------------------------------- ### Identify and segment bad data segments Source: https://legrandnico.github.io/systole/_modules/systole/utils.html This code processes a boolean array to identify the start and end indices of bad segments, returning them as a list of tuples. ```python # Find the start and end of each bad segments bad_segments_list = [0] if boolean_segments[0] == 1 else [] bad_segments_list.extend( [ idx for idx in range(1, len(boolean_segments) - 1) if (boolean_segments[idx] == 1) & (boolean_segments[idx - 1] == 0) | (boolean_segments[idx] == 0) & (boolean_segments[idx - 1] == 1) ] ) if boolean_segments[-1] == 1: bad_segments_list.append(len(boolean_segments)) # Make it a list of tuples (start, end) bad_segments_tuples = [ (bad_segments_list[i], bad_segments_list[i + 1]) for i in range(0, len(bad_segments_list), 2) ] return bad_segments_tuples ``` -------------------------------- ### Editor.__init__() Source: https://legrandnico.github.io/systole/generated/interact/systole.interact.Editor.html Initializes the Editor class for visualization and manual edition of peaks vectors. ```APIDOC ## Editor.__init__() ### Description Initializes the Editor class for visualization and manual edition of peaks vectors. ### Method Constructor ### Endpoint Editor.__init__(signal, sfreq, signal_type, corrected_json='corrected.json', figsize=(15, 7), viewer=None, corrected_peaks=None, bad_segments=None) ### Parameters #### Parameters - **signal** (ndarray) - Required - The physiological signal. - **sfreq** (int) - Required - The sampling frequency of the physiological signal. - **signal_type** (str) - Required - The type of signal being analyzed. Can be "PPG", "ECG" or "RESP". Defaults to "PPG". - **corrected_json** (str | PathLike) - Optional - Path to the corrected JSON file. Defaults to 'corrected.json'. - **figsize** (Tuple[int, int]) - Optional - The size of the interactive Matplotlib figure for peaks edition. Defaults to (15, 7). - **viewer** (Viewer | None) - Optional - The viewer instance from which the editor is called. - **corrected_peaks** (ndarray | None) - Optional - The 1d array of corrected peaks indexes, in case the signal was previously edited. - **bad_segments** (list | None) - Optional - List of start_idx and end_idx annotating bad segments, in case the signal was previously edited. ### Returns None ``` -------------------------------- ### Display Viewer interface Source: https://legrandnico.github.io/systole/notebooks/6-WorkingWithBIDSFolders.html Displays the IO box, editor commands, and output for the Viewer instance. ```python display(view.io_box, view.editor.commands_box, view.output) ``` -------------------------------- ### Plot Poincaré with Bokeh backend Source: https://legrandnico.github.io/systole/_downloads/9b802a1913878e2b8f3cbc21be3e0b15/plot_pointcare.ipynb Uses Bokeh as the plotting backend, requiring output_notebook() and show() to display the plot in a notebook. The same RR data is used as in the Matplotlib example. ```python from bokeh.io import output_notebook from bokeh.plotting import show output_notebook() from systole import import_rr from systole.plots import plot_poincare show(plot_poincare(rr, input_type="rr_ms", backend="bokeh")) ``` -------------------------------- ### Initializing and Reading PPG Signal Source: https://legrandnico.github.io/systole/_downloads/517fdd79621ce3738da52dd7295d8666/plot_RecordingPPG.ipynb Create an Oximeter instance, initialize it, and read the signal for a specified duration. ```python oxi = Oximeter(serial=ser).setup().read(duration=10) ``` -------------------------------- ### Initialize stimulus arrays Source: https://legrandnico.github.io/systole/notebooks/5-InstantaneousHeartRate.html Creates zero-filled arrays for neutral and disgust conditions and populates them based on stimulus indices. ```python neutral, disgust = np.zeros(len(new_time)), np.zeros(len(new_time)) disgust[np.round(np.where(ecg_df.stim.to_numpy() == 2)[0]).astype(int)] = 1 neutral[np.round(np.where(ecg_df.stim.to_numpy() == 1)[0]).astype(int)] = 1 ``` -------------------------------- ### Display environment and package versions with watermark Source: https://legrandnico.github.io/systole/notebooks/5-InstantaneousHeartRate.html Loads the watermark extension and prints environment details (Python version, package versions, and last update). The output shows the exact versions of systole, numba, numpy, and other dependencies. ```python %load_ext watermark %watermark -n -u -v -iv -w -p systole,numba ``` ```text Last updated: Mon Apr 07 2025 Python implementation: CPython Python version : 3.12.9 IPython version : 9.0.2 systole: 0.3.1 numba : 0.61.0 numpy : 2.1.3 sys : 3.12.9 (main, Feb 5 2025, 03:22:31) [GCC 13.3.0] bokeh : 3.7.2 seaborn : 0.13.2 matplotlib: 3.10.1 systole : 0.3.1 Watermark: 2.5.0 ``` -------------------------------- ### Oximeter Source: https://legrandnico.github.io/systole/_modules/systole/recording.html Documentation for the Oximeter class, which records PPG signals from a Nonin pulse oximeter via a serial connection. It includes constructor parameters, attributes, and methods for setup, reading, and plotting. ```APIDOC ## Oximeter ### Description Class for recording PPG signal with Nonin pulse oximeter. ### Constructor `Oximeter(serial, sfreq=75, add_channels=None, data_format="2")` ### Parameters - **serial** (serial object) - Required - The `serial` instance interfacing with the USB port. - **sfreq** (int) - Optional - The sampling frequency of the recording. Default is 75 Hz. - **add_channels** (int or None) - Optional - If int, will create as many additional channels. If None, no additional channels created. - **data_format** (str) - Optional - Data format returned by the USB dongle ("2" or "7"). See https://www.nonin.com/wp-content/uploads/6000-7000-CP-7602-000-11_ENG.pdf for details. The pulse waveform value is automatically normalized and range between 0 and 255 both for data format "2" and "7". ### Attributes - **instant_rr** : Time serie of instantaneous heartrate. - **n_channels** (int or None) : Number of additional channels. - **recording** : Time serie of PPG signal. - **sfreq** : Sampling frequency. Default value is 75 Hz. - **threshold** : The threshold used to detect beat peaks. Will use the average + standard deviation. - **times** : Time vector (in seconds). - **diff** : Records the differential of the PPG signal. Used to detect heartbeat peaks. - **peaks** : List of 0 and 1. 1 index detected peaks. - **channels** : Additional channels to record. Will continuously record *n_channels* additional channels in parallel of `recording` with default `0` as default value. - **serial** : PySerial object indexing the USB port to read. - **rr** : RR intervals time course. The time course will be generated if `self.find_peaks` is used. ### Methods - `setup()` - Initialize the recording. This will find the start byte to ensure that all the forthcoming data is in Synch. You should not wait more than ~10s between the setup and the recording, otherwise the buffer will start to overwrite the data. - `read(duration)` - Will continuously record for certain amount of time (specified by the *duration* parameter, in seconds). This is the easiest and most robust method, but it is not possible to run instructions in the meantime. - `readInWaiting()` - Will read all the available bytes (up to 10 seconds of recording). When inserted into a while loop, it allows to record PPG signal together with other scripts. - `plot()` - Inspect the recorded signal. - `save()` - Store recording as .npy file. ### Example ```python import serial ser = serial.Serial('COM4') from systole.recording import Oximeter oximeter = Oximeter(serial=ser, sfreq=75) oximeter.setup() oximeter.read(duration=10) oximeter.plot() ``` ### Warning Data read from the serial port are appended to list and processed for pulse detection and instantaneous heart rate estimation. The time required to append new data to the recording will increase as its size increase. You should beware that this processing time does not exceed the sampling frequency (i.e. 75Hz or 0.013 seconds per sample for Nonin pulse oximeters) to allow continuous recording and fast processing of in waiting samples. We recommend storing regularly 5 minutes recording as .npy file using the `save()` function. ``` -------------------------------- ### Create Oximeter instance Source: https://legrandnico.github.io/systole/_modules/systole/recording.html Create an Oximeter instance using the previously defined serial connection. ```python from systole.recording import Oximeter oximeter = Oximeter(serial=ser, sfreq=75) ``` -------------------------------- ### Viewer Source: https://legrandnico.github.io/systole/api.html Handles the interaction with BIDS structured folders. ```APIDOC ## Viewer ### Description Handles the interaction with BIDS structured folders. ### Parameters - `bids_folder` (optional) - Path to the BIDS folder. - `preprocessed_folder` (optional) - Path to the preprocessed folder. - Additional arguments as needed. ``` -------------------------------- ### Open serial port (simulated or USB) Source: https://legrandnico.github.io/systole/auto_examples/Recording/plot_RecordingPPG.html Creates a simulated serial port for demonstration. To use a real Nonin pulse oximeter, uncomment the import and provide the correct COM port. ```python ser = serialSim() # Simulate a device # Use a USB device # import serial # ser = serial.Serial("COM4") # Use this line for USB recording ``` -------------------------------- ### Configure widget observers and layout Source: https://legrandnico.github.io/systole/_modules/systole/interact/interact.html Sets up observers to trigger updates or plotting functions when widget values change and organizes the widgets into a layout. ```python # Keep updated if dropdown menus are used self.bids_folder_.observe(self.update_list, names="value") self.preprocessed_folder_.observe(self.update_list, names="value") self.session_.observe(self.update_list, names="value") self.modality_.observe(self.update_list, names="value") self.pattern_.observe(self.update_list, names="value") self.signal_type_.observe(self.plot_signal, names="value") self.participants_.observe(self.plot_signal, names="value") # Show the navigator and main plot self.io_box = widgets.VBox( [ widgets.HBox( [ self.bids_folder_, self.preprocessed_folder_, self.session_, self.participants_, self.modality_, ] ), widgets.HBox([self.pattern_, self.signal_type_, self.output_folder_]), ] ) self.output = widgets.Output() # Plot the first pysio file if any self.plot_signal(change=None) ``` -------------------------------- ### Detect R peaks with ecg_peaks() using hamilton method Source: https://legrandnico.github.io/systole/generated/detection/systole.detection.ecg_peaks.html Example usage of ecg_peaks() with the 'hamilton' method, resampling from 2000 Hz, and local peak search. The output shows that 24 peaks were detected. ```python >>> from systole import import_dataset >>> from systole.detection import ecg_peaks >>> signal_df = import_dataset()[:20*2000] >>> signal, peaks = ecg_peaks(signal_df.ecg.to_numpy(), method='hamilton', >>> sfreq=2000, find_local=True) >>> print(f'{sum(peaks)} peaks detected.') 24 peaks detected. ``` -------------------------------- ### Set up real serial connection for Oximeter Source: https://legrandnico.github.io/systole/auto_examples/Recording/plot_HeartBeatrpeggios.html Uncomment and adjust the COM port to enable real online data acquisition from a pulse oximeter. ```python import serial ser = serial.Serial('COM4') # Change this value according to your setup ``` -------------------------------- ### Importing Dataset Source: https://legrandnico.github.io/systole/_downloads/fc0cb4e604f0bf542fc9ccc4d677418b/plot_PeaksCorrection.ipynb Load the ECG dataset. ```python ecg_df = import_dataset1(modalities=["ECG", "Stim"]) ```