Skip to content

Commit 3d5ecfc

Browse files
Merge pull request #776 from neuropsychology/dev
0.2.4
2 parents 8c56493 + 751e5ba commit 3d5ecfc

61 files changed

Lines changed: 2531 additions & 788 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AUTHORS.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Core contributors
3030
Contributors
3131
-------------
3232

33+
* `Gansheng Tan <https://github.com/GanshengT>`_ *(Washington University, USA)*
3334
* `Hung Pham <https://github.com/hungpham2511>`_ *(Eureka Robotics, Singapore)*
3435
* `Christopher Schölzel <https://github.com/CSchoel>`_ *(THM University of Applied Sciences, Germany)*
3536
* `Duy Le <https://github.com/duylp>`_ *(Hubble, Singapore)*
@@ -53,7 +54,7 @@ Contributors
5354
* `Marek Sokol <https://github.com/sokolmarek>`_ *(Faculty of Biomedical Engineering of the CTU in Prague, Czech Republic)*
5455

5556

56-
Thanks also to `Gansheng Tan <https://github.com/GanshengT>`_, `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>`_.
57+
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>`_.
5758

5859

5960

NEWS.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
News
22
=====
33

4+
0.2.4
5+
-------------------
6+
Fixes
7+
+++++++++++++
48

9+
* `eda_sympathetic()` has been reviewed: low-pass filter and resampling have been added to be in
10+
line with the original paper
11+
* `eda_findpeaks()` using methods proposed in nabian2018 is reviewed and improved. Differentiation
12+
has been added before smoothing. Skin conductance response criteria have been revised based on
13+
the original paper.
514

615

716

docs/functions/eda.rst

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@ Preprocessing
3131
"""""""""""""""""""""
3232
.. autofunction:: neurokit2.eda.eda_phasic
3333

34-
*eda_autocor()*
35-
"""""""""""""""
36-
.. autofunction:: neurokit2.eda.eda_autocor
37-
38-
*eda_changepoints()*
39-
"""""""""""""""""""""
40-
.. autofunction:: neurokit2.eda.eda_changepoints
41-
4234
*eda_peaks()*
4335
"""""""""""""""""""""
4436
.. autofunction:: neurokit2.eda.eda_peaks
@@ -47,9 +39,6 @@ Preprocessing
4739
"""""""""""""""""""""
4840
.. autofunction:: neurokit2.eda.eda_fixpeaks
4941

50-
*eda_sympathetic()*
51-
"""""""""""""""""""""
52-
.. autofunction:: neurokit2.eda.eda_sympathetic
5342

5443

5544
Analysis
@@ -62,6 +51,18 @@ Analysis
6251
"""""""""""""""""""""""""
6352
.. autofunction:: neurokit2.eda.eda_intervalrelated
6453

54+
*eda_sympathetic()*
55+
"""""""""""""""""""""
56+
.. autofunction:: neurokit2.eda.eda_sympathetic
57+
58+
*eda_autocor()*
59+
"""""""""""""""
60+
.. autofunction:: neurokit2.eda.eda_autocor
61+
62+
*eda_changepoints()*
63+
"""""""""""""""""""""
64+
.. autofunction:: neurokit2.eda.eda_changepoints
65+
6566

6667

6768
Miscellaneous

docs/functions/hrv.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,5 @@ Intervals
4646

4747
.. automodule:: neurokit2.hrv
4848
:members:
49-
:exclude-members: hrv, hrv_time, hrv_frequency, hrv_nonlinear, hrv_rqa, hrv_rsa
49+
:exclude-members: hrv, hrv_time, hrv_frequency, hrv_nonlinear, hrv_rqa, hrv_rsa, intervals_process, intervals_to_peaks
5050

docs/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Then, at the top of each of your Python script, you should be able to import the
2323

2424
.. code-block:: console
2525
26-
pip install https://github.com/neuropsychology/neurokit/zipball/dev
26+
pip install https://github.com/neuropsychology/neurokit/zipball/dev --upgrade
2727
2828
2929

docs/readme/README_examples.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# Setup matplotlib with Agg to run on server
1111
matplotlib.use("Agg")
1212
plt.rcParams["figure.figsize"] = (10, 6.5)
13+
plt.rcParams["savefig.facecolor"] = "white"
1314

1415
# =============================================================================
1516
# Quick Example

neurokit2/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from .video import *
3333

3434
# Info
35-
__version__ = "0.2.3"
35+
__version__ = "0.2.4"
3636

3737

3838
# Maintainer info

neurokit2/complexity/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .complexity_lyapunov import complexity_lyapunov
99
from .complexity_relativeroughness import complexity_relativeroughness
1010
from .complexity_rqa import complexity_rqa
11+
from .entropy_angular import entropy_angular
1112
from .entropy_approximate import entropy_approximate
1213
from .entropy_attention import entropy_attention
1314
from .entropy_bubble import entropy_bubble
@@ -155,6 +156,7 @@
155156
"complexity_dfa",
156157
"complexity_relativeroughness",
157158
"complexity_rqa",
159+
"entropy_angular",
158160
"entropy_maximum",
159161
"entropy_shannon",
160162
"entropy_shannon_joint",
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
import pandas as pd
4+
import scipy.stats
5+
6+
from .utils_complexity_embedding import complexity_embedding
7+
8+
9+
def entropy_angular(signal, delay=1, dimension=2, show=False, **kwargs):
10+
"""**Angular entropy (AngEn)**
11+
12+
The Angular Entropy (AngEn) is the name that we use in NeuroKit to refer to the complexity
13+
method described in Nardelli et al. (2022), referred as comEDA due to its application to EDA
14+
signal. The method comprises the following steps: 1) Phase space reconstruction, 2) Calculation
15+
of the angular distances between all the pairs of points in the phase space; 3) Computation of
16+
the probability density function (PDF) of the distances; 4) Quadratic Rényi entropy of the PDF.
17+
18+
Parameters
19+
----------
20+
signal : Union[list, np.array, pd.Series]
21+
The signal (i.e., a time series) in the form of a vector of values.
22+
delay : int
23+
Time delay (often denoted *Tau* :math:`\\tau`, sometimes referred to as *lag*) in samples.
24+
See :func:`complexity_delay` to estimate the optimal value for this parameter.
25+
dimension : int
26+
Embedding Dimension (*m*, sometimes referred to as *d* or *order*). See
27+
:func:`complexity_dimension` to estimate the optimal value for this parameter.
28+
**kwargs : optional
29+
Other arguments.
30+
31+
Returns
32+
--------
33+
angen : float
34+
The Angular Entropy (AngEn) of the signal.
35+
info : dict
36+
A dictionary containing additional information regarding the parameters used
37+
to compute the index.
38+
39+
See Also
40+
--------
41+
entropy_renyi
42+
43+
Examples
44+
----------
45+
.. ipython:: python
46+
47+
import neurokit2 as nk
48+
49+
# Simulate a Signal with Laplace Noise
50+
signal = nk.signal_simulate(duration=2, frequency=[5, 3], noise=0.1)
51+
52+
# Compute Angular Entropy
53+
@savefig p_entropy_angular1.png scale=100%
54+
angen, info = nk.entropy_angular(signal, delay=1, dimension=3, show=True)
55+
@suppress
56+
plt.close()
57+
58+
59+
References
60+
-----------
61+
* Nardelli, M., Greco, A., Sebastiani, L., & Scilingo, E. P. (2022). ComEDA: A new tool for
62+
stress assessment based on electrodermal activity. Computers in Biology and Medicine, 150,
63+
106144.
64+
65+
"""
66+
# Sanity checks
67+
if isinstance(signal, (np.ndarray, pd.DataFrame)) and signal.ndim > 1:
68+
raise ValueError(
69+
"Multidimensional inputs (e.g., matrices or multichannel data) are not supported yet."
70+
)
71+
72+
# 1. Phase space reconstruction (time-delay embeddings)
73+
embedded = complexity_embedding(signal, delay=delay, dimension=dimension)
74+
75+
# 2. Angular distances between all the pairs of points in the phase space
76+
angles = _angular_distance(embedded)
77+
78+
# 3. Compute the probability density function (PDF) of the upper triangular matrix
79+
bins, pdf = _kde_sturges(angles)
80+
81+
# 4. Apply the quadratic Rényi entropy to the PDF
82+
angen = -np.log2(np.sum(pdf**2))
83+
84+
# Normalize to the range [0, 1] by the log of the number of bins
85+
86+
# Note that in the paper (eq. 4 page 4) there is a minus sign, but adding it would give
87+
# negative values, plus the linked code does not seem to do that
88+
# https://github.com/NardelliM/ComEDA/blob/main/comEDA.m#L103
89+
angen = angen / np.log2(len(bins))
90+
91+
if show is True:
92+
# Plot the PDF as a bar chart
93+
plt.bar(bins[:-1], pdf, width=bins[1] - bins[0], align="edge", alpha=0.5)
94+
# Set the x-axis limits to the range of the data
95+
plt.xlim([np.min(angles), np.max(angles)])
96+
# Print titles
97+
plt.suptitle(f"Angular Entropy (AngEn) = {angen:.3f}")
98+
plt.title("Distribution of Angular Distances:")
99+
100+
return angen, {"bins": bins, "pdf": pdf}
101+
102+
103+
def _angular_distance(m):
104+
"""
105+
Compute angular distances between all the pairs of points.
106+
"""
107+
# Get index of upper triangular to avoid double counting
108+
idx = np.triu_indices(m.shape[0], k=1)
109+
110+
# compute the magnitude of each vector
111+
magnitudes = np.linalg.norm(m, axis=1)
112+
113+
# compute the dot product between all pairs of vectors using np.matmul function, which is
114+
# more efficient than np.dot for large matrices; and divide the dot product matrix by the
115+
# product of the magnitudes to get the cosine of the angle
116+
cos_angles = np.matmul(m, m.T)[idx] / np.outer(magnitudes, magnitudes)[idx]
117+
118+
# clip the cosine values to the range [-1, 1] to avoid any numerical errors and compute angles
119+
return np.arccos(np.clip(cos_angles, -1, 1))
120+
121+
122+
def _kde_sturges(x):
123+
"""
124+
Computes the PDF of a vector x using a kernel density estimator based on linear diffusion
125+
processes with a Gaussian kernel. The number of bins of the PDF is chosen applying the Sturges
126+
method.
127+
"""
128+
# Estimate the bandwidth
129+
iqr = np.percentile(x, 75) - np.percentile(x, 25)
130+
bandwidth = 0.9 * iqr / (len(x) ** 0.2)
131+
132+
# Compute the number of bins using the Sturges method
133+
nbins = int(np.ceil(np.log2(len(x)) + 1))
134+
135+
# Compute the bin edges
136+
bins = np.linspace(np.min(x), np.max(x), nbins + 1)
137+
138+
# Compute the kernel density estimate
139+
xi = (bins[:-1] + bins[1:]) / 2
140+
pdf = np.sum(
141+
scipy.stats.norm.pdf((xi.reshape(-1, 1) - x.reshape(1, -1)) / bandwidth), axis=1
142+
) / (len(x) * bandwidth)
143+
144+
# Normalize the PDF
145+
pdf = pdf / np.sum(pdf)
146+
147+
return bins, pdf

neurokit2/complexity/entropy_differential.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@ def entropy_differential(signal, base=2, **kwargs):
1717
----------
1818
signal : Union[list, np.array, pd.Series]
1919
The signal (i.e., a time series) in the form of a vector of values.
20-
20+
base: float
21+
The logarithmic base to use, defaults to ``2``, giving a unit in *bits*. Note that ``scipy.
22+
stats.entropy()`` uses Euler's number (``np.e``) as default (the natural logarithm), giving
23+
a measure of information expressed in *nats*.
2124
**kwargs : optional
2225
Other arguments passed to ``scipy.stats.differential_entropy()``.
2326
2427
Returns
2528
--------
2629
diffen : float
2730
The Differential entropy of the signal.
28-
base: float
29-
The logarithmic base to use, defaults to ``2``, giving a unit in *bits*. Note that ``scipy.
30-
stats.entropy()`` uses Euler's number (``np.e``) as default (the natural logarithm), giving
31-
a measure of information expressed in *nats*.
3231
info : dict
3332
A dictionary containing additional information regarding the parameters used
3433
to compute Differential entropy.

0 commit comments

Comments
 (0)