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

[Fix] complexity_coarsegraining(): fix method #892

Merged
merged 12 commits into from
Sep 22, 2023
40 changes: 28 additions & 12 deletions neurokit2/complexity/entropy_multiscale.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,12 @@ def entropy_multiscale(
if "delay" in kwargs:
kwargs.pop("delay")

# Parameters selection
# Default parameters
algorithm = entropy_sample
refined = False
coarsegraining = "nonoverlapping"

# Parameters adjustement for variants
if method in ["MSEn", "SampEn"]:
pass # The default arguments are good
elif method in ["MSApEn", "ApEn", "MSPEn", "PEn", "MSWPEn", "WPEn"]:
Expand Down Expand Up @@ -326,13 +328,9 @@ def entropy_multiscale(
info["Value"] = np.array(
[
_entropy_multiscale(
coarse=complexity_coarsegraining(
signal,
scale=scale,
method=coarsegraining,
show=False,
**kwargs,
),
signal,
scale=scale,
coarsegraining=coarsegraining,
algorithm=algorithm,
dimension=dimension,
tolerance=info["Tolerance"],
Expand Down Expand Up @@ -378,13 +376,31 @@ def _entropy_multiscale_plot(mse, info):
# =============================================================================
# Methods
# =============================================================================
def _entropy_multiscale(coarse, algorithm, dimension, tolerance, refined=False, **kwargs):
def _entropy_multiscale(
signal,
scale,
coarsegraining,
algorithm,
dimension,
tolerance,
refined=False,
**kwargs,
):
"""Wrapper function that works both on 1D and 2D coarse-grained (for composite)"""

# Get coarse-grained signal
coarse = complexity_coarsegraining(signal, scale=scale, method=coarsegraining)

# Get delay
delay = 1 # If non-overlapping
if coarsegraining in ["rolling", "interpolate"]:
delay = scale

# For 1D coarse-graining
if coarse.ndim == 1:
return algorithm(
coarse,
delay=1,
delay=delay,
dimension=dimension,
tolerance=tolerance,
**kwargs,
Expand All @@ -398,7 +414,7 @@ def _entropy_multiscale(coarse, algorithm, dimension, tolerance, refined=False,
[
algorithm(
coarse[i],
delay=1,
delay=delay,
dimension=dimension,
tolerance=tolerance,
**kwargs,
Expand All @@ -412,7 +428,7 @@ def _entropy_multiscale(coarse, algorithm, dimension, tolerance, refined=False,
[
_phi(
coarse[i],
delay=1,
delay=delay,
dimension=dimension,
tolerance=tolerance,
approximate=False,
Expand Down
32 changes: 24 additions & 8 deletions neurokit2/complexity/utils_complexity_coarsegraining.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage.filters

from ..signal import signal_interpolate
from .utils_complexity_embedding import complexity_embedding


def complexity_coarsegraining(signal, scale=2, method="nonoverlapping", show=False, **kwargs):
def complexity_coarsegraining(
signal, scale=2, method="nonoverlapping", show=False, **kwargs
):
"""**Coarse-graining of a signal**

The goal of coarse-graining is to represent the signal at a different "scale". The
Expand Down Expand Up @@ -184,11 +186,16 @@ def complexity_coarsegraining(signal, scale=2, method="nonoverlapping", show=Fal
# Relying on scipy is a fast alternative to:
# pd.Series(signal).rolling(window=scale).mean().values[scale-1::]
# https://stackoverflow.com/questions/13728392/moving-average-or-running-mean
coarse = scipy.ndimage.filters.uniform_filter1d(signal, size=scale, mode="nearest")
coarse = coarse[scale - 1 : :]
# coarse = scipy.ndimage.filters.uniform_filter1d(
# signal, size=scale, mode="nearest"
# )
# coarse = coarse[scale - 1 : :]
coarse = complexity_embedding(signal, dimension=scale, delay=1).mean(axis=1)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the coarsegraining procedure but it doesn't seem to have entirely solved the issue:

image

@hsyu001 let's just make sure that we have the same sample entropy results: would you mind computing SampEn with these parameters using your own algorithm:

signal = [1, 2, 3, 5, 3, 1, 2, 4, 5, 7, 3, 2, 6, 2, 4, 8, 2]
tol = 2

With NK, this gives:

nk.entropy_sample(signal, dimension=2, delay=3, tolerance=tol)
> (0.2831469172863898,
 {'Dimension': 2, 'Delay': 3, 'Tolerance': 2.0034572195207527})

This comment was marked as outdated.

This comment was marked as outdated.

This comment was marked as outdated.

This comment was marked as outdated.

This comment was marked as outdated.


elif method == "timeshift":
coarse = np.transpose(np.reshape(signal[: scale * (n // scale)], (n // scale, scale)))
coarse = np.transpose(
np.reshape(signal[: scale * (n // scale)], (n // scale, scale))
)

else:
raise ValueError("Unknown `method`: {}".format(method))
Expand All @@ -204,8 +211,15 @@ def complexity_coarsegraining(signal, scale=2, method="nonoverlapping", show=Fal
def _complexity_show(signal, coarse, method="nonoverlapping"):
plt.plot(signal, linewidth=1.5)
if method == "nonoverlapping":
plt.plot(np.linspace(0, len(signal), len(coarse)), coarse, color="red", linewidth=0.75)
plt.scatter(np.linspace(0, len(signal), len(coarse)), coarse, color="red", linewidth=0.5)
plt.plot(
np.linspace(0, len(signal), len(coarse)),
coarse,
color="red",
linewidth=0.75,
)
plt.scatter(
np.linspace(0, len(signal), len(coarse)), coarse, color="red", linewidth=0.5
)
elif method == "timeshift":
for i in range(len(coarse)):
plt.plot(
Expand All @@ -215,7 +229,9 @@ def _complexity_show(signal, coarse, method="nonoverlapping"):
linewidth=0.75,
)
else:
plt.plot(np.linspace(0, len(signal), len(coarse)), coarse, color="red", linewidth=1)
plt.plot(
np.linspace(0, len(signal), len(coarse)), coarse, color="red", linewidth=1
)
plt.title(f'Coarse-graining using method "{method}"')


Expand Down
Loading