From 8ddecc77d7d6dbdb76208ff6977b86adf5041c50 Mon Sep 17 00:00:00 2001 From: Nicolas Legrand Date: Mon, 25 Sep 2023 16:09:08 +0200 Subject: [PATCH] fix panels in the documentation (#66) * drop sphinx-panel and use sphinx-design instead * remove the changelogs in the documentation, this will only be on GitHub from now --- docs/source/_static/style.css | 243 ------------------ docs/source/changelog.rst | 45 ---- docs/source/conf.py | 20 +- docs/source/index.rst | 91 ++++--- .../notebooks/1-PhysiologicalSignals.ipynb | 8 +- .../notebooks/1-PhysiologicalSignals.md | 8 +- docs/source/notebooks/2-DetectingCycles.ipynb | 20 +- docs/source/notebooks/2-DetectingCycles.md | 20 +- docs/source/releases/v0.0.1.txt | 5 - docs/source/releases/v0.1.0.txt | 52 ---- docs/source/releases/v0.1.1.txt | 21 -- docs/source/releases/v0.1.2.txt | 14 - docs/source/releases/v0.1.3.txt | 15 -- docs/source/releases/v0.2.0.txt | 65 ----- docs/source/releases/v0.2.1.txt | 19 -- docs/source/releases/v0.2.2.txt | 24 -- docs/source/releases/v0.2.3.txt | 32 --- docs/source/releases/v0.2.4.txt | 8 - docs/source/releases/v0.2.5.txt | 8 - systole/detectors/msptd.py | 4 +- tests/test_detection.py | 17 +- 21 files changed, 93 insertions(+), 646 deletions(-) delete mode 100644 docs/source/_static/style.css delete mode 100644 docs/source/changelog.rst delete mode 100644 docs/source/releases/v0.0.1.txt delete mode 100644 docs/source/releases/v0.1.0.txt delete mode 100644 docs/source/releases/v0.1.1.txt delete mode 100644 docs/source/releases/v0.1.2.txt delete mode 100644 docs/source/releases/v0.1.3.txt delete mode 100644 docs/source/releases/v0.2.0.txt delete mode 100644 docs/source/releases/v0.2.1.txt delete mode 100644 docs/source/releases/v0.2.2.txt delete mode 100644 docs/source/releases/v0.2.3.txt delete mode 100644 docs/source/releases/v0.2.4.txt delete mode 100644 docs/source/releases/v0.2.5.txt diff --git a/docs/source/_static/style.css b/docs/source/_static/style.css deleted file mode 100644 index d0df5d4e..00000000 --- a/docs/source/_static/style.css +++ /dev/null @@ -1,243 +0,0 @@ -body { - font-family: 'Open Sans', sans-serif; - color: #444444 !important; - max-width: 100% !important; -} - -h1 { font-size: 40px !important; } -h2 { font-size: 32px !important; } -h3 { font-size: 24px !important; } -h4 { font-size: 18px !important; } -h5 { font-size: 14px !important; } -h6 { font-size: 10px !important; } - -footer a{ - - color: #4c72b0 !important; -} -a.reference { - color: #4c72b0 !important; -} - -blockquote p { - font-size: 14px !important; -} - -blockquote { - padding-top: 4px !important; - padding-bottom: 4px !important; - margin: 0 0 0px !important; -} - -pre { - margin-top: 11.5px !important; - background-color: #f6f6f9 !important; -} - -code { - color: #49759c !important; - background-color: transparent !important; -} - -code.descclassname { - padding-right: 0px !important; -} - -code.descname { - padding-left: 0px !important; -} - -dt:target, span.highlighted { - background-color: #ffffff !important; -} - -ul { - padding-left: 20px !important; -} - -ul.dropdown-menu { - padding-left: 0px !important; -} - -.alert-info { - background-color: #bbd2ea !important; - border-color: #bbd2ea !important; - color: #2c3e50 !important; -} - -.alert-warning { - background-color: #e09572 !important; - border-color: #e09572 !important; - color: #222222 !important; -} - -img { - margin-bottom: 10px !important; -} - -/* From https://github.com/twbs/bootstrap/issues/1768 */ -*[id]:before { - display: block; - content: " "; - margin-top: -60px; - height: 60px; - visibility: hidden; -} - -.dataframe table { - /*Uncomment to center tables horizontally*/ - /* margin-left: auto; */ - /* margin-right: auto; */ - border: none; - border-collapse: collapse; - border-spacing: 0; - font-size: 12px; - table-layout: fixed; -} - -.dataframe thead { - border-bottom: 1px solid; - vertical-align: bottom; -} - -.dataframe tr, th, td { - text-align: left; - vertical-align: middle; - padding: 0.5em 0.5em; - line-height: normal; - white-space: normal; - max-width: none; - border: none; -} - -.dataframe th { - font-weight: bold; -} - -table { - margin-bottom: 20px; -} - -tbody tr:nth-child(odd) { - background: #f5f5f5; -} - -tbody tr:hover { - background: rgba(66, 165, 245, 0.2); -} - -.label, -.badge { - display: inline-block; - padding: 2px 4px; - font-size: 11.844px; - /* font-weight: bold; */ - line-height: 13px; - color: #ffffff; - vertical-align: baseline; - white-space: nowrap; - /* text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); */ - background-color: #999999; -} -.badge { - padding-left: 9px; - padding-right: 9px; - -webkit-border-radius: 9px; - -moz-border-radius: 9px; - border-radius: 9px; - opacity: 70%; -} -.badge-api { - background-color: #c44e52; -} -.badge-defaults { - background-color: #dd8452; -} -.badge-docs { - background-color: #8172b3; -} -.badge-feature { - background-color: #55a868; -} -.badge-enhancement { - background-color: #4c72b0; -} -.badge-fix { - background-color: #ccb974; -} - -.navbar-brand { - padding-top: 16px; - padding-bottom: 16px; -} - -/* Override some aspects of the pydata-sphinx-theme: taken from Pandas */ - -:root { - /* Use softer blue from bootstrap's default info color */ - --pst-color-info: 23, 162, 184; - --pst-color-active-navigation: 1, 50, 67; - } - - /* Main index page overview cards */ - - .intro-card { - background: #fff; - border-radius: 0; - padding: 30px 10px 20px 10px; - margin: 10px 0px; - } - - .intro-card p.card-text { - margin: 0px; - } - - .intro-card .card-img-top { - height: 52px; - width: 52px; - margin-left: auto; - margin-right: auto; - } - - .intro-card .card-header { - border: none; - background-color:white; - color: #150458 !important; - font-size: var(--pst-font-size-h5); - font-weight: bold; - padding: 2.5rem 0rem 0.5rem 0rem; - } - - .intro-card .card-footer { - border: none; - background-color:white; - } - - .intro-card .card-footer p.card-text{ - max-width: 220px; - margin-left: auto; - margin-right: auto; - } - - .custom-button { - background-color:#DCDCDC; - border: none; - color: #fff; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 0.9rem; - border-radius: 0.5rem; - max-width: 120px; - padding: 0.5rem 0rem; - } - - .custom-button a { - color: #fff; - } - - .custom-button p { - margin-top: 0; - margin-bottom: 0rem; - color: #fff; - } \ No newline at end of file diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst deleted file mode 100644 index dc3f59cd..00000000 --- a/docs/source/changelog.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _whatsnew: - -.. currentmodule:: systole - -.. role:: raw-html(raw) - :format: html - -.. role:: raw-latex(raw) - :format: latex - -.. |API| replace:: :raw-html:`API` :raw-latex:`{\small\sc [API]}` -.. |Defaults| replace:: :raw-html:`Defaults` :raw-latex:`{\small\sc [Defaults]}` -.. |Docs| replace:: :raw-html:`Docs` :raw-latex:`{\small\sc [Docs]}` -.. |Feature| replace:: :raw-html:`Feature` :raw-latex:`{\small\sc [Feature]}` -.. |Enhancement| replace:: :raw-html:`Enhancement` :raw-latex:`{\small\sc [Enhancement]}` -.. |Fix| replace:: :raw-html:`Fix` :raw-latex:`{\small\sc [Fix]}` - -What's new in each version -========================== - -This page contains information about what has changed in each new version of ``systole``. - -.. raw:: html - -
- -.. include:: releases/v0.2.4.txt - -.. include:: releases/v0.2.3.txt - -.. include:: releases/v0.2.2.txt - -.. include:: releases/v0.2.1.txt - -.. include:: releases/v0.2.0.txt - -.. include:: releases/v0.1.3.txt - -.. include:: releases/v0.1.2.txt - -.. include:: releases/v0.1.1.txt - -.. include:: releases/v0.1.0.txt - -.. include:: releases/v0.0.1.txt diff --git a/docs/source/conf.py b/docs/source/conf.py index 4880dbb7..8e08ccc8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -79,9 +79,6 @@ # The master toctree document. master_doc = "index" -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. @@ -99,17 +96,17 @@ dict( name="GitHub", url="https://github.com/embodied-computation-group/systole", - icon="fab fa-github-square", + icon="fa-brands fa-square-github", ), dict( name="Twitter", url="https://twitter.com/visceral_mind", - icon="fab fa-twitter-square", + icon="fa-brands fa-square-twitter", ), dict( name="Pypi", url="https://pypi.org/project/systole/", - icon="fas fa-box", + icon="fa-solid fa-box", ), ], "logo": { @@ -118,18 +115,11 @@ html_sidebars = {"**": []} -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] -html_css_files = ["https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"] +# -- Options for HTML output ------------------------------------------------- + html_logo = "images/logo_small.svg" html_favicon = "images/logo_small.svg" -def setup(app): - app.add_css_file('style.css') - - # -- Intersphinx ------------------------------------------------ intersphinx_mapping = { diff --git a/docs/source/index.rst b/docs/source/index.rst index c38c1e24..99496933 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -66,56 +66,70 @@ If you are using **Systole** in a publication we ask you to cite the following p Systole documentation ===================== -.. grid:: 4 - .. grid-item-card:: Getting started +.. grid:: 2 - :img-top: images/forward-fast-solid.png + .. grid-item-card:: Getting started - New to *Systole*? Check out the getting started guides. They contain an - introduction to *Systole'* main concepts and links to additional tutorials. - +++ - .. link-button:: getting_started - :type: ref - :text: To the getting started guides - :classes: btn btn-light btn-block stretched-link + .. image:: images/forward-fast-solid.png + :width: 200 + :align: center - .. grid-item-card:: Example gallery + New to *Systole*? Check out the getting started guides. They contain an + introduction to *Systole'* main concepts and links to additional tutorials. + +++ - :img-top: images/table-cells-large-solid.png + .. button-ref:: getting_started + :expand: - See this section for examples of using Systole in different ways. - +++ - .. link-button:: auto_examples/index - :type: ref - :text: To the example gallery - :classes: btn btn-light btn-block stretched-link + Getting started - .. grid-item-card:: Tutorials + .. grid-item-card:: Example gallery - :img-top: images/tutorials.png + .. image:: images/table-cells-large-solid.png + :width: 200 + :align: center - New to cardiac signal analysis? Want to see how you can use *Systole* when dealing - with real-world problems? Check out the tutorial notebooks for an introduction to - theoretical and practical aspects of physiological signal analysis for cognitive - neuroscience. - +++ - .. link-button:: tutorials - :type: ref - :text: To the tutorial notebooks - :classes: btn btn-light btn-block stretched-link + See this section for examples of using Systole in different ways. + +++ + .. button-ref:: auto_examples/index + :expand: - .. grid-item-card:: API reference - :img-top: images/code-solid.png + Learning with examples - The reference guide contains a detailed description of the Systole API. The - reference describes how the methods work and which parameters can be used. +.. grid:: 2 - +++ + .. grid-item-card:: Tutorials - .. link-button:: api - :type: ref - :text: To the reference guide - :classes: btn btn-light btn-block stretched-link + .. image:: images/tutorials.png + :width: 200 + :align: center + + New to cardiac signal analysis? Want to see how you can use *Systole* when dealing + with real-world problems? Check out the tutorial notebooks for an introduction to + theoretical and practical aspects of physiological signal analysis for cognitive + neuroscience. + +++ + .. button-ref:: tutorials + :expand: + + Interactive tutorial on physiological signal analysis + + + .. grid-item-card:: API reference + + .. image:: images/code-solid.png + :width: 200 + :align: center + + The reference guide contains a detailed description of the Systole API. The + reference describes how the methods work and which parameters can be used. + + +++ + + .. button-ref:: api + :expand: + + API references Acknowledgements @@ -176,5 +190,4 @@ Contributors Gallery Tutorials API - Release notes References \ No newline at end of file diff --git a/docs/source/notebooks/1-PhysiologicalSignals.ipynb b/docs/source/notebooks/1-PhysiologicalSignals.ipynb index 81403d3f..d76ed1f3 100644 --- a/docs/source/notebooks/1-PhysiologicalSignals.ipynb +++ b/docs/source/notebooks/1-PhysiologicalSignals.ipynb @@ -399,7 +399,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To illustrate the analysis of ECG signals, we will use a dataset containing ECG and respiratory recording from a healthy participant. These data were acquired concurrently with the paradigm described in {cite:p}`20202:legrand`, where subjects were asked to memorize and later inhibit/suppress images and ECG was recorded using a standard 3-point electrode montage. You can load this data set with Systole directly using `systole.import_dataset1()`. As this dataset contains many modalities (ECG, respiration, EDA), you can decide to download only some of them to save time. This is what we are doing with the `modalities` argument." + "To illustrate the analysis of ECG signals, we will use a dataset containing ECG and respiratory recordings from a healthy participant. These data were acquired concurrently with the paradigm described in {cite:p}`2020:legrand`, where subjects were asked to memorize and later inhibit/suppress images and ECG was recorded using a standard 3-point electrode montage. You can load this data set with Systole directly using `systole.import_dataset1()`. As this dataset contains many modalities (ECG, respiration, EDA), you can decide to download only some of them to save time. This is what we are doing with the `modalities` argument." ] }, { @@ -639,10 +639,10 @@ "source": [ "```{admonition} A note on data format\n", ":class: note\n", - "[Systole](https://embodied-computation-group.github.io/systole/#) will output a boolean peaks vector after detection by default (i.e. a vector that has the same length than the original signal with `True` values indicating where the peaks were detected). We found this approach more intuitive as the time series keeps the same size and it can easier to slice it according to events observed in another signal (e.g the respiration or the stim channel). However, the same information can also be encoded as peaks indexes (`peaks_idx`), referring to the sample number where the R peaks were detected, or directly as RR-intervals expressed in seconds or milliseconds (`rr_s` and `rr_ms` respectively). Other packages will use different formats as default for encoding and you might have to convert your inputs. This can be done easily using the [input_conversion function](#systole.utils.input_conversion), which will let you convert your data accordingly.\n", + "[Systole](https://embodied-computation-group.github.io/systole/#) will output a boolean peaks vector after detection by default (i.e. a vector that has the same length as the original signal with `True` values indicating where the peaks were detected). We found this approach more intuitive as the time series keeps the same size and it can be easier to slice it according to events observed in another signal (e.g. the respiration or the stim channel). However, the same information can also be encoded as peaks indexes (`peaks_idx`), referring to the sample number where the R peaks were detected, or directly as RR-intervals expressed in seconds or milliseconds (`rr_s` and `rr_ms` respectively). Other packages will use different formats as default for encoding and you might have to convert your inputs. This can be done quickly using {py:func}`systole.utils.input_conversion`, which will let you convert your data accordingly.\n", "```\n", "\n", - "We illustrate some example of input conversion below:" + "We illustrate some examples of input conversion below:" ] }, { @@ -1027,7 +1027,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.9.16" }, "vscode": { "interpreter": { diff --git a/docs/source/notebooks/1-PhysiologicalSignals.md b/docs/source/notebooks/1-PhysiologicalSignals.md index 3f9255ce..708b1c23 100644 --- a/docs/source/notebooks/1-PhysiologicalSignals.md +++ b/docs/source/notebooks/1-PhysiologicalSignals.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.15.1 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -63,7 +63,7 @@ This tutorial notebook introduces two commonly used methods to record cardiac ac +++ -To illustrate the analysis of ECG signals, we will use a dataset containing ECG and respiratory recording from a healthy participant. These data were acquired concurrently with the paradigm described in {cite:p}`20202:legrand`, where subjects were asked to memorize and later inhibit/suppress images and ECG was recorded using a standard 3-point electrode montage. You can load this data set with Systole directly using `systole.import_dataset1()`. As this dataset contains many modalities (ECG, respiration, EDA), you can decide to download only some of them to save time. This is what we are doing with the `modalities` argument. +To illustrate the analysis of ECG signals, we will use a dataset containing ECG and respiratory recordings from a healthy participant. These data were acquired concurrently with the paradigm described in {cite:p}`2020:legrand`, where subjects were asked to memorize and later inhibit/suppress images and ECG was recorded using a standard 3-point electrode montage. You can load this data set with Systole directly using `systole.import_dataset1()`. As this dataset contains many modalities (ECG, respiration, EDA), you can decide to download only some of them to save time. This is what we are doing with the `modalities` argument. ```{code-cell} ipython3 :tags: [hide-output] @@ -135,10 +135,10 @@ sns.despine() ```{admonition} A note on data format :class: note -[Systole](https://embodied-computation-group.github.io/systole/#) will output a boolean peaks vector after detection by default (i.e. a vector that has the same length than the original signal with `True` values indicating where the peaks were detected). We found this approach more intuitive as the time series keeps the same size and it can easier to slice it according to events observed in another signal (e.g the respiration or the stim channel). However, the same information can also be encoded as peaks indexes (`peaks_idx`), referring to the sample number where the R peaks were detected, or directly as RR-intervals expressed in seconds or milliseconds (`rr_s` and `rr_ms` respectively). Other packages will use different formats as default for encoding and you might have to convert your inputs. This can be done easily using the [input_conversion function](#systole.utils.input_conversion), which will let you convert your data accordingly. +[Systole](https://embodied-computation-group.github.io/systole/#) will output a boolean peaks vector after detection by default (i.e. a vector that has the same length as the original signal with `True` values indicating where the peaks were detected). We found this approach more intuitive as the time series keeps the same size and it can be easier to slice it according to events observed in another signal (e.g. the respiration or the stim channel). However, the same information can also be encoded as peaks indexes (`peaks_idx`), referring to the sample number where the R peaks were detected, or directly as RR-intervals expressed in seconds or milliseconds (`rr_s` and `rr_ms` respectively). Other packages will use different formats as default for encoding and you might have to convert your inputs. This can be done quickly using {py:func}`systole.utils.input_conversion`, which will let you convert your data accordingly. ``` -We illustrate some example of input conversion below: +We illustrate some examples of input conversion below: ```{code-cell} ipython3 # From boolean peaks vector to peaks indexs diff --git a/docs/source/notebooks/2-DetectingCycles.ipynb b/docs/source/notebooks/2-DetectingCycles.ipynb index e0fb0745..dfad4951 100644 --- a/docs/source/notebooks/2-DetectingCycles.ipynb +++ b/docs/source/notebooks/2-DetectingCycles.ipynb @@ -363,9 +363,9 @@ "id": "c4591c61", "metadata": {}, "source": [ - "This notebook focuses on the characterisation of cardiac cycles from the physiological signals we previously described (PPG and ECG). Here we only briefly mention different QRS-detection algorithms as related to the estimation of heart rate frequency, which is a commonly used measure in psychology and cognitive science. However, both the ECG and the PPG signals contain rich information beyond the beat to beat frequency that is relevant for cognitive and physiological modelling. All these approaches are not fully covered by [Systole](https://embodied-computation-group.github.io/systole/#), but are implemented in other software (e.g see {cite:p}`2021:makowski` for ECG components delineation).\n", + "This notebook focuses on the characterisation of cardiac cycles from the physiological signals we previously described (PPG and ECG). Here we only briefly mention different QRS-detection algorithms as related to the estimation of heart rate frequency, which is a commonly used measure in psychology and cognitive science. However, both the ECG and the PPG signals contain rich information beyond the beat-to-beat frequency that is relevant for cognitive and physiological modelling. All these approaches are not fully covered by [Systole](https://embodied-computation-group.github.io/systole/#), but are implemented in other software (e.g. see {cite:p}`2021:makowski` for ECG components delineation).\n", "\n", - "In this notebook, we are going to review the peak detection algorithm, which future tutorials covering, for example, heart-rate variability will build upon. Here our intention is to provide a better intuition of what is happening in our peak detection algorithms, the corrections that can be applied, and the possible bias and artefacts that can emerge. However, you do not need a perfect understanding of all these steps if you want to apply peak detection to youre data, and you might also consider skipping this part to proceed directly to the next tutorial focusing on artefact detection and correction." + "In this notebook, we are going to review the peak detection algorithm, which future tutorials covering, for example, heart-rate variability will build upon. Here our intention is to provide a better intuition of what is happening in our peak detection algorithms, the corrections that can be applied, and the possible bias and artefacts that can emerge. However, you do not need a perfect understanding of all these steps if you want to apply peak detection to your data, and you might also consider skipping this part to proceed directly to the next tutorial focusing on artefact detection and correction." ] }, { @@ -381,7 +381,7 @@ "id": "3f96f57f", "metadata": {}, "source": [ - "Because we will ultimately be interested in heart rate and its variability, our first goal will be to detect the R peaks. A large variety of algorithms have been proposed to extract the timing of R waves while controlling for signal noise and physiological variability. Reviewing and comparing all of the available methods is beyond the scope of this tutorial. Here, we are going to restrict our focus to some of the most popular methods, which also are available in the Python open-source ecosystem. We will use the [ecg_peaks](#systole.detection.ecg_peaks) function to perform R peak detection. This function can call a variety of peaks detection algorithms among the following: \n", + "Because we will ultimately be interested in heart rate and its variability, our first goal will be to detect the R peaks. A large variety of algorithms have been proposed to extract the timing of R waves while controlling for signal noise and physiological variability. Reviewing and comparing all of the available methods is beyond the scope of this tutorial. Here, we are going to restrict our focus to some of the most popular methods, which also are available in the Python open-source ecosystem. We will use the {py:func}`systole.detection.ecg_peaks` function to perform R peak detection. This function can call a variety of peak detection algorithms among the following: \n", "\n", "- `sleepecg` (default)\n", "- `hamilton`\n", @@ -401,7 +401,7 @@ "id": "75144002", "metadata": {}, "source": [ - "Let's first load an ECG recording. Here, we will select a 5 minute interval and compare the performances of the different algorithms supported by Systole." + "Let's first load an ECG recording. Here, we will select a 5-minute interval and compare the performances of the different algorithms supported by Systole." ] }, { @@ -443,7 +443,7 @@ "\n", "The [Pan-Tompkins algorithm](https://en.wikipedia.org/wiki/Pan%E2%80%93Tompkins_algorithm) was introduced in 1985. It uses band-pass filtering and derivation to identify the QRS complex and apply an adaptive threshold to detect the R peaks on the filtered signal.\n", "\n", - "This is a very popular - maybe to most popular - method for R peaks detection. One of the advantages is that it can easily be applied for online detection (see [this implementation](https://github.com/c-labpl/qrs_detector) for example). As we can see in the timing report, this algorithm is also the fastest among those available in the Systole toolbox, making ideal for applications such as online peak detection and biofeedback. You can also see that the algorithm's peak detection is highly accurate as we do not see any artifacts in the estimated R-R time series. One notable exception is the first peak which has been estimated incorrectly. This is due to the filtering steps in the Pan-Tompkins algorithms, and can be corrected by using a slightly longer signal than the range of interest." + "This is a very popular - maybe the most popular - method for R peak detection. One of the advantages is that it can easily be applied for online detection (see [this implementation](https://github.com/c-labpl/qrs_detector) for example). As we can see in the timing report, this algorithm is also the fastest among those available in the Systole toolbox, making it ideal for applications such as online peak detection and biofeedback. You can also see that the algorithm's peak detection is highly accurate as we do not see any artifacts in the estimated R-R time series. One notable exception is the first peak which has been estimated incorrectly. This is due to the filtering steps in the Pan-Tompkins algorithms and can be corrected by using a slightly longer signal than the range of interest." ] }, { @@ -894,7 +894,7 @@ "id": "29444c5f", "metadata": {}, "source": [ - "Let's first simulate some PPG recording. Here, we will use the `serialSim` class from Systole in conjunction with the `Oximeter` recording class. This will *simulate* the presence of a pulse oximeter on the computer and provide the information from a pre-recorded signal in real time. Simulating synthetic cardiac data in this way can be a great way to test your data analysis pipelines before collecting data - avoiding painful mistakes that can occur!" + "Let's first simulate some PPG recording. Here, we will use the `serialSim` class from Systole in conjunction with the `Oximeter` recording class. This will *simulate* the presence of a pulse oximeter on the computer and provide the information from a pre-recorded signal in real-time. Simulating synthetic cardiac data in this way can be a great way to test your data analysis pipelines before collecting data - avoiding painful mistakes that can occur!" ] }, { @@ -1005,11 +1005,11 @@ "id": "a5b02f0c", "metadata": {}, "source": [ - "A simple online approach like the one we described is usually good enough to detect all the systolic peaks, provided that the subject is not moving too much. The package comes with two algoritms for systolic peaks detection presented below and that can be controlled through the `method` parameter of the [ppg_peaks](#systole.detection.ppg_peaks).\n", + "A simple online approach like the one we described is usually good enough to detect all the systolic peaks, provided that the subject is not moving too much. The package comes with two algorithms for systolic peaks detection presented below and that can be controlled through the `method` parameter of the {py:func}`systole.detection.ppg_peaks`.\n", "\n", "#### Rolling mean\n", "\n", - "The current default is an adaptation of the rolling mean method proposed by {cite:p}`2019:vanGent`. This method has the advantage of being fast, simple and to perform well when the signal to noise ration is good." + "The current default is an adaptation of the rolling mean method proposed by {cite:p}`2019:vanGent`. This method has the advantage of being fast, simple and performing well when the signal-to-noise ratio is good." ] }, { @@ -1155,7 +1155,7 @@ "id": "5cdac5ff", "metadata": {}, "source": [ - "Clipping is a form of distortion that can limit signal when it exceeds a certain threshold [see the Wikipedia page](https://en.wikipedia.org/wiki/Clipping_(signal_processing)). Some PPG devices can produce clipping artefacts when recording the PPG signal, for example if a participant is pressing too hard on the fingerclip. Here, we can see that some clipping artefacts are found between 100 and 150 seconds in the previous recording. The threshold values (here `255`), is often set by the device and can easily be found depending on the manufacturer. These artefacts should be corrected before systolic peaks detection. One way to correct these artefacts is to remove the portion of the signal where clipping artefacts occurs and use cubic spline interpolation to reconstruct a plausible estimate of the *real* underlying signal. This is what the function `interpolate_clipping()` does {cite:p}`2019:vanGent`." + "Clipping is a form of distortion that can limit signal when it exceeds a certain threshold [see the Wikipedia page](https://en.wikipedia.org/wiki/Clipping_(signal_processing)). Some PPG devices can produce clipping artefacts when recording the PPG signal, for example, if a participant is pressing too hard on the finger clip. Here, we can see that some clipping artefacts are found between 100 and 150 seconds in the previous recording. The threshold value (here `255`), is often set by the device and can easily be found depending on the manufacturer. These artefacts should be corrected before systolic peak detection. One way to correct these artefacts is to remove the portion of the signal where clipping artefacts occur and use cubic spline interpolation to reconstruct a plausible estimate of the *real* underlying signal. This is what the function `interpolate_clipping()` does {cite:p}`2019:vanGent`." ] }, { @@ -1237,7 +1237,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.9.16" }, "vscode": { "interpreter": { diff --git a/docs/source/notebooks/2-DetectingCycles.md b/docs/source/notebooks/2-DetectingCycles.md index 373d627e..dfb9dc1e 100644 --- a/docs/source/notebooks/2-DetectingCycles.md +++ b/docs/source/notebooks/2-DetectingCycles.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.15.1 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -42,9 +42,9 @@ output_notebook() sns.set_context('talk') ``` -This notebook focuses on the characterisation of cardiac cycles from the physiological signals we previously described (PPG and ECG). Here we only briefly mention different QRS-detection algorithms as related to the estimation of heart rate frequency, which is a commonly used measure in psychology and cognitive science. However, both the ECG and the PPG signals contain rich information beyond the beat to beat frequency that is relevant for cognitive and physiological modelling. All these approaches are not fully covered by [Systole](https://embodied-computation-group.github.io/systole/#), but are implemented in other software (e.g see {cite:p}`2021:makowski` for ECG components delineation). +This notebook focuses on the characterisation of cardiac cycles from the physiological signals we previously described (PPG and ECG). Here we only briefly mention different QRS-detection algorithms as related to the estimation of heart rate frequency, which is a commonly used measure in psychology and cognitive science. However, both the ECG and the PPG signals contain rich information beyond the beat-to-beat frequency that is relevant for cognitive and physiological modelling. All these approaches are not fully covered by [Systole](https://embodied-computation-group.github.io/systole/#), but are implemented in other software (e.g. see {cite:p}`2021:makowski` for ECG components delineation). -In this notebook, we are going to review the peak detection algorithm, which future tutorials covering, for example, heart-rate variability will build upon. Here our intention is to provide a better intuition of what is happening in our peak detection algorithms, the corrections that can be applied, and the possible bias and artefacts that can emerge. However, you do not need a perfect understanding of all these steps if you want to apply peak detection to youre data, and you might also consider skipping this part to proceed directly to the next tutorial focusing on artefact detection and correction. +In this notebook, we are going to review the peak detection algorithm, which future tutorials covering, for example, heart-rate variability will build upon. Here our intention is to provide a better intuition of what is happening in our peak detection algorithms, the corrections that can be applied, and the possible bias and artefacts that can emerge. However, you do not need a perfect understanding of all these steps if you want to apply peak detection to your data, and you might also consider skipping this part to proceed directly to the next tutorial focusing on artefact detection and correction. +++ @@ -52,7 +52,7 @@ In this notebook, we are going to review the peak detection algorithm, which fut +++ -Because we will ultimately be interested in heart rate and its variability, our first goal will be to detect the R peaks. A large variety of algorithms have been proposed to extract the timing of R waves while controlling for signal noise and physiological variability. Reviewing and comparing all of the available methods is beyond the scope of this tutorial. Here, we are going to restrict our focus to some of the most popular methods, which also are available in the Python open-source ecosystem. We will use the [ecg_peaks](#systole.detection.ecg_peaks) function to perform R peak detection. This function can call a variety of peaks detection algorithms among the following: +Because we will ultimately be interested in heart rate and its variability, our first goal will be to detect the R peaks. A large variety of algorithms have been proposed to extract the timing of R waves while controlling for signal noise and physiological variability. Reviewing and comparing all of the available methods is beyond the scope of this tutorial. Here, we are going to restrict our focus to some of the most popular methods, which also are available in the Python open-source ecosystem. We will use the {py:func}`systole.detection.ecg_peaks` function to perform R peak detection. This function can call a variety of peak detection algorithms among the following: - `sleepecg` (default) - `hamilton` @@ -68,7 +68,7 @@ The detection algorithm can be selected via the `method` parameter. In these tut +++ -Let's first load an ECG recording. Here, we will select a 5 minute interval and compare the performances of the different algorithms supported by Systole. +Let's first load an ECG recording. Here, we will select a 5-minute interval and compare the performances of the different algorithms supported by Systole. ```{code-cell} ipython3 # Import ECG recording @@ -86,7 +86,7 @@ from systole.detectors import pan_tompkins, hamilton, moving_average, christov, The [Pan-Tompkins algorithm](https://en.wikipedia.org/wiki/Pan%E2%80%93Tompkins_algorithm) was introduced in 1985. It uses band-pass filtering and derivation to identify the QRS complex and apply an adaptive threshold to detect the R peaks on the filtered signal. -This is a very popular - maybe to most popular - method for R peaks detection. One of the advantages is that it can easily be applied for online detection (see [this implementation](https://github.com/c-labpl/qrs_detector) for example). As we can see in the timing report, this algorithm is also the fastest among those available in the Systole toolbox, making ideal for applications such as online peak detection and biofeedback. You can also see that the algorithm's peak detection is highly accurate as we do not see any artifacts in the estimated R-R time series. One notable exception is the first peak which has been estimated incorrectly. This is due to the filtering steps in the Pan-Tompkins algorithms, and can be corrected by using a slightly longer signal than the range of interest. +This is a very popular - maybe the most popular - method for R peak detection. One of the advantages is that it can easily be applied for online detection (see [this implementation](https://github.com/c-labpl/qrs_detector) for example). As we can see in the timing report, this algorithm is also the fastest among those available in the Systole toolbox, making it ideal for applications such as online peak detection and biofeedback. You can also see that the algorithm's peak detection is highly accurate as we do not see any artifacts in the estimated R-R time series. One notable exception is the first peak which has been estimated incorrectly. This is due to the filtering steps in the Pan-Tompkins algorithms and can be corrected by using a slightly longer signal than the range of interest. ```{code-cell} ipython3 %%timeit @@ -159,7 +159,7 @@ show( +++ -Let's first simulate some PPG recording. Here, we will use the `serialSim` class from Systole in conjunction with the `Oximeter` recording class. This will *simulate* the presence of a pulse oximeter on the computer and provide the information from a pre-recorded signal in real time. Simulating synthetic cardiac data in this way can be a great way to test your data analysis pipelines before collecting data - avoiding painful mistakes that can occur! +Let's first simulate some PPG recording. Here, we will use the `serialSim` class from Systole in conjunction with the `Oximeter` recording class. This will *simulate* the presence of a pulse oximeter on the computer and provide the information from a pre-recorded signal in real-time. Simulating synthetic cardiac data in this way can be a great way to test your data analysis pipelines before collecting data - avoiding painful mistakes that can occur! ```{code-cell} ipython3 # Pulse oximeter simulation @@ -207,11 +207,11 @@ sns.despine() ppg = import_ppg() ``` -A simple online approach like the one we described is usually good enough to detect all the systolic peaks, provided that the subject is not moving too much. The package comes with two algoritms for systolic peaks detection presented below and that can be controlled through the `method` parameter of the [ppg_peaks](#systole.detection.ppg_peaks). +A simple online approach like the one we described is usually good enough to detect all the systolic peaks, provided that the subject is not moving too much. The package comes with two algorithms for systolic peaks detection presented below and that can be controlled through the `method` parameter of the {py:func}`systole.detection.ppg_peaks`. #### Rolling mean -The current default is an adaptation of the rolling mean method proposed by {cite:p}`2019:vanGent`. This method has the advantage of being fast, simple and to perform well when the signal to noise ration is good. +The current default is an adaptation of the rolling mean method proposed by {cite:p}`2019:vanGent`. This method has the advantage of being fast, simple and performing well when the signal-to-noise ratio is good. ```{code-cell} ipython3 show( @@ -233,7 +233,7 @@ show( +++ -Clipping is a form of distortion that can limit signal when it exceeds a certain threshold [see the Wikipedia page](https://en.wikipedia.org/wiki/Clipping_(signal_processing)). Some PPG devices can produce clipping artefacts when recording the PPG signal, for example if a participant is pressing too hard on the fingerclip. Here, we can see that some clipping artefacts are found between 100 and 150 seconds in the previous recording. The threshold values (here `255`), is often set by the device and can easily be found depending on the manufacturer. These artefacts should be corrected before systolic peaks detection. One way to correct these artefacts is to remove the portion of the signal where clipping artefacts occurs and use cubic spline interpolation to reconstruct a plausible estimate of the *real* underlying signal. This is what the function `interpolate_clipping()` does {cite:p}`2019:vanGent`. +Clipping is a form of distortion that can limit signal when it exceeds a certain threshold [see the Wikipedia page](https://en.wikipedia.org/wiki/Clipping_(signal_processing)). Some PPG devices can produce clipping artefacts when recording the PPG signal, for example, if a participant is pressing too hard on the finger clip. Here, we can see that some clipping artefacts are found between 100 and 150 seconds in the previous recording. The threshold value (here `255`), is often set by the device and can easily be found depending on the manufacturer. These artefacts should be corrected before systolic peak detection. One way to correct these artefacts is to remove the portion of the signal where clipping artefacts occur and use cubic spline interpolation to reconstruct a plausible estimate of the *real* underlying signal. This is what the function `interpolate_clipping()` does {cite:p}`2019:vanGent`. ```{code-cell} ipython3 signal = ppg[ppg.time.between(110, 113)].ppg.to_numpy() # Extract a portion of signal with clipping artefacts diff --git a/docs/source/releases/v0.0.1.txt b/docs/source/releases/v0.0.1.txt deleted file mode 100644 index 647fb32e..00000000 --- a/docs/source/releases/v0.0.1.txt +++ /dev/null @@ -1,5 +0,0 @@ - -v0.0.1 ------- - -Alpha release. diff --git a/docs/source/releases/v0.1.0.txt b/docs/source/releases/v0.1.0.txt deleted file mode 100644 index 278cdf02..00000000 --- a/docs/source/releases/v0.1.0.txt +++ /dev/null @@ -1,52 +0,0 @@ - -v0.1.0 ------- - -Initial release. - -**Detection** - -a. ppg_peaks() -b. hr_subspaces() -c. interpolate_clipping() -d. rr_outliers() - - -**HRV** - -a. nnX() -b. pnX() -c. rmssd() -d. time_domain() -e. frequency_domain() -f. nonlinear() - - -**Plotting** - -a. plot_hr() -b. plot_events() -c. plot_oximeter() -d. plot_subspaces() -e. plot_psd() -f. circular() -g. plot_circular() - - -**Recording** - -a. Oximeter() - - -**Report** - -a. report_oxi() - - -**Utils** - -a. norm_triggers() -b. time_shift() -c. heart_rate() -d. to_angles() -e. to_epochs() diff --git a/docs/source/releases/v0.1.1.txt b/docs/source/releases/v0.1.1.txt deleted file mode 100644 index 20cf846c..00000000 --- a/docs/source/releases/v0.1.1.txt +++ /dev/null @@ -1,21 +0,0 @@ -v0.1.1 ------- - -**New functions** - -- |Feature| Add the **plotly** sub-module, a set of Plotly functions comprising :py:func:`systole.plotly.plot_raw`, :py:func:`systole.plotly.plot_subspaces`, :py:func:`systole.plotly.plot_ectopic`, :py:func:`systole.plotly.plot_shortlong`, :py:func:`systole.plotly.plot_frequency`, :py:func:`systole.plotly.plot_nonlinear`, :py:func:`systole.plotly.plot_timedomain`. -- |Feature| Add :py:func:`systole.utils.simulate_rr()`, for random RR interval simulation with different kind of artefacts. Can also return peak vector. -- |Feature| The **correction** sub-module has been largely rewritten and now include :py:func:`systole.correction.correct_extra`, :py:func:`systole.correction.correct_missed`, :py:func:`systole.correction.interpolate_bads`, :py:func:`systole.correction.correct_rr`, :py:func:`systole.correction.correct_peaks`, :py:func:`systole.correction.correct_missed_peaks`, `systole.correction.correct_extra_peaks`. These function can correct artefacts either using peaks addition/removal or by interpolation of the RR time series. - -**Enhancements** - -- |Enhancement| The **detection** sub-module has been improved. It is now about 10x faster and returns more information. The main function has been renamed to :py:func:`systole.detection.rr_artefacts`. - -**Bugfixes** - -- |Fix| :py:func:`systole.correction.interpolate_clipping`: add exception in case of clipping artefacts at the edge of the signal segment. This can cause cash during recording. The default behavior is now to decrement the last/first item in case of threshold value. The threshold can be changed manually. This procedure can result in slightly inaccurate interpolation, using a longer recording should always be preferred when possible. -- |Fix| The PPG signal simulator used for testing can now run infinitely. - -**Contributors** - -* `Jan C. Brammer `_ \ No newline at end of file diff --git a/docs/source/releases/v0.1.2.txt b/docs/source/releases/v0.1.2.txt deleted file mode 100644 index 3d502fbf..00000000 --- a/docs/source/releases/v0.1.2.txt +++ /dev/null @@ -1,14 +0,0 @@ -v0.1.2 ------- - - **New functions** - -- |Feature| Add :py:func:`systole.utils.to_rr()`. for peaks or index vectors convertion to RR intervals -- |Feature| Add :py:func:`systole.recording.BrainVisionExG()`, a class to read physio recording from BrainVision ExG products via TCP/IP connection. -- |Feature| Add :py:func:`systole.recording.findOximeter()`, find the USB port where Nonin Oximeter is plugged by looping through the USB port and checking the input. -- |Feature| Add :py:func:`systole.detection.ecg_peaks()`. A wrapper around py-ecg-detectors for basic ECG peaks detection. - -**Enhancements** -- |Enhancement| Improved documentation and examples. -- |Enhancement| Simplification of PPG example data import. -- |Enhancement| Improved interactive plotting functions. diff --git a/docs/source/releases/v0.1.3.txt b/docs/source/releases/v0.1.3.txt deleted file mode 100644 index 49e33667..00000000 --- a/docs/source/releases/v0.1.3.txt +++ /dev/null @@ -1,15 +0,0 @@ -v0.1.3 ------- - -- |Enhancement| :py:func:`systole.plotly.plot_raw()`: add `ecg_method` parameter to control the ECG peak detection method used. -- |Enhancement| Download dataset directly from GitHub instead of copying the files at install. -- |Enhancement| Harmonization of :py:func:`systole.plotting.plot_raw()` and :py:func:`systole.plotting.plot_raw()` (replace the `plot_hr()` function), and :py:func:`systole.plotly.plot_subspaces()` and :py:func:`systole.plotly.plot_subspaces()`. -- |Enhancement| The :py:class:`systole.recording.Oximeter()` class has been improved: - -* :py:func:`systole.recording.Oximeter.setup()` has an `nAttempts` argument so it will not run forever if no valid signal is recordedfor a given number of attempts (default is 100). -* :py:func:`systole.recording.Oximeter.check()` has been updated and accept data format #7 from Xpods, allowing more flexibility. -* :py:func:`systole.recording.Oximeter.save()` will now save additional channels and support `.txt` and `.npy` file extensions. - -- |Enhancement| Create a :py:func:`systole.recording.Oximeter.reset()` method to avoid improper use of `__init__()`. -- |Enhancement| Add pre-commit hooks, flake8, black and isort CI tests. -- |Enhancement| Add type hints and CI testing with mypy. \ No newline at end of file diff --git a/docs/source/releases/v0.2.0.txt b/docs/source/releases/v0.2.0.txt deleted file mode 100644 index 3ac0baef..00000000 --- a/docs/source/releases/v0.2.0.txt +++ /dev/null @@ -1,65 +0,0 @@ -v0.2.0 ------- - -Highlights -++++++++++ - -This is a major release that introduces a new plotting backend using Bokeh instead of Plotly, several API changes and performance improvement (Numba). - -The `plots` submodule now includes the following functions: - -- :func:`systole.plots.plot_circular` -- :func:`systole.plots.plot_ectopic` -- :func:`systole.plots.plot_events` -- :func:`systole.plots.plot_evoked` -- :func:`systole.plots.plot_frequency` -- :func:`systole.plots.plot_poincare` -- :func:`systole.plots.plot_raw` -- :func:`systole.plots.plot_rr` -- :func:`systole.plots.plot_shortlong` -- :func:`systole.plots.plot_subspaces` - -These functions can be called using Bokeh or Matplotlib as backend (`plot_circular` only accepts Matplotlib for now). - -All the methods available in :func:`systole.detection.ecg_peaks` are now accelerated with Numba. - -- |Enhancement| Add Numba support for :func:`systole.detectors.pan_tompkins()` (~7x faster) -- |Enhancement| Add Numba support for :func:`systole.detectors.hamilton()` (~10x faster) -- |Enhancement| Add Numba support for :func:`systole.detectors.christov()` (~30x faster) -- |Enhancement| Add Numba support for :func:`systole.detectors.engelse_zeelenberg()` (~20x faster) - -This also drops py-ecg-detectors dependency, and the refactored code is now included in Systole. - -This release also includes some API changes and renaming for consistency (`oxi_peaks` -> `ppg_peaks`). - -Changes -+++++++ - -- |Api| Remove support for Plotly. Add support for Bokeh. Merge interactive and non interactive modules into a unique :func:`plots` module. - -- |Docs| Use PyData Sphinx theme. - -- |Docs| Several additions to the tutorials pages, now divided into 5 Chapters: -1. Physiological signal analysis -2. Detecting cycles -3. Detecting and correcting artefacts -4. Heart rate variability -5. Instantaneous heart rate and evoked heart rate - -- |Docs| Add examples for :func:`systole.correction.correct_peaks()` and :func:`systole.correction.correct_rr()` in the gallery. - -- |Enhancement| |Feature| |API| Most of the functions now accept inputs as `peaks` (boolean vector), `peaks_idx` (int indexes vector), `rr_ms` (floats RR intervals in miliseconds) or `rr_s` (floats RR intervals in seconds). :func:`systole.utils.to_rr()` has been renamed to :py:func:`systole.utils.input_conversion()` and support the conversion between data types. - -- |Fix| |Enhancement| :func:`systole.detection.ppg_peaks()` now handles negative values/ low sampling rate. - -- |Enhancement| :func:`systole.correction.correct_peaks()` and :func:`systole.correction.correct_rr()` have been improved for performances, allows `n` iterations and print results. - -- |Enhancement| utils.to_epochs() now accept multiple condition (as list) and returns rejection vectors for each one. The code has also been simplified and runs faster. - -- |Fix| Remove use of `outdated`. - -Contributors -++++++++++++ - -- Gidon Levakov (gidonlevakov@gmail.com) -- Peter Doggart (peter.doggart@pulseai.io) \ No newline at end of file diff --git a/docs/source/releases/v0.2.1.txt b/docs/source/releases/v0.2.1.txt deleted file mode 100644 index b0a9d106..00000000 --- a/docs/source/releases/v0.2.1.txt +++ /dev/null @@ -1,19 +0,0 @@ -v0.2.1 ------- - -Highlights -++++++++++ - -This release fixes dependencies and import errors pointed during the review of the JOSS Systole paper and add the paper to the main repository. It also adds several bug fixes and functionality improvements. - -We recommend all users upgrade to this new version. - -Changes -+++++++ - -- |Fix| Important BUGFIX: :func:`systole.hrv.frequency_domain()` switched the variables `"power_hf_nu"` and `"power_lf_nu"` before returning. -- |Fix| Important BUGFIX: :func:`systole.hrv.frequency_domain()` the variables `"power_hf_nu"` and `"power_lf_nu"` were not properly scalled (x100) before returning. -- |Fix| |Enhancement| Fix :func:`systole.plots.plot_evoked()` so it works with the three inputs types and improve documentation. -- |Enhancement| Fix :func:`systole.utils.norm_triggers()` is now using Numba's jit by default. -- |Enhancement| |Defaults| The :func:`systole.plots.plot_frequency()` function clip power to be >= 0 by default. -- |Defaults| The :func:`systole.hrv.frequency_domain()` :func:`systole.hrv.time_domain()` functions rounds with the same parameter (`6`). diff --git a/docs/source/releases/v0.2.2.txt b/docs/source/releases/v0.2.2.txt deleted file mode 100644 index 8327300d..00000000 --- a/docs/source/releases/v0.2.2.txt +++ /dev/null @@ -1,24 +0,0 @@ -v0.2.2 ------- - -Highlights -++++++++++ - -This release seeks to improve the heart rate variability module and will be the reference version for the JOSS publication. - -Changes -+++++++ - -- |Feature| HRV module: Add a function func:`systole.hrv.all_domain()` to easily compute all possible HRV indices. -- |Feature| HRV module: Add recurence plot analysis and fast recurrence matrix computation (thanks to Dominique Makowski), and basic quantitative metrics (recurrence rate, l_max, l_mean, determinism rate, Shannon entropy). These metrics are now part of the nonlinear reports (py:func:`systole.hrv.nonlinear_domain()`) or can be called separately (py:func:`systole.hrv.recurrence()`). -- |Feature| |Enhancement| HRV module: The time domain function (func:`systole.hrv.time_domain()`) also reports the SDSD by default. -- |Feature| |Enhancement| HRV module: The frequency domain function (func:`systole.hrv.frequency_domain()`) now returns the total power and LF/HF ratio by default. -- |Feature| Plotting module: Add nicer rendering for table output for time_domain, frequency_domain and nonlinear_domain functions, working either with Tabulate or Bokeh. -- |Feature| Add a respiratory peaks detection function (func:`systole.detection.rsp_peaks()`). Very simple for now, will be improved in the future. -- |Docs| |Enhancement| Improve tutorial notebook on HRV. - -Contributors -++++++++++++ - -- Dominique Makowski (https://github.com/DominiqueMakowski) -- Bertrand Hermann (bertrand.hermann@ghu-paris.fr) diff --git a/docs/source/releases/v0.2.3.txt b/docs/source/releases/v0.2.3.txt deleted file mode 100644 index 83c36ef0..00000000 --- a/docs/source/releases/v0.2.3.txt +++ /dev/null @@ -1,32 +0,0 @@ -v0.2.3 ------- - -Highlights -++++++++++ - -This release introduce two new components: - -* the report module for fast preprocessing of ECG/PPG/Respiration dataset with interactive reports -* the Editor/Viewer for manual peaks correction and segments annotation. - -Both modules are intended to integrate tightly with BIDS structured folders, but can also be used separately. Some features are still missing (especially related to respiration), and will be covered in future releases. - -Changes -+++++++ - -- |Fix| Rewrite func:`systole.correction.correct_missed` (now func:`systole.correction.correct_missed_rr`), func:`systole.correction.correct_extra` (now func:`systole.correction.correct_extra_rr`) and func:`systole.correction.correct_rr` to fix the index overflows caused by the insertion or deletion of intervals. All function are now jited, which should also provided performance improvements. -- |Fix| func:`systole.detection.interpolate_clipping()` was not handling thresholds correctly if the provided threshold is not the max/min of the time series. -- |Enhancement| |Feature| Add a func:`systole.utils.find_clipping()` function to automatically find clipping threshold. This function is called by ppg_peaks when clipping=True and clipping_threshold="auto". -- |Feature| Plots module: Add a function func:`systole.plots.plot_raw()` can now plot respiratory signal. -- |Feature| Utils : Add a function func:`systole.utils.nan_cleaning()` to automatically interpolate NaN values in a given signal. This function can be called automatically (if required) and the begining of `ppg_peaks`, `ecg_peaks` or `rsp_peaks` by setting the parameter `clean_nan` to `True`. The default is `False`. -- Remove the default sampling frequencies for `ecg_peaks`, `ppg_peaks` and `rsp_peaks`. Do not resample if `sfreq` and `new_sfreq` are identicals. -- Remove func:`systole.recording.findOximeter()`. -- |Feature| func:`systole.plots.plot_raw()` and:py:func:`systole.plots.plot_r()` now accept an `events_params` parameter to plot events together with the instantaneous heart rate. -- |Enhancement| func:`systole.detection.ppg_peaks()`: change `noise_removal` to moving_average and add a `moving_average_length` parameter to control the window length. -- |Enhancement| |Feature| func:`systole.plots.plot_raw()` and func:`systole.plots.plot_r()` now accept a `bad_segments` parameter to help visualize annotated bad segments from the Editor/Viewer. -- |Feature| Add func:`systole.utils.norm_bad_segments()` and func:`systole.utils.get_valid_segments()` to help filtering part of the signal that are marked as bad. - -Contributors -++++++++++++ - -- Alex Vasilichi (https://github.com/alexvasilichi) diff --git a/docs/source/releases/v0.2.4.txt b/docs/source/releases/v0.2.4.txt deleted file mode 100644 index 7b7856e0..00000000 --- a/docs/source/releases/v0.2.4.txt +++ /dev/null @@ -1,8 +0,0 @@ -v0.2.4 ------- - -Changes -+++++++ - -- |Fix| Viewer was erroring when saving empty bad segment variable. -- |Enhancement| |API| Rename :py:mod:`systole.viewer` to :py:mod:`systole.interact`. Refactor the Viewer and the Editor so the Editor can be used separately to work with any kind of time series. The Viewer only handle integration with BIDS structured folders. diff --git a/docs/source/releases/v0.2.5.txt b/docs/source/releases/v0.2.5.txt deleted file mode 100644 index 9956e6f7..00000000 --- a/docs/source/releases/v0.2.5.txt +++ /dev/null @@ -1,8 +0,0 @@ -v0.2.5 (dev) ------------- - -Changes -+++++++ - -- |Fix| The Viewer now load correctly peaks and bad segments in case of variable sampling frequencies. -- |Feature| Add the Multi-scale peak and trough detection algorithm (MSPTD) for peaks and onsets detection in PPG signals. \ No newline at end of file diff --git a/systole/detectors/msptd.py b/systole/detectors/msptd.py index 6ae74d1f..49ea0a81 100644 --- a/systole/detectors/msptd.py +++ b/systole/detectors/msptd.py @@ -27,8 +27,8 @@ def msptd( sfreq : The sampling frequency (Hz). kind : - The type of detection to perform. Can be `"peaks"`, `"onsets"` or `"peaks-onsets"`. - Defaults to `"peaks-onsets"`. + The type of detection to perform. Can be `"peaks"`, `"onsets"` or + `"peaks-onsets"`. Defaults to `"peaks-onsets"`. .. tip: Using `"peaks"` and `"onsets"` will skip the non required part of the diff --git a/tests/test_detection.py b/tests/test_detection.py index 4a5e741a..16424b2f 100644 --- a/tests/test_detection.py +++ b/tests/test_detection.py @@ -20,15 +20,15 @@ def test_ppg_peaks(self): assert np.all(rolling_average_signal == msptd_signal) # mean RR intervals - assert np.diff(np.where(rolling_average_peaks)[0]).mean() == 874.2068965517242 - assert np.diff(np.where(msptd_peaks)[0]).mean() == 867.3105263157895 + assert np.isclose(np.diff(np.where(rolling_average_peaks)[0]).mean(), 874.2068965517242) + assert np.isclose(np.diff(np.where(msptd_peaks)[0]).mean(), 867.3105263157895) # with nan removal and clipping correction rolling_average_signal2, rolling_average_peaks2 = ppg_peaks( ppg, clipping_thresholds=(0, 255), clean_nan=True, sfreq=75 ) assert (rolling_average_signal == rolling_average_signal2).all() - assert np.diff(np.where(rolling_average_peaks2)[0]).mean() == 874.2068965517242 + assert np.isclose(np.diff(np.where(rolling_average_peaks2)[0]).mean(), 874.2068965517242) def test_ecg_peaks(self): signal_df = import_dataset1(modalities=["ECG"])[: 20 * 2000] @@ -55,25 +55,20 @@ def test_resp_peaks(self): # Import respiration recording resp = import_dataset1(modalities=["Respiration"])[: 200 * 2000].respiration.to_numpy() - rolling_average_signal, rolling_average_peaks = rsp_peaks( + rolling_average_signal, _ = rsp_peaks( resp, sfreq=2000, kind="peaks", method="rolling_average" ) - msptd_signal, msptd_peaks = rsp_peaks( + msptd_signal, _ = rsp_peaks( resp, sfreq=2000, kind="peaks", method="msptd" ) assert np.all(rolling_average_signal == msptd_signal) - # mean respiration intervals - #assert np.diff(np.where(rolling_average_peaks)[0]).mean() == 874.2068965517242 - #assert np.diff(np.where(msptd_peaks)[0]).mean() == 867.3105263157895 - # with nan removal - rolling_average_signal2, rolling_average_peaks2 = rsp_peaks( + rolling_average_signal2, _ = rsp_peaks( resp, clean_nan=True, sfreq=2000, kind="peaks", method="rolling_average" ) assert (rolling_average_signal == rolling_average_signal2).all() - #assert np.diff(np.where(rolling_average_peaks2)[0]).mean() == 874.2068965517242 def test_rr_artefacts(self): ppg = import_ppg().ppg.to_numpy()