# Source code for neurokit2.complexity.entropy_cumulativeresidual

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)