Skip to content

Commit

Permalink
Merge pull request #1017 from DerAndereJohannes/feature-codebook-docs
Browse files Browse the repository at this point in the history
[Feature] Codebook Generation from Documentation
  • Loading branch information
DominiqueMakowski committed Aug 13, 2024
2 parents 86f42cf + b37098f commit b99036c
Show file tree
Hide file tree
Showing 28 changed files with 471 additions and 235 deletions.
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 @@ -140,4 +141,4 @@ def find_version():
# 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_static_path = ["_static"]
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)
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
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
4 changes: 3 additions & 1 deletion neurokit2/ecg/ecg_intervalrelated.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ def ecg_intervalrelated(data, sampling_rate=1000):
DataFrame
A dataframe containing the analyzed ECG features. The analyzed features consist of the following:
* ``ECG_Rate_Mean``: the mean heart rate.
.. codebookadd::
ECG_Rate_Mean|The mean heart rate.
* ``ECG_HRV``: the different heart rate variability metrices.
See :func:`.hrv_summary()` docstrings for details.
Expand Down
45 changes: 23 additions & 22 deletions neurokit2/ecg/ecg_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,27 @@ def ecg_process(ecg_signal, sampling_rate=1000, method="neurokit"):
signals : DataFrame
A DataFrame of the same length as the ``ecg_signal`` containing the following columns:
* ``"ECG_Raw"``: the raw signal.
* ``"ECG_Clean"``: the cleaned signal.
* ``"ECG_Rate"``: heart rate interpolated between R-peaks.
* ``"ECG_Quality"``: the quality of the cleaned signal
* ``"ECG_R_Peaks"``: the R-peaks marked as "1" in a list of zeros.
* ``"ECG_P_Peaks"``: the P-peaks marked as "1" in a list of zeros
* ``"ECG_P_Onsets"``: the P-onsets marked as "1" in a list of zeros.
* ``"ECG_P_Offsets"``: the P-offsets marked as "1" in a list of zeros.
* ``"ECG_Q_Peaks"``: the Q-peaks marked as "1" in a list of zeros .
* ``"ECG_R_Onsets"``: the R-onsets marked as "1" in a list of zeros.
* ``"ECG_R_Offsets"``: the R-offsets marked as "1" in a list of zeros.
* ``"ECG_S_Peaks"``: the S-peaks marked as "1" in a list of zeros.
* ``"ECG_T_Peaks"``: the T-peaks marked as "1" in a list of zeros.
* ``"ECG_T_Onsets"``: the T-onsets marked as "1" in a list of zeros.
* ``"ECG_T_Offsets"``: the T-offsets marked as "1" in a list of zeros.
* ``"ECG_Phase_Atrial"``: cardiac phase, marked by "1" for systole and "0" for diastole.
* ``"ECG_Phase_Completion_Atrial"``: cardiac phase (atrial) completion, expressed in
percentage (from 0 to 1), representing the stage of the current cardiac phase.
* ``"ECG_Phase_Ventricular"``: cardiac phase, marked by "1" for systole and "0" for
diastole.
* ``"ECG_Phase_Completion_Ventricular"``: cardiac phase (ventricular) completion, expressed
in percentage (from 0 to 1), representing the stage of the current cardiac phase.
.. codebookadd::
ECG_Raw|The raw signal.
ECG_Clean|The cleaned signal.
ECG_Rate|Heart rate interpolated between R-peaks.
ECG_Quality|The quality of the cleaned signal.
ECG_R_Peaks|The R-peaks marked as "1" in a list of zeros.
ECG_R_Onsets|The R-onsets marked as "1" in a list of zeros.
ECG_R_Offsets|The R-offsets marked as "1" in a list of zeros.
ECG_P_Peaks|The P-peaks marked as "1" in a list of zeros.
ECG_P_Onsets|The P-onsets marked as "1" in a list of zeros.
ECG_P_Offsets|The P-offsets marked as "1" in a list of zeros.
ECG_Q_Peaks|The Q-peaks marked as "1" in a list of zeros.
ECG_S_Peaks|The S-peaks marked as "1" in a list of zeros.
ECG_T_Peaks|The T-peaks marked as "1" in a list of zeros.
ECG_T_Onsets|The T-onsets marked as "1" in a list of zeros.
ECG_T_Offsets|The T-offsets marked as "1" in a list of zeros.
ECG_Phase_Atrial|Cardiac phase, marked by "1" for systole and "0" for diastole.
ECG_Phase_Completion_Atrial|Cardiac phase (atrial) completion, expressed in \
percentage (from 0 to 1), representing the stage of the current cardiac phase.
ECG_Phase_Completion_Ventricular|Cardiac phase (ventricular) completion, expressed \
in percentage (from 0 to 1), representing the stage of the current cardiac phase.
rpeaks : dict
A dictionary containing the samples at which the R-peaks occur, accessible with the key
Expand Down Expand Up @@ -88,6 +87,8 @@ def ecg_process(ecg_signal, sampling_rate=1000, method="neurokit"):
@suppress
plt.close()
"""

# Sanitize and clean input
Expand Down
28 changes: 12 additions & 16 deletions neurokit2/eda/eda_eventrelated.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,18 @@ def eda_eventrelated(epochs, silent=False):
by the `Label` column (if not present, by the `Index` column). The analyzed features consist
the following:
* ``"EDA_SCR"``: indication of whether Skin Conductance Response (SCR) occurs following the event
(1 if an SCR onset is present and 0 if absent) and if so, its corresponding peak amplitude,
time of peak, rise and recovery time. If there is no occurrence of SCR, nans are displayed
for the below features.
* ``"EDA_Peak_Amplitude"``: the maximum amplitude of the phasic component of the signal.
* ``"SCR_Peak_Amplitude"``: the peak amplitude of the first SCR in each epoch.
* ``"SCR_Peak_Amplitude_Time"``: the timepoint of each first SCR peak amplitude.
* ``"SCR_RiseTime"``: the risetime of each first SCR i.e., the time it takes for SCR to
reach peak amplitude from onset.
* ``"SCR_RecoveryTime"``: the half-recovery time of each first SCR i.e., the time it takes
for SCR to decrease to half amplitude.
.. codebookadd::
EDA_SCR|indication of whether Skin Conductance Response (SCR) occurs following the \
event (1 if an SCR onset is present and 0 if absent) and if so, its corresponding \
peak amplitude, time of peak, rise and recovery time. If there is no occurrence \
of SCR, nans are displayed for the below features.
EDA_Peak_Amplitude|The maximum amplitude of the phasic component of the signal.
SCR_Peak_Amplitude|The peak amplitude of the first SCR in each epoch.
SCR_Peak_Amplitude_Time|The timepoint of each first SCR peak amplitude.
SCR_RiseTime|The risetime of each first SCR i.e., the time it takes for SCR to \
reach peak amplitude from onset.
SCR_RecoveryTime|The half-recovery time of each first SCR i.e., the time it takes \
for SCR to decrease to half amplitude.
See Also
--------
Expand Down
8 changes: 5 additions & 3 deletions neurokit2/eda/eda_intervalrelated.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ def eda_intervalrelated(data, sampling_rate=1000, **kwargs):
A dataframe containing the analyzed EDA features. The analyzed
features consist of the following:
* ``"SCR_Peaks_N"``: the number of occurrences of Skin Conductance Response (SCR).
* ``"SCR_Peaks_Amplitude_Mean"``: the mean amplitude of the SCR peak occurrences.
* ``"EDA_Tonic_SD"``: the mean amplitude of the SCR peak occurrences.
.. codebookadd::
SCR_Peaks_N|The number of occurrences of Skin Conductance Response (SCR).
SCR_Peaks_Amplitude_Mean|The mean amplitude of the SCR peak occurrences.
EDA_Tonic_SD|The mean amplitude of the SCR peak occurrences.
* ``"EDA_Sympathetic"``: see :func:`eda_sympathetic` (only computed if signal duration
> 64 sec).
* ``"EDA_Autocorrelation"``: see :func:`eda_autocor` (only computed if signal duration
Expand Down
Loading

0 comments on commit b99036c

Please sign in to comment.