Skip to content

Commit

Permalink
Merge pull request #992 from neuropsychology/dev
Browse files Browse the repository at this point in the history
0.2.10
  • Loading branch information
DominiqueMakowski authored Aug 16, 2024
2 parents 1aa8dee + d783e40 commit 45c9ad9
Show file tree
Hide file tree
Showing 52 changed files with 933 additions and 384 deletions.
1 change: 1 addition & 0 deletions .github/workflows/docs-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
pip install EMD-signal
pip install cvxopt
pip install ts2vg
pip install pickleshare
pip install https://github.com/neuropsychology/neurokit/zipball/dev
- name: Build documentation 📜
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/docs-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
pip install EMD-signal
pip install cvxopt
pip install ts2vg
pip install pickleshare
pip install https://github.com/neuropsychology/neurokit/zipball/dev
- name: Build documentation 📜
Expand Down
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Contributors
* `Jannik Gut <https://github.com/rostro36>`_
* `Nattapong Thammasan <https://github.com/Nattapong-OnePlanet>`_ *(OnePlanet, Netherlands)*
* `Marek Sokol <https://github.com/sokolmarek>`_ *(Faculty of Biomedical Engineering of the CTU in Prague, Czech Republic)*
* `Johannes Herforth <https://github.com/DerAndereJohannes>`_ *(University of Luxembourg, Luxembourg)*


Thanks also to `Chuan-Peng Hu <https://github.com/hcp4715>`_, `@ucohen <https://github.com/ucohen>`_, `Anthony Gatti <https://github.com/gattia>`_, `Julien Lamour <https://github.com/lamourj>`_, `@renatosc <https://github.com/renatosc>`_, `Nicolas Beaudoin-Gagnon <https://github.com/Fegalf>`_ and `@rubinovitz <https://github.com/rubinovitz>`_ for their contribution in `NeuroKit 1 <https://github.com/neuropsychology/NeuroKit.py>`_.
Expand Down
4 changes: 2 additions & 2 deletions data/gudb/download_gudb.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
# creating class which loads the experiment
ecg_class = ecg_gudb_database.GUDb(participant, experiment)

# Chest Strap Data - only donwload if R-peaks annotations are available
# Chest Strap Data - only download if R-peaks annotations are available
if ecg_class.anno_cs_exists:

data = pd.DataFrame({"ECG": ecg_class.cs_V2_V1})
Expand All @@ -41,7 +41,7 @@
anno = pd.DataFrame({"Rpeaks": ecg_class.anno_cs})
anno["Participant"] = "GUDB_%.2i" %(participant)
anno["Sampling_Rate"] = 250
anno["Database"] = "GUDB (" + experiment + ")"
anno["Database"] = "GUDB_" + experiment

# Store with the rest
dfs_ecg.append(data)
Expand Down
24 changes: 12 additions & 12 deletions data/ludb/download_ludb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
The database consists of 200 10-second 12-lead ECG signal records representing different morphologies of the ECG signal. The ECGs were collected from healthy volunteers and patients, which had various cardiovascular diseases. The boundaries of P, T waves and QRS complexes were manually annotated by cardiologists for all 200 records.
Steps:
1. In the command line, run 'pip install gsutil'
2. Then, 'gsutil -m cp -r gs://ludb-1.0.0.physionet.org D:/YOURPATH/NeuroKit/data/ludb'
This will download all the files in a folder named 'ludb-1.0.0.physionet.org' at the
destination you entered.
1. Download zipped data base from https://physionet.org/content/ludb/1.0.1/
2. Unzip the folder so that you have a `lobachevsky-university-electrocardiography-database-1.0.1/` folder'
3. Run this script.
"""
import pandas as pd
Expand All @@ -16,31 +14,33 @@
import os


files = os.listdir("./ludb-1.0.0.physionet.org/")

dfs_ecg = []
dfs_rpeaks = []


for participant in range(200):
filename = str(participant + 1)

data, info = wfdb.rdsamp("./ludb-1.0.0.physionet.org/" + filename)
data, info = wfdb.rdsamp(
"./lobachevsky-university-electrocardiography-database-1.0.1/data/" + filename
)

# Get signal
data = pd.DataFrame(data, columns=info["sig_name"])
data = data[["i"]].rename(columns={"i": "ECG"})
data["Participant"] = "LUDB_%.2i" %(participant + 1)
data["Participant"] = "LUDB_%.2i" % (participant + 1)
data["Sample"] = range(len(data))
data["Sampling_Rate"] = info['fs']
data["Sampling_Rate"] = info["fs"]
data["Database"] = "LUDB"

# Get annotations
anno = wfdb.rdann("./ludb-1.0.0.physionet.org/" + filename, 'atr_i')
anno = wfdb.rdann(
"./lobachevsky-university-electrocardiography-database-1.0.1/data/" + filename, "i"
)
anno = anno.sample[np.where(np.array(anno.symbol) == "N")[0]]
anno = pd.DataFrame({"Rpeaks": anno})
anno["Participant"] = "LUDB_%.2i" %(participant + 1)
anno["Sampling_Rate"] = info['fs']
anno["Participant"] = "LUDB_%.2i" % (participant + 1)
anno["Sampling_Rate"] = info["fs"]
anno["Database"] = "LUDB"

# Store with the rest
Expand Down
Empty file.
90 changes: 90 additions & 0 deletions docs/codebook.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Codebook
========

Here you can download the complete codebook which details the variables that you can compute using the NeuroKit package.

.. raw:: html

<div style="text-align: center;">
<a href="_static/neurokit_codebook.csv" download="neurokit_codebook.csv">
<button style="background-color: #4CAF50; color: white; padding: 10px 20px; margin: 10px; border: none; cursor: pointer; width: 50%;">Download Codebook</button>
</a>
</div>

This codebook contains detailed descriptions of all variables, their descriptions, and additional metadata.


Codebook Table
==============

.. raw:: html

<style>
#csvDataTable {
width: 100%;
border-collapse: collapse;
.. background-color: #f8f8f8;
color: white;
}
#csvDataTable th, #csvDataTable td {
padding: 8px 12px;
border: 1px solid #ccc;
text-align: left;
}
</style>

<div id="csv-table">
<table id="csvDataTable">
</table>
</div>

<script>
function parseCSVLine(text) {
const cols = [];
let col = '';
let insideQuotes = false;
for (let i = 0; i < text.length; i++) {
const char = text[i];
if (insideQuotes && char === '"' && text[i + 1] == '"') {
i++;
col += char;
continue;
}
if (char === '"' && text[i - 1] !== '\\') {
insideQuotes = !insideQuotes;
continue;
}
if (char === ',' && !insideQuotes) {
cols.push(col);
col = '';
} else {
col += char;
}
}
cols.push(col);
return cols.map(field => field.replace(/""/g, '"')); // Replace escaped quotes
}
document.addEventListener("DOMContentLoaded", function() {
fetch('_static/neurokit_codebook.csv')
.then(response => response.text())
.then(csv => {
let lines = csv.trim().split('\n');
let html = '<tr><th>' + parseCSVLine(lines[0]).join('</th><th>') + '</th></tr>';
for (let i = 1; i < lines.length; i++) {
html += '<tr><td>' + parseCSVLine(lines[i]).join('</td><td>') + '</td></tr>';
}
document.getElementById('csvDataTable').innerHTML = html;
})
.catch(error => console.error('Error loading the CSV file:', error));
});
</script>

5 changes: 3 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath("../"))


Expand Down Expand Up @@ -69,6 +69,7 @@ def find_version():
"sphinxemoji.sphinxemoji",
"sphinx_copybutton",
"myst_nb",
"directives.csv_codebook_directive",
]

# Add any paths that contain templates here, relative to this directory.
Expand Down Expand Up @@ -131,7 +132,7 @@ def find_version():
"use_issues_button": True,
"path_to_docs": "docs/",
"use_edit_page_button": True,
"logo_only": True,
# "logo_only": True,
"show_toc_level": 1,
"navigation_with_keys": False,
}
Expand Down
89 changes: 89 additions & 0 deletions docs/directives/csv_codebook_directive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import csv
import os
from docutils import nodes
from docutils.parsers.rst import Directive

abrv_to_sensor = {
"ecg": "Electrocardiography",
"eda": "Electrodermal Activity",
"rsp": "Respiration",
"ppg": "Photoplethysmography",
"eeg": "Electroencephalography",
"emg": "Electromyography",
"eog": "Electrooculography",
"hrv": "Heart Rate Variability",
}

class CSVDocDirective(Directive):
has_content = True

def run(self):
# Codebook path
csv_file_path = os.path.join(os.path.abspath('.'), "_static", "neurokit_codebook.csv")

# Check if the file exists and whether it is empty
file_empty = not os.path.exists(csv_file_path) or os.stat(csv_file_path).st_size == 0

# List to hold bullet list nodes
bullet_list = nodes.bullet_list()

doc_source_name = self.state.document.settings.env.temp_data.get('object')[0]

maybe_sensor = doc_source_name.split("_")
doc_sensor = "N/A"

if len(maybe_sensor) > 0 and maybe_sensor[0] in abrv_to_sensor:
doc_sensor = abrv_to_sensor[maybe_sensor[0]]

# Open the CSV file and append the content
with open(csv_file_path, 'a', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)

# Write header if file is newly created or empty
if file_empty:
header = ['Field Name', 'Field Description', 'Field Category', 'Source File Name']
writer.writerow(header)

# Iterate through rows: add them to the codebook and add them to the page
for line in self.content:

fields = line.split('|')

# Remove multi line long space sequences
for fid in range(len(fields)):
fields[fid] = " ".join(fields[fid].split())

# Append last fields
fields.append(doc_sensor)
fields.append(f"{doc_source_name}.py")

# Write to CSV
writer.writerow([field.strip() for field in fields])


# Prepare the documentation stylization
if len(fields) >= 2:
paragraph = nodes.paragraph()

# Create backtick formatting around the field name
field1 = nodes.literal('', '', nodes.Text(fields[0].strip()))

# Add the remainder of the line
colon_space = nodes.Text(': ')
field2 = nodes.Text(fields[1].strip())

# Add all the parts to the paragraph
paragraph += field1
paragraph += colon_space
paragraph += field2

# Add to the bullet point list
list_item = nodes.list_item()
list_item += paragraph
bullet_list += list_item

return [bullet_list]


def setup(app):
app.add_directive("codebookadd", CSVDocDirective)
2 changes: 1 addition & 1 deletion docs/examples/eeg_microstates/eeg_microstates.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Several different clustering algorithms can be used to segment your EEG recordings into microstates. These algorithms mainly differ in how they define cluster membership and the cost functionals to be optimized ([Xu & Tian, 2015](10.1007/s40745-015-0040-1)). The method to use hence depends on your data and the underlying assumptions of the methods (e.g., some methods ignore polarity). There is no one true method that gives the best results but you can refer to [Poulsen et al., 2018](https://www.researchgate.net/publication/331367421_Microstate_EEGlab_toolbox_An_introductory_guide#pf6) if you would like a more detailed review of the different clustering methods."
"Several different clustering algorithms can be used to segment your EEG recordings into microstates. These algorithms mainly differ in how they define cluster membership and the cost functionals to be optimized ([Xu & Tian, 2015](https://doi.org/10.1007/s40745-015-0040-1)). The method to use hence depends on your data and the underlying assumptions of the methods (e.g., some methods ignore polarity). There is no one true method that gives the best results but you can refer to [Poulsen et al., 2018](https://www.researchgate.net/publication/331367421_Microstate_EEGlab_toolbox_An_introductory_guide#pf6) if you would like a more detailed review of the different clustering methods."
]
},
{
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ You can navigate to the different sections using the left panel. We recommend ch
installation
authors
cite_us
codebook
examples/index
functions/index
resources/index
Expand Down
4 changes: 2 additions & 2 deletions docs/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ REM Command file for Sphinx documentation
@REM SPHINXBUILD="D:\Python3\Scripts\sphinx-build.exe"

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD==python -m sphinx
set SPHINXBUILD=python -m sphinx
)
set SOURCEDIR="."
set BUILDDIR="_build"
Expand All @@ -34,4 +34,4 @@ goto end
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
popd
2 changes: 1 addition & 1 deletion neurokit2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from .video import *

# Info
__version__ = "0.2.9"
__version__ = "0.2.10"


# Maintainer info
Expand Down
38 changes: 20 additions & 18 deletions neurokit2/ecg/ecg_eventrelated.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,30 @@ def ecg_eventrelated(epochs, silent=False):
by the `Label` column (if not present, by the `Index` column). The analyzed features
consist of the following:
* ``ECG_Rate_Max``: the maximum heart rate after stimulus onset.
* ``ECG_Rate_Min``: the minimum heart rate after stimulus onset.
* ``ECG_Rate_Mean``: the mean heart rate after stimulus onset.
* ``ECG_Rate_SD``: the standard deviation of the heart rate after stimulus onset.
* ``ECG_Rate_Max_Time``: the time at which maximum heart rate occurs.
* ``ECG_Rate_Min_Time``: the time at which minimum heart rate occurs.
* ``ECG_Phase_Atrial``: indication of whether the onset of the event concurs with
respiratory systole (1) or diastole (0).
* ``ECG_Phase_Ventricular``: indication of whether the onset of the event concurs with
respiratory systole (1) or diastole (0).
* ``ECG_Phase_Atrial_Completion``: indication of the stage of the current cardiac (atrial)
phase (0 to 1) at the onset of the event.
* ``ECG_Phase_Ventricular_Completion``: indication of the stage of the current cardiac
(ventricular) phase (0 to 1) at the onset of the event.
.. codebookadd::
ECG_Rate_Max|The maximum heart rate after stimulus onset.
ECG_Rate_Min|The minimum heart rate after stimulus onset.
ECG_Rate_Mean|The mean heart rate after stimulus onset.
ECG_Rate_SD|The standard deviation of the heart rate after stimulus onset.
ECG_Rate_Max_Time|The time at which maximum heart rate occurs.
ECG_Rate_Min_Time|The time at which minimum heart rate occurs.
ECG_Phase_Atrial|Indication of whether the onset of the event concurs with \
respiratory systole (1) or diastole (0).
ECG_Phase_Ventricular|Indication of whether the onset of the event concurs with \
respiratory systole (1) or diastole (0).
ECG_Phase_Atrial_Completion|Indication of the stage of the current cardiac (atrial) \
phase (0 to 1) at the onset of the event.
ECG_Phase_Ventricular_Completion|Indication of the stage of the current cardiac \
(ventricular) phase (0 to 1) at the onset of the event.
We also include the following *experimental* features related to the parameters of a
quadratic model:
* ``ECG_Rate_Trend_Linear``: The parameter corresponding to the linear trend.
* ``ECG_Rate_Trend_Quadratic``: The parameter corresponding to the curvature.
* ``ECG_Rate_Trend_R2``: the quality of the quadratic model. If too low, the parameters
might not be reliable or meaningful.
.. codebookadd::
ECG_Rate_Trend_Linear|The parameter corresponding to the linear trend.
ECG_Rate_Trend_Quadratic|The parameter corresponding to the curvature.
ECG_Rate_Trend_R2|The quality of the quadratic model. If too low, the parameters \
might not be reliable or meaningful.
See Also
--------
Expand Down
Loading

0 comments on commit 45c9ad9

Please sign in to comment.