Core Information Theory Classes

This module contains the core data structures and main functions for information-theoretic analysis.

Classes

class driada.information.info_base.TimeSeries(data, discrete=None, ts_type=None, shuffle_mask=None, name=None)[source]

Bases: object

Single time series with automatic type detection and analysis capabilities.

Represents a univariate time series with automatic detection of whether it’s discrete (categorical, binary, count) or continuous (linear, circular). Provides methods for entropy calculation, mutual information estimation, filtering, and complexity analysis.

Parameters:
  • data (array-like) – 1D time series data.

  • discrete (bool, optional) – If provided, overrides automatic type detection. Legacy parameter for backward compatibility.

  • ts_type (TimeSeriesType or str, optional) – Explicit type specification. Can be a TimeSeriesType object or string: - ‘binary’: discrete binary data - ‘categorical’: discrete categorical data - ‘count’: discrete monotonic count data - ‘timeline’: discrete regularly spaced values - ‘linear’: continuous linear data - ‘circular’: continuous circular/angular data - ‘ambiguous’: ambiguous discrete data

  • shuffle_mask (array-like, optional) – Boolean mask indicating valid positions for shuffling operations. Used in significance testing.

  • name (str, optional) – Name of the time series (provides context for type detection).

data

The time series data as 1D numpy array.

Type:

ndarray

discrete

Whether the time series is discrete.

Type:

bool

type_info

Detailed type information including subtype and confidence.

Type:

TimeSeriesType

scdata

Min-max scaled data to [0,1].

Type:

ndarray

data_scale

Scaling factor used by MinMaxScaler.

Type:

float

copula_normal_data

Copula-normalized data (continuous only).

Type:

ndarray or None

int_data

Integer representation (discrete only).

Type:

ndarray or None

is_binary

True if discrete with exactly 2 unique values, False otherwise.

Type:

bool

bool_data

Boolean representation (binary discrete only).

Type:

ndarray or None

shuffle_mask

Boolean mask for valid shuffle positions.

Type:

ndarray

entropy

Cached entropy values for different downsampling factors.

Type:

dict

kdtree

Cached KD-tree for k-NN searches.

Type:

KDTree or None

kdtree_query

Cached k-NN query results.

Type:

tuple or None

get_entropy(ds=1)[source]

Compute entropy with optional downsampling.

get_kdtree()[source]

Get or build KD-tree for k-NN operations.

get_kdtree_query(k=5)[source]

Get k-nearest neighbors.

filter(method='gaussian', \*\*kwargs)[source]

Apply signal filtering and return new TimeSeries.

approximate_entropy(m=2, r=None)[source]

Calculate approximate entropy (continuous only).

Parameters:
Return type:

float

estimate_tau(**kwargs)[source]

Estimate optimal embedding delay via TDMI (continuous only).

estimate_embedding_dim(**kwargs)[source]

Estimate embedding dimension via FNN (continuous only).

recurrence_graph(**kwargs)[source]

Build recurrence graph with automatic embedding.

rqa(**kwargs)[source]

Compute recurrence quantification analysis measures.

visibility_graph(**kwargs)[source]

Build horizontal or natural visibility graph.

ordinal_partition_network(**kwargs)[source]

Build ordinal partition network from rank patterns.

permutation_entropy(**kwargs)[source]

Compute permutation entropy.

define_ts_type(ts)[source]

Legacy static method for type detection (deprecated).

Notes

  • Type detection uses statistical heuristics and can be overridden

  • Discrete data is stored as integers for efficient computation

  • Continuous data is copula-normalized for GCMI estimation

  • Entropy is computed in bits for consistency

Warning

This class is NOT thread-safe. The following attributes are lazily computed and cached, which can cause race conditions with concurrent access: - entropy dict (via get_entropy) - kdtree (via get_kdtree) - kdtree_query dict (via get_kdtree_query)

For concurrent usage, ensure proper synchronization or use separate TimeSeries instances per thread.

Examples

>>> import numpy as np
>>> # Continuous time series
>>> ts = TimeSeries(np.random.randn(1000))
>>> print(ts.discrete)
False
>>> entropy = ts.get_entropy()
>>>
>>> # Binary discrete time series
>>> ts_binary = TimeSeries([0, 1, 0, 1, 1, 0], ts_type='binary')
>>> print(ts_binary.is_binary)
True
>>>
>>> # With shuffle mask
>>> data = np.sin(np.linspace(0, 10*np.pi, 1000))
>>> mask = np.ones(1000, dtype=bool)
>>> mask[:100] = False  # Invalid positions at start
>>> ts_masked = TimeSeries(data, shuffle_mask=mask)
static define_ts_type(ts)[source]

Legacy method for backward compatibility. Use is_discrete_time_series instead.

Deprecated since version 2.0: Use driada.information.time_series_types.is_discrete_time_series() instead.

Attempts to determine if a time series is discrete or continuous using simple heuristics based on unique value ratio. This method is maintained only for backward compatibility and may be removed in future versions.

Parameters:

ts (array-like) – Time series data to analyze.

Returns:

True if likely discrete, False if likely continuous.

Return type:

bool

Warns:
  • DeprecationWarning – Always emitted when this method is called.

  • UserWarning – If time series is too short (<100 samples) or type is ambiguous.

Notes

The legacy heuristic uses uniqueness ratio (n_unique/n_samples): - < 0.25: classified as discrete - > 0.70: classified as continuous - Between: ambiguous, defaults to continuous

The new type detection system is much more sophisticated and accurate.

__init__(data, discrete=None, ts_type=None, shuffle_mask=None, name=None)[source]

Initialize TimeSeries object.

Parameters:
  • data (array-like) – Time series data

  • discrete (bool, optional) – Legacy parameter for backward compatibility. If provided, overrides auto-detection.

  • ts_type (TimeSeriesType or str, optional) – Full type specification or a string matching existing subtypes: - ‘binary’: discrete binary data - ‘categorical’: discrete categorical data - ‘count’: discrete monotonic count data - ‘timeline’: discrete regularly spaced values - ‘linear’: continuous linear data - ‘circular’: continuous circular/angular data - ‘ambiguous’: ambiguous discrete data

  • shuffle_mask (array-like, optional) – Mask for valid shuffling positions

  • name (str, optional) – Name of the time series (used for context in type detection)

Raises:

ValueError – If data is not a numpy array, not 1D, has less than 2 points, contains NaN or infinite values, or if shuffle_mask has invalid length or values.

clear_caches()[source]

Clear cached data to free memory in batch processing.

get_kdtree()[source]

Get or build KDTree for efficient nearest neighbor queries.

Lazily constructs a KDTree from the time series data on first access and caches it for subsequent calls. The tree is built using the reshaped data (flattened to 2D).

Returns:

KDTree structure for the time series data. Built using the build_tree function from ksg module.

Return type:

sklearn.neighbors.KDTree

Notes

The KDTree is cached in self.kdtree after first construction. Data is reshaped to (n_samples, -1) before tree construction to handle multi-dimensional time series.

See also

get_kdtree_query

Query the KDTree for k-nearest neighbors.

get_kdtree_query(k=5)[source]

Query KDTree for k-nearest neighbors of each point.

Performs k-nearest neighbor search for all points in the time series data. Results are cached for efficiency when called multiple times with the same k value.

Parameters:

k (int, default=5) – Number of nearest neighbors to find for each point. The default value is DEFAULT_NN (5). Note that the query returns k+1 neighbors since each point is its own nearest neighbor.

Returns:

distancesndarray of shape (n_samples, k+1)

Distances to the k+1 nearest neighbors for each point.

indicesndarray of shape (n_samples, k+1)

Indices of the k+1 nearest neighbors for each point.

Return type:

tuple of (distances, indices)

Notes

The query includes each point as its own nearest neighbor (distance 0), so k+1 neighbors are returned. Results are cached for each k value in self.kdtree_query dictionary.

See also

get_kdtree

Build or retrieve the KDTree structure.

get_entropy(ds=1)[source]

Calculate entropy of the time series.

Computes Shannon entropy for the time series data, using appropriate methods for discrete vs continuous variables. Results are cached by downsampling factor.

Parameters:

ds (int, default=1) – Downsampling factor. Data is subsampled by taking every ds-th sample before entropy calculation. Must be positive integer.

Returns:

Entropy value in bits. For discrete variables, uses discrete entropy calculation. For continuous variables, uses non-parametric KSG entropy estimation.

Return type:

float

Raises:

ValueError – If ds is not a positive integer or if ds >= len(data).

Notes

  • For discrete data: Uses entropy_d which directly returns bits.

  • For continuous data: Uses nonparam_entropy_c with base=2 to get bits.

  • Results are cached in self.entropy dict keyed by ds value.

  • Downsampling is applied as data[::ds] for both discrete and continuous data.

See also

entropy_d

Discrete entropy calculation.

nonparam_entropy_c

Continuous entropy estimation using KSG method.

filter(method='gaussian', **kwargs)[source]

Apply filtering to the time series and return a new filtered TimeSeries.

Parameters:
  • method (str) – Filtering method: ‘gaussian’, ‘savgol’, ‘wavelet’, or ‘none’

  • **kwargs – Method-specific parameters: - gaussian: sigma (default: 1.0) - savgol: window_length (default: 5), polyorder (default: 2) - wavelet: wavelet (default: ‘db4’), level (default: None)

Returns:

New TimeSeries object with filtered data

Return type:

TimeSeries

approximate_entropy(m=2, r=None)[source]

Calculate approximate entropy (ApEn) of the time series.

Approximate entropy is a regularity statistic that quantifies the unpredictability of fluctuations in a time series. A time series containing many repetitive patterns has a relatively small ApEn; a less predictable process has a higher ApEn.

Parameters:
  • m (int, optional) – Pattern length. Common values are 1 or 2. Default is 2.

  • r (float, optional) – Tolerance threshold for pattern matching. If None, defaults to 0.2 times the standard deviation of the data.

Returns:

The approximate entropy value. Higher values indicate more randomness/complexity.

Return type:

float

Raises:

ValueError – If called on a discrete TimeSeries.

Notes

This method is only valid for continuous time series. For discrete time series, consider using other complexity measures.

Examples

>>> np.random.seed(42)
>>> ts = TimeSeries(np.random.randn(1000), discrete=False)
>>> apen = ts.approximate_entropy(m=2)
>>> print(f"Approximate entropy: {apen:.3f}")
Approximate entropy: 1.637
estimate_tau(**kwargs)[source]

Estimate optimal embedding delay. See information.recurrence.estimate_tau.

estimate_embedding_dim(**kwargs)[source]

Estimate embedding dimension via FNN. See information.recurrence.estimate_embedding_dim.

takens_embedding(**kwargs)[source]

Compute Takens delay embedding. See information.recurrence.takens_embedding.

recurrence_graph(**kwargs)[source]

Build recurrence graph. See information.recurrence.recurrence_graph.

rqa(**kwargs)[source]

Compute RQA measures. See information.recurrence.rqa.

visibility_graph(**kwargs)[source]

Build visibility graph. See information.recurrence.visibility_graph.

ordinal_partition_network(**kwargs)[source]

Build ordinal partition network. See information.recurrence.ordinal_partition_network.

permutation_entropy(**kwargs)[source]

Compute permutation entropy. See information.recurrence.permutation_entropy.

class driada.information.info_base.MultiTimeSeries(data_or_tslist, labels=None, distmat=None, rescale_rows=False, data_name=None, downsampling=None, discrete=None, shuffle_mask=None, allow_zero_columns=False, name=None)[source]

Bases: MVData

Multiple aligned time series with dimensionality reduction capabilities.

Represents a collection of aligned univariate time series, all of the same type (either all continuous or all discrete). Inherits from MVData to enable direct application of dimensionality reduction methods.

Parameters:
  • data_or_tslist (ndarray or list of TimeSeries) – Either: - 2D numpy array of shape (n_series, n_timepoints) - List of TimeSeries objects (must all be same type)

  • labels (array-like, optional) – Labels for dimensionality reduction visualization.

  • distmat (array-like, optional) – Precomputed distance matrix.

  • rescale_rows (bool, default=False) – Whether to rescale each time series to [0, 1].

  • data_name (str, optional) – Name for the dataset.

  • downsampling (int, optional) – Downsampling factor.

  • discrete (bool, optional) – Required when passing numpy array. Specifies if all series are discrete.

  • shuffle_mask (array-like, optional) – Boolean mask for valid shuffle positions. If not provided, combines individual TimeSeries masks using AND operation.

  • allow_zero_columns (bool, default=False) – Whether to allow time points where all series are zero.

data

Stacked time series data of shape (n_series, n_timepoints).

Type:

ndarray

discrete

Whether all time series are discrete.

Type:

bool

scdata

Min-max scaled data for each series.

Type:

ndarray

copula_normal_data

Copula-normalized data (continuous only).

Type:

ndarray or None

int_data

Integer representation (discrete only).

Type:

ndarray or None

shuffle_mask

Combined shuffle mask for all series.

Type:

ndarray

entropy

Cached joint entropy values for different downsampling factors.

Type:

dict

shape

Shape of the data array.

Type:

tuple

Plus all attributes from MVData parent class.
get_entropy(ds=1)[source]

Compute joint entropy with optional downsampling.

filter(method='gaussian', \*\*kwargs)[source]

Apply filtering to all series and return new MultiTimeSeries.

Plus all methods from MVData parent class (get_embedding, etc.).
Raises:

ValueError – If mixing continuous and discrete TimeSeries. If TimeSeries have different lengths. If combined shuffle_mask has no valid positions. If data is not 2D when providing numpy array. If discrete parameter is not specified when providing numpy array.

Notes

  • All component time series must be of the same type (continuous/discrete)

  • Shuffle masks are combined restrictively (AND operation)

  • Joint entropy is computed for up to 2 discrete variables

  • Inherits dimensionality reduction capabilities from MVData

Examples

>>> import numpy as np
>>> # From numpy array
>>> data = np.random.randn(10, 1000)  # 10 time series, 1000 points each
>>> mts = MultiTimeSeries(data, discrete=False)
>>>
>>> # From list of TimeSeries
>>> ts_list = [TimeSeries(np.random.randn(1000)) for _ in range(5)]
>>> mts = MultiTimeSeries(ts_list)
>>>
>>> # Apply dimensionality reduction
>>> embedding = mts.get_embedding(method='pca', dim=2)  
Calculating PCA embedding...
>>>
>>> # Filter all time series
>>> mts_filtered = mts.filter(method='gaussian', sigma=2.0)
__init__(data_or_tslist, labels=None, distmat=None, rescale_rows=False, data_name=None, downsampling=None, discrete=None, shuffle_mask=None, allow_zero_columns=False, name=None)[source]

Initialize MultiTimeSeries object.

Parameters:
  • data_or_tslist (ndarray or list of TimeSeries) – Either a 2D numpy array of shape (n_series, n_timepoints) or a list of TimeSeries objects (must all be same type)

  • labels (array-like, optional) – Labels for dimensionality reduction visualization

  • distmat (array-like, optional) – Precomputed distance matrix

  • rescale_rows (bool, default=False) – Whether to rescale each time series to [0, 1]

  • data_name (str, optional) – Name for the dataset

  • downsampling (int, optional) – Downsampling factor

  • discrete (bool, optional) – Required when passing numpy array. Specifies if all series are discrete

  • shuffle_mask (array-like, optional) – Boolean mask for valid shuffle positions

  • allow_zero_columns (bool, default=False) – Whether to allow time points where all series are zero

  • name (str, optional) – Name for the MultiTimeSeries. If provided and components lack names, they will be auto-named as “{name}_{i}”.

Notes

This method handles both numpy array and list of TimeSeries inputs, validates compatibility, combines data and metadata, and initializes the parent MVData class for dimensionality reduction capabilities.

If a name is provided, components without names will automatically be named “{name}_{i}” (e.g., “position_2d_0”, “position_2d_1”).

clear_caches()[source]

Clear cached data to free memory in batch processing.

property shape

Return shape of the data for compatibility with numpy-like access.

Returns:

Shape as (n_variables, n_timepoints).

Return type:

tuple of int

get_entropy(ds=1)[source]

Calculate joint entropy of the multivariate time series.

Computes joint Shannon entropy for multivariate data, using appropriate methods based on whether the variables are discrete or continuous. Results are cached by downsampling factor.

Parameters:

ds (int, default=1) – Downsampling factor. Data is subsampled by taking every ds-th sample before entropy calculation. Must be positive integer.

Returns:

Joint entropy value in bits. The method used depends on data type:

  • Discrete data: Uses entropy_d for single variable, joint_entropy_dd for 2 variables. More than 2 discrete variables not yet supported.

  • Continuous data: Uses Gaussian copula entropy estimation (ent_g) for any number of variables.

Return type:

float

Raises:

NotImplementedError – If attempting to compute joint entropy for more than 2 discrete variables.

Notes

  • Results are cached in self.entropy dict keyed by ds value.

  • Downsampling is applied as data[:, ::ds] before computation.

  • For continuous multivariate data, uses copula-based entropy estimation which is more efficient than KSG for multiple variables.

See also

entropy_d

Single discrete variable entropy.

joint_entropy_dd

Joint entropy for 2 discrete variables.

ent_g

Gaussian copula entropy for continuous multivariate data.

filter(method='gaussian', **kwargs)[source]

Apply filtering to all time series components and return a new filtered MultiTimeSeries.

Parameters:
  • method (str) – Filtering method: ‘gaussian’, ‘savgol’, ‘wavelet’, or ‘none’

  • **kwargs – Method-specific parameters (see TimeSeries.filter for details)

Returns:

New MultiTimeSeries object with all components filtered

Return type:

MultiTimeSeries

population_recurrence_graph(**kwargs)[source]

Build population recurrence graph. See information.recurrence.population_recurrence_graph.

Main Functions

driada.information.info_base.get_mi(x, y, shift=0, ds=1, k=5, estimator='gcmi', check_for_coincidence=False, mi_estimator_kwargs=None)[source]

Compute mutual information between two (possibly multidimensional) variables.

Efficiently calculates mutual information (MI) between continuous, discrete, or mixed-type variables. Supports both univariate and multivariate inputs, with time-shifted analysis capabilities for temporal dependencies.

Parameters:
  • x (TimeSeries, MultiTimeSeries, or array-like) – First variable. Can be: - TimeSeries: univariate time series (continuous or discrete) - MultiTimeSeries: multivariate time series - array-like: converted to TimeSeries internally

  • y (TimeSeries, MultiTimeSeries, or array-like) – Second variable. Must have same length as x.

  • shift (int, default=0) – Number of samples to shift y after downsampling. Positive values shift y forward in time (y leads x). Used for time-delayed MI.

  • ds (int, default=1) – Downsampling factor. Takes every ds-th sample to reduce computation. Note: for GCMI with ds>1, copula transform is applied before downsampling which may affect accuracy for large ds or non-smooth signals.

  • k (int, default=5) – Number of nearest neighbors for KSG estimator. Common values: - k=4-5: optimal for most applications - k=3-10: for low dimensions (d≤3) - k=10-20: for higher dimensions

  • estimator ({'gcmi', 'ksg'}, default='gcmi') – MI estimation method: - ‘gcmi’: Gaussian Copula MI (fast, gives lower bound) - ‘ksg’: Kraskov-Stögbauer-Grassberger (slower, more accurate)

  • check_for_coincidence (bool, default=False) – If True, checks for MI(X,X) computation and handles appropriately: - For discrete single TimeSeries: returns H(X) (well-defined) - For continuous variables: raises ValueError (MI would be infinite) - For discrete MultiTimeSeries: raises NotImplementedError (not yet supported) Set to False to bypass this check (use with caution).

  • mi_estimator_kwargs (dict, optional) – Additional keyword arguments passed to the MI estimator function.

Returns:

Mutual information in bits. Always non-negative (clipped at 0). For GCMI, this is a lower bound on the true MI.

Return type:

float

Notes

The function automatically handles different variable type combinations: - Continuous-Continuous: Uses GCMI or KSG as specified - Discrete-Discrete: Uses exact MI computation (same for both estimators) - Mixed (Continuous-Discrete): Uses appropriate mixed estimators - Multivariate: Supported for continuous variables only

For discrete-discrete MI, the estimator parameter is ignored since MI can be computed exactly from the joint probability distribution.

GCMI is recommended for most applications as it’s much faster and provides a useful lower bound. KSG is more accurate but computationally expensive, especially for large datasets.

Examples

>>> # Simple correlation detection
>>> np.random.seed(42)
>>> x = np.random.randn(1000)
>>> y = x + np.random.randn(1000) * 0.5
>>> mi = get_mi(x, y)
>>> print(f"MI = {mi:.3f} bits")
MI = 1.114 bits
>>> # Time-delayed mutual information
>>> ts1 = TimeSeries(np.sin(np.linspace(0, 10*np.pi, 1000)))
>>> ts2 = TimeSeries(np.sin(np.linspace(0, 10*np.pi, 1000) + np.pi/4))
>>> mi_delay = get_mi(ts1, ts2, shift=25)  # Check 25-sample delay
>>> # Multivariate MI
>>> mts1 = MultiTimeSeries(np.random.randn(3, 1000), discrete=False)
>>> mts2 = MultiTimeSeries(np.random.randn(2, 1000), discrete=False)
>>> mi_multi = get_mi(mts1, mts2)

See also

get_1d_mi

MI for univariate time series (called internally)

get_multi_mi

MI between multiple and single time series

get_tdmi

Time-delayed MI for finding optimal embedding delays

conditional_mi

Conditional mutual information I(X;Y|Z)

driada.information.info_base.get_1d_mi(ts1, ts2, shift=0, ds=1, k=5, estimator='gcmi', check_for_coincidence=True, mi_estimator_kwargs=None)[source]

Computes mutual information between two 1d variables efficiently

Parameters:
  • ts1 (TimeSeries/MultiTimeSeries instance or numpy array) – First time series or variable

  • ts2 (TimeSeries/MultiTimeSeries instance or numpy array) – Second time series or variable

  • shift (int, default=0) – ts2 will be roll-moved by the number ‘shift’ after downsampling by ‘ds’ factor

  • ds (int, default=1) – downsampling constant (take every ‘ds’-th point)

  • k (int, default=5) – number of neighbors for ksg estimator

  • estimator (str, default='gcmi') –

    Estimation method. Should be ‘ksg’ (accurate but slow) and ‘gcmi’ (fast, but estimates the lower bound on MI). In most cases ‘gcmi’ should be preferred.

    Note on downsampling with GCMI: For performance reasons, when ds > 1, the copula transformation is applied to the full data before downsampling. This is an approximation that works well for small downsampling factors (ds ≤ 5) and smooth signals, but may introduce inaccuracies for large downsampling factors or highly variable signals.

  • check_for_coincidence (bool, default=True) – If True, checks for MI(X,X) computation at zero shift: - For discrete variables: returns H(X) - For continuous variables: raises ValueError (MI is infinite) Default: True.

  • mi_estimator_kwargs (dict, optional) – Additional keyword arguments passed to the MI estimator function.

Returns:

mi – Mutual information in bits (or its lower bound in case of ‘gcmi’ estimator) between ts1 and (possibly) shifted ts2. Both estimators return values in bits.

Return type:

float

driada.information.info_base.get_multi_mi(tslist, ts2, shift=0, ds=1, k=5, estimator='gcmi', mi_estimator_kwargs=None)[source]

Compute mutual information between multiple time series and a single time series.

Parameters:
  • tslist (list of TimeSeries) – List of TimeSeries objects (multivariate X)

  • ts2 (TimeSeries) – Single TimeSeries object (Y)

  • shift (int, optional) – Number of samples to shift ts2. Default: 0

  • ds (int, optional) – Downsampling factor. Default: 1

  • k (int, optional) – Number of neighbors for KSG estimator. Default: 5

  • estimator (str, optional) –

    Estimation method. ‘gcmi’ (fast, lower bound) or ‘ksg’ (slower, more accurate). Default: ‘gcmi’

    Note on downsampling with GCMI: For performance reasons, when ds > 1, the copula transformation is applied to the full data before downsampling. This is an approximation that works well for small downsampling factors (ds ≤ 5) and smooth signals, but may introduce inaccuracies for large downsampling factors or highly variable signals.

  • mi_estimator_kwargs (dict, optional) – Additional keyword arguments passed to the MI estimator function.

Returns:

Mutual information I(X;Y) in bits where X is the multivariate input from tslist

Return type:

float

driada.information.info_base.get_tdmi(data, min_shift=1, max_shift=100, nn=5, estimator='gcmi')[source]

Compute time-delayed mutual information (TDMI) for a time series.

Calculates mutual information between a time series and delayed versions of itself across a range of time lags. Useful for detecting temporal dependencies and optimal embedding delays.

Parameters:
  • data (array-like) – 1D time series data.

  • min_shift (int, optional) – Minimum time lag to compute. Default: 1.

  • max_shift (int, optional) – Maximum time lag to compute (exclusive). Default: 100.

  • nn (int, optional) – Number of nearest neighbors for KSG MI estimation. Only used when estimator=’ksg’. Default: 5.

  • estimator ({'gcmi', 'ksg'}, optional) – MI estimator to use. ‘gcmi’ is faster but provides a lower bound, ‘ksg’ is more accurate but slower. Default: ‘gcmi’.

Returns:

TDMI values in bits for each time lag from min_shift to max_shift-1.

Return type:

list of float

Notes

  • The first minimum in TDMI often indicates optimal embedding delay

  • High TDMI at specific lags indicates periodic structure

  • All values are returned in bits for consistency

  • For long time series, ‘gcmi’ is recommended for speed

  • For precise embedding delay detection, ‘ksg’ may be more accurate

Examples

>>> data = np.sin(np.linspace(0, 10*np.pi, 1000))
>>> tdmi = get_tdmi(data, min_shift=1, max_shift=50)
>>> optimal_delay = np.argmin(tdmi) + 1  # First minimum
>>>
>>> # Using KSG for more accuracy
>>> tdmi_ksg = get_tdmi(data, min_shift=1, max_shift=50, estimator='ksg')
driada.information.info_base.conditional_mi(ts1, ts2, ts3, ds=1, k=5)[source]

Calculate conditional mutual information I(X;Y|Z).

Computes the conditional mutual information between ts1 (X) and ts2 (Y) given ts3 (Z) for various combinations of continuous and discrete variables.

Parameters:
  • ts1 (TimeSeries) – First variable (X). Must be continuous.

  • ts2 (TimeSeries) – Second variable (Y). Can be continuous or discrete.

  • ts3 (TimeSeries) – Conditioning variable (Z). Can be continuous or discrete.

  • ds (int, optional) – Downsampling factor. Default: 1.

  • k (int, optional) – Number of neighbors for entropy estimation. Default: 5.

Returns:

Conditional mutual information I(X;Y|Z) in bits.

Return type:

float

Raises:

ValueError – If ts1 is discrete (only continuous X is currently supported).

Notes

Supports four cases: - CCC: All continuous - uses Gaussian copula - CCD: X,Y continuous, Z discrete - uses Gaussian copula per Z value - CDC: X,Z continuous, Y discrete - uses chain rule identity - CDD: X continuous, Y,Z discrete - uses entropy decomposition

For the CDD case, GCMI estimator has limitations due to uncontrollable biases (copula transform does not conserve entropy). See https://doi.org/10.1002/hbm.23471 for details.

Conditional MI can be negative due to estimation biases, especially with finite samples. This function uses adaptive thresholds: - Small negatives (< 1% of entropy scale): Silently clipped to 0 - Moderate negatives (1-10% of scale): Clipped with warning - Large negatives (> 10% of scale): Raises ValueError

The CDD case is particularly prone to negative biases due to mixed estimators and receives more lenient treatment.

driada.information.info_base.interaction_information(ts1, ts2, ts3, ds=1, k=5)[source]

Calculate three-way interaction information II(X;Y;Z).

The interaction information quantifies the amount of information that is shared among all three variables. It can be positive (synergy) or negative (redundancy).

Parameters:
  • ts1 (TimeSeries) – First variable (X). Must be continuous.

  • ts2 (TimeSeries) – Second variable (Y). Can be continuous or discrete.

  • ts3 (TimeSeries) – Third variable (Z). Can be continuous or discrete.

  • ds (int, optional) – Downsampling factor. Default: 1.

  • k (int, optional) – Number of neighbors for entropy estimation. Default: 5.

Returns:

Interaction information II(X;Y;Z) in bits. - II < 0: Redundancy (Y and Z provide overlapping information about X) - II > 0: Synergy (Y and Z together provide more information than separately)

Return type:

float

Notes

The interaction information is computed using Williams & Beer convention: II(X;Y;Z) = I(X;Y|Z) - I(X;Y) = I(X;Z|Y) - I(X;Z)

This implementation assumes X is the target variable (e.g., neural activity) and Y, Z are predictor variables (e.g., behavioral features).

Usage Examples

TimeSeries Objects

from driada.information import TimeSeries
import numpy as np

# Discrete time series (e.g., spike counts)
spike_counts = np.random.poisson(2, 1000)
ts_discrete = TimeSeries(spike_counts, discrete=True)

# Continuous time series (e.g., LFP)
lfp_signal = np.random.randn(1000)
ts_continuous = TimeSeries(lfp_signal, discrete=False)

# Access properties
print(f"Is discrete: {ts_discrete.discrete}")
print(f"Data shape: {ts_discrete.data.shape}")
print(f"Data length: {ts_discrete.data.shape[0]}")

MultiTimeSeries Objects

from driada.information import MultiTimeSeries

# Multiple neural recordings
neural_data = np.random.randn(50, 10000)  # 50 neurons, 10000 timepoints
mts = MultiTimeSeries(neural_data, discrete=False)

# Properties
print(f"Number of series: {mts.n_dim}")  # n_dim = number of rows/series
print(f"Number of timepoints: {mts.n_points}")  # n_points = number of columns/timepoints
print(f"Data shape: {mts.data.shape}")  # Access numpy array shape directly

# Access individual series data
neuron_5_data = mts.data[5, :]  # Returns numpy array for neuron 5

Basic Mutual Information

from driada.information import get_mi

# MI between two continuous variables
x = TimeSeries(np.random.randn(1000), discrete=False)
y = TimeSeries(np.random.randn(1000), discrete=False)

mi_gcmi = get_mi(x, y, estimator='gcmi')
print(f"MI (GCMI): {mi_gcmi:.3f} bits")

# MI between discrete variables
x_discrete = TimeSeries(np.random.randint(0, 5, 1000), discrete=True)
y_discrete = TimeSeries(np.random.randint(0, 3, 1000), discrete=True)

mi_discrete = get_mi(x_discrete, y_discrete)  # Default estimator handles discrete data
print(f"MI (discrete): {mi_discrete:.3f} bits")

Conditional Mutual Information

from driada.information import conditional_mi

# CMI: I(X;Y|Z)
x = TimeSeries(np.random.randn(1000), discrete=False)
y = TimeSeries(np.random.randn(1000), discrete=False)
z = TimeSeries(np.random.randn(1000), discrete=False)

cmi = conditional_mi(x, y, z)
print(f"I(X;Y|Z) = {cmi:.3f} bits")

Interaction Information

from driada.information import interaction_information

# Three-way interaction
x = TimeSeries(np.random.randn(1000), discrete=False)
y = TimeSeries(np.random.randn(1000), discrete=False)
z = TimeSeries(np.random.randn(1000), discrete=False)

ii = interaction_information(x, y, z)
print(f"Interaction information: {ii:.3f} bits")

# Interpretation:
# ii > 0: Synergy (variables together provide more info)
# ii < 0: Redundancy (variables share information)

Advanced Usage

Time-lagged MI

from driada.information import get_tdmi

# Compute time-delayed mutual information
signal = np.random.randn(1000)

# Calculate TDMI for lags 1 to 50
tdmi_values = get_tdmi(signal, min_shift=1, max_shift=50)

# Find optimal embedding delay (first local minimum)
from scipy.signal import argrelmin
minima = argrelmin(np.array(tdmi_values))[0]
if len(minima) > 0:
    optimal_delay = minima[0] + 1  # +1 because min_shift=1

# Plot TDMI curve
import matplotlib.pyplot as plt
plt.plot(range(1, 50), tdmi_values)
plt.xlabel('Time lag')
plt.ylabel('TDMI (bits)')

Multivariate MI

# MI between groups of variables
group1 = MultiTimeSeries(np.random.randn(5, 1000), discrete=False)
group2 = MultiTimeSeries(np.random.randn(3, 1000), discrete=False)

mi_groups = get_mi(group1, group2, estimator='gcmi')
print(f"MI between groups: {mi_groups:.3f} bits")

Estimator Selection

# Compare different estimators
x = TimeSeries(np.random.randn(1000), discrete=False)
y = TimeSeries(np.random.randn(1000), discrete=False)

estimators = ['gcmi', 'ksg']  # Both work for continuous data

for est in estimators:
    mi = get_mi(x, y, estimator=est)
    print(f"{est}: {mi:.3f} bits")

Best Practices

  1. Data Preparation: - Ensure time series are aligned - Remove artifacts before analysis - Consider normalization for continuous data

  2. Estimator Choice: - gcmi: Fast, good for continuous Gaussian-like data - ksg: Non-parametric, works for any continuous distribution - discrete: For categorical/integer data

  3. Sample Size: - MI estimation requires sufficient data - Rule of thumb: >1000 samples for reliable estimates - More data needed for higher dimensions

  4. Bias Correction: - All estimators have finite sample bias - Consider shuffle controls for significance testing

# Example: Significance testing
def mi_significance(x, y, n_shuffles=100):
    true_mi = get_mi(x, y)

    shuffle_mi = []
    for _ in range(n_shuffles):
        y_shuffled = TimeSeries(
            np.random.permutation(y.data),
            discrete=y.discrete
        )
        shuffle_mi.append(get_mi(x, y_shuffled))

    p_value = np.mean(shuffle_mi >= true_mi)
    return true_mi, p_value