import itertools
import numpy as np
import pandas as pd
from .entropy_shannon import _entropy_freq
[docs]
def entropy_cumulativeresidual(signal, symbolize=None, show=False, freq=None):
"""**Cumulative residual entropy (CREn)**
The cumulative residual entropy is an alternative to the Shannon
differential entropy with several advantageous properties, such as non-negativity. The key idea
is to use the cumulative distribution (CDF) instead of the density function in Shannon's
entropy.
.. math::
CREn = -\\int_{0}^{\\infty} p(|X| > x) \\log_{2} p(|X| > x) dx
Similarly to :func:`Shannon entropy <entropy_shannon>` and :func:`Petrosian fractal dimension
<fractal_petrosian>`, different methods to transform continuous signals into discrete ones are
available. See :func:`complexity_symbolize` for details.
This function can be called either via ``entropy_cumulativeresidual()`` or ``complexity_cren()``.
Parameters
----------
signal : Union[list, np.array, pd.Series]
The signal (i.e., a time series) in the form of a vector of values.
symbolize : str
Method to convert a continuous signal input into a symbolic (discrete) signal. ``None`` by
default, which skips the process (and assumes the input is already discrete). See
:func:`complexity_symbolize` for details.
show : bool
If ``True``, will show the discrete the signal.
freq : np.array
Instead of a signal, a vector of probabilities can be provided.
Returns
-------
CREn : float
The cumulative residual entropy.
info : dict
A dictionary containing ``Values`` for each pair of events.
Examples
----------
.. ipython:: python
import neurokit2 as nk
signal = [1, 1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 3]
@savefig p_entropy_cumulativeresidual1.png scale=100%
cren, info = nk.entropy_cumulativeresidual(signal, show=True)
@suppress
plt.close()
.. ipython:: python
cren
References
-----------
* Rao, M., Chen, Y., Vemuri, B. C., & Wang, F. (2004). Cumulative residual entropy: a new
measure of information. IEEE transactions on Information Theory, 50(6), 1220-1228.
"""
# Sanity checks
if isinstance(signal, (np.ndarray, pd.DataFrame)) and signal.ndim > 1:
raise ValueError(
"Multidimensional inputs (e.g., matrices or multichannel data) are not supported yet."
)
if freq is None:
events, freq = _entropy_freq(signal, symbolize=symbolize, show=show)
freq = freq / np.sum(freq)
events, freq = zip(*sorted(zip(events, freq)))
# Get the CDF
cdf = {a: _ for a, _ in zip(events, np.cumsum(freq))}
terms = np.zeros(len(events))
for i, (a, b) in enumerate(_entropy_cumulativeresidual_pairwise(events)):
pgx = cdf[a]
term = (b - a) * pgx * np.log2(pgx)
terms[i] = term
return -np.nansum(terms), {"Values": terms, "Symbolization": symbolize}
# =============================================================================
# Utilities
# =============================================================================
def _entropy_cumulativeresidual_pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)