Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sparsEDA return a tonic,phasic pair #965

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fd86de2
0.2.8
DominiqueMakowski Nov 6, 2023
c13a97e
Add requests
DominiqueMakowski Nov 6, 2023
d006bf4
Update setup.py
DominiqueMakowski Nov 6, 2023
03dea78
add function head of visibility-graph detector
JonasEmrich Dec 12, 2023
c3718f6
add FastNVG implementation
JonasEmrich Dec 13, 2023
84076ce
activate detector in ecg_findpeaks()
JonasEmrich Dec 13, 2023
4c93d18
fix code formatting
JonasEmrich Dec 13, 2023
4eff069
minor
DominiqueMakowski Dec 13, 2023
d12f22f
adapt cleaning documentation
JonasEmrich Dec 14, 2023
7674b25
updated documentation and removed method by koka2022
JonasEmrich Dec 14, 2023
a3a6513
Add deprecation warning for method koka2022
JonasEmrich Dec 15, 2023
79b4888
[Hotfix] requests is not builtin anymore
DominiqueMakowski Dec 18, 2023
8b6d145
Add fig2img
DominiqueMakowski Dec 18, 2023
388b9f6
Merge branch 'master' into dev
DominiqueMakowski Dec 18, 2023
87c8c60
Update eeg_1min_200hz.pickle
DominiqueMakowski Dec 18, 2023
72e7601
Merge branch 'dev' into pr/939
DominiqueMakowski Dec 18, 2023
51b48bd
Merge pull request #939 from JonasEmrich/ecg_peaks_vgmethod
DominiqueMakowski Dec 21, 2023
11f8d59
remove tests from project
Jan 2, 2024
2c410f4
change prune to recursive-exclude tests
Jan 2, 2024
cdbefba
Merge pull request #941 from Ninebell/dev
DominiqueMakowski Jan 3, 2024
efe3269
Optimise _ecg_findpeaks_elgendi
purpl3F0x Jan 4, 2024
2a1c621
Merge pull request #943 from purpl3F0x/dev
DominiqueMakowski Jan 10, 2024
b365588
[Fix + Improvement] Faster _ecg_findpeaks_hamilton and bug-fix
purpl3F0x Jan 10, 2024
576fa90
Added minimum N in rodrigues2021 peaks detector
LucaCerina Jan 17, 2024
b25b8bb
Merge pull request #949 from LucaCerina/bugfix-function_ecg_findpeaks…
DominiqueMakowski Jan 17, 2024
7abb936
update docstring to reflect implementation of khodadad2018
danibene Jan 18, 2024
e74b5f8
update description of khodadad2018 to reflect implementation of cleaning
danibene Jan 18, 2024
ba9b3a3
Merge pull request #952 from danibene/docs/rsp_clean_khodadad2018
DominiqueMakowski Jan 19, 2024
56aba7f
Merge pull request #947 from purpl3F0x/dev
DominiqueMakowski Jan 21, 2024
b55b383
Fix poetry config
stasinos Feb 17, 2024
d4d76a0
Make sparsEDA return a (tonic,phasic) pair
stasinos Feb 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,4 @@ studies/complexity_structure/README_cache/
studies/complexity_structure/manuscript_cache/
studies/complexity_eeg/data_attractor.csv
studies/complexity_eeg/data_delay.csv
testing.ipynb
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ include NEWS.rst
include LICENSE
include README.rst

recursive-include tests *
recursive-exclude tests *
recursive-exclude * __pycache__
recursive-exclude * *.py[co]

recursive-include docs *.rst conf.py *.jpg *.png *.gif

Binary file modified data/eeg_1min_200hz.pickle
Binary file not shown.
4 changes: 2 additions & 2 deletions neurokit2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
from .video import *

# Info
__version__ = "0.2.7"
__version__ = "0.2.8"


# Maintainer info
__author__ = "The NeuroKit development team"
__email__ = "[email protected]"
__email__ = "[email protected]"


# Citation
Expand Down
6 changes: 2 additions & 4 deletions neurokit2/data/database.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import pathlib
import urllib.parse
import urllib
import zipfile

import requests


def download_from_url(url, destination_path=None):
"""**Download Files from URLs**
Expand All @@ -26,7 +24,7 @@ def download_from_url(url, destination_path=None):
destination_path = _download_path_sanitize(url, destination_path)

# Download the file
response = requests.get(url)
response = urllib.request.urlopen(url)

if response.status_code == 200:
with destination_path.open("wb") as file:
Expand Down
10 changes: 8 additions & 2 deletions neurokit2/data/read_xdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@ def read_xdf(filename, upsample=2, fillmissing=None):

# Special treatment for some devices
if stream["info"]["name"][0] == "Muse":
# Rename GYRO channels and add ACCelerometer
# Rename GYRO channels
if stream["info"]["type"][0] == "GYRO":
dat = dat.rename(columns={"X": "GYRO_X", "Y": "GYRO_Y", "Z": "GYRO_Z"})
dat["ACC"] = np.sqrt(dat["GYRO_X"] ** 2 + dat["GYRO_Y"] ** 2 + dat["GYRO_Z"] ** 2)
# Compute movement
dat["GYRO"] = np.sqrt(dat["GYRO_X"] ** 2 + dat["GYRO_Y"] ** 2 + dat["GYRO_Z"] ** 2)

if stream["info"]["type"][0] == "ACC":
dat = dat.rename(columns={"X": "ACC_X", "Y": "ACC_Y", "Z": "ACC_Z"})
# Compute acceleration
dat["ACC"] = np.sqrt(dat["ACC_X"] ** 2 + dat["ACC_Y"] ** 2 + dat["ACC_Z"] ** 2)

# Muse - PPG data has three channels: ambient, infrared, red
if stream["info"]["type"][0] == "PPG":
Expand Down
57 changes: 33 additions & 24 deletions neurokit2/ecg/ecg_clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
"""**ECG Signal Cleaning**

Clean an ECG signal to remove noise and improve peak-detection accuracy. Different cleaning
method are implemented.
methods are implemented.

* ``'neurokit'`` (default): 0.5 Hz high-pass butterworth filter (order = 5), followed by
powerline filtering (see ``signal_filter()``). By default, ``powerline = 50``.
Expand All @@ -28,7 +28,8 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
description!**
* ``'engzeemod2012'``: Method used in Engelse & Zeelenberg (1979). **Please help providing a
better description!**

* ``'vg'``: Method used in Visibility Graph Based Detection Emrich et al. (2023)
and Koka et al. (2022). A 4.0 Hz high-pass butterworth filter (order = 2).

Parameters
----------
Expand All @@ -39,7 +40,7 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
method : str
The processing pipeline to apply. Can be one of ``"neurokit"`` (default),
``"biosppy"``, ``"pantompkins1985"``, ``"hamilton2002"``, ``"elgendi2010"``,
``"engzeemod2012"``.
``"engzeemod2012"``, ``'vg'``.
**kwargs
Other arguments to be passed to specific methods.

Expand Down Expand Up @@ -71,6 +72,7 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
"ECG_Hamilton" : nk.ecg_clean(ecg, sampling_rate=250, method="hamilton2002"),
"ECG_Elgendi" : nk.ecg_clean(ecg, sampling_rate=250, method="elgendi2010"),
"ECG_EngZeeMod" : nk.ecg_clean(ecg, sampling_rate=250, method="engzeemod2012"),
"ECG_VG" : nk.ecg_clean(ecg, sampling_rate=250, method="vg"),
"ECG_TC" : nk.ecg_clean(ecg, sampling_rate=250, method="templateconvolution")
})

Expand All @@ -91,6 +93,9 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
* Elgendi, M., Jonkman, M., & De Boer, F. (2010). Frequency Bands Effects on QRS Detection.
Biosignals, Proceedings of the Third International Conference on Bio-inspired Systems and
Signal Processing, 428-431.
* Emrich, J., Koka, T., Wirth, S., & Muma, M. (2023), Accelerated Sample-Accurate R-Peak
Detectors Based on Visibility Graphs. 31st European Signal Processing Conference
(EUSIPCO), 1090-1094, doi: 10.23919/EUSIPCO58844.2023.10290007

"""
ecg_signal = as_vector(ecg_signal)
Expand Down Expand Up @@ -118,7 +123,14 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
clean = _ecg_clean_elgendi(ecg_signal, sampling_rate)
elif method in ["engzee", "engzee2012", "engzeemod", "engzeemod2012"]:
clean = _ecg_clean_engzee(ecg_signal, sampling_rate)
elif method in ["vg", "vgraph", "koka2022"]:
elif method in ["vg", "vgraph", "fastnvg", "emrich", "emrich2023"]:
clean = _ecg_clean_vgraph(ecg_signal, sampling_rate)
elif method in ["koka2022", "koka"]:
warn(
"The 'koka2022' method has been replaced by 'emrich2023'."
" Please replace method='koka2022' by method='emrich2023'.",
category=NeuroKitWarning,
)
clean = _ecg_clean_vgraph(ecg_signal, sampling_rate)
elif method in ["templateconvolution"]:
clean = _ecg_clean_templateconvolution(ecg_signal, sampling_rate)
Expand All @@ -141,7 +153,7 @@ def ecg_clean(ecg_signal, sampling_rate=1000, method="neurokit", **kwargs):
"NeuroKit error: ecg_clean(): 'method' should be "
"one of 'neurokit', 'biosppy', 'pantompkins1985',"
" 'hamilton2002', 'elgendi2010', 'engzeemod2012',"
" 'templateconvolution'."
" 'templateconvolution', 'vg'."
)
return clean

Expand All @@ -168,9 +180,7 @@ def _ecg_clean_nk(ecg_signal, sampling_rate=1000, **kwargs):
order=5,
)

clean = signal_filter(
signal=clean, sampling_rate=sampling_rate, method="powerline", **kwargs
)
clean = signal_filter(signal=clean, sampling_rate=sampling_rate, method="powerline", **kwargs)
return clean


Expand All @@ -194,9 +204,7 @@ def _ecg_clean_biosppy(ecg_signal, sampling_rate=1000):

# -> get_filter()
# -> _norm_freq()
frequency = (
2 * np.array(frequency) / sampling_rate
) # Normalize frequency to Nyquist Frequency (Fs/2).
frequency = 2 * np.array(frequency) / sampling_rate # Normalize frequency to Nyquist Frequency (Fs/2).

# -> get coeffs
a = np.array([1])
Expand Down Expand Up @@ -305,10 +313,15 @@ def _ecg_clean_engzee(ecg_signal, sampling_rate=1000):


# =============================================================================
# Engzee Modified (2012)
# Visibility-Graph Detector - Emrich et al. (2023) & Koka et al. (2022)
# =============================================================================
def _ecg_clean_vgraph(ecg_signal, sampling_rate=1000):
"""Filtering used by Taulant Koka and Michael Muma (2022).
"""Filtering used for Visibility-Graph Detectors Emrich et al. (2023) and Koka et al. (2022).

- J. Emrich, T. Koka, S. Wirth and M. Muma, "Accelerated Sample-Accurate R-Peak
Detectors Based on Visibility Graphs," 31st European Signal Processing
Conference (EUSIPCO), 2023, pp. 1090-1094, doi: 10.23919/EUSIPCO58844.2023.10290007,
https://ieeexplore.ieee.org/document/10290007

- T. Koka and M. Muma (2022), Fast and Sample Accurate R-Peak Detection for Noisy ECG Using
Visibility Graphs. In: 2022 44th Annual International Conference of the IEEE Engineering
Expand All @@ -332,12 +345,12 @@ def _ecg_clean_vgraph(ecg_signal, sampling_rate=1000):
# Template Convolution (Exploratory)
# =============================================================================
def _ecg_clean_templateconvolution(ecg_signal, sampling_rate=1000):
"""Filter and Convolve ECG signal with QRS complex template.
Totally exploratory method by Dominique Makowski, use at your own risks.
"""Filter and Convolve ECG signal with QRS complex template. Totally exploratory method by Dominique Makowski, use
at your own risks.

The idea is to use a QRS template to convolve the signal with, in order to magnify the QRS features. However,
it doens't work well and creates a lot of artifacts. If you have ideas for improvement please let me know!

The idea is to use a QRS template to convolve the signal with, in order to magnify the QRS
features. However, it doens't work well and creates a lot of artifacts. If you have ideas for
improvement please let me know!
"""

window_size = int(np.round(sampling_rate / 4))
Expand All @@ -355,14 +368,10 @@ def _ecg_clean_templateconvolution(ecg_signal, sampling_rate=1000):
)

# Detect peaks
peaks, _ = scipy.signal.find_peaks(
filtered, distance=sampling_rate / 3, height=0.5 * np.std(filtered)
)
peaks, _ = scipy.signal.find_peaks(filtered, distance=sampling_rate / 3, height=0.5 * np.std(filtered))
peaks = peaks[peaks + 0.6 * sampling_rate < len(ecg_signal)]

idx = [
np.arange(p - int(sampling_rate / 2), p + int(sampling_rate / 2)) for p in peaks
]
idx = [np.arange(p - int(sampling_rate / 2), p + int(sampling_rate / 2)) for p in peaks]
epochs = np.array([filtered[i] for i in idx])
qrs = np.mean(epochs, axis=0)

Expand Down
Loading
Loading