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:
objectSingle 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
- type_info
Detailed type information including subtype and confidence.
- Type:
- scdata
Min-max scaled data to [0,1].
- Type:
ndarray
- copula_normal_data
Copula-normalized data (continuous only).
- Type:
ndarray or None
- int_data
Integer representation (discrete only).
- Type:
ndarray or None
- bool_data
Boolean representation (binary discrete only).
- Type:
ndarray or None
- shuffle_mask
Boolean mask for valid shuffle positions.
- Type:
ndarray
- kdtree
Cached KD-tree for k-NN searches.
- Type:
KDTree or None
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:
- 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.
- 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:
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_queryQuery 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_kdtreeBuild 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:
- 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_dDiscrete entropy calculation.
nonparam_entropy_cContinuous 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:
- 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:
- Returns:
The approximate entropy value. Higher values indicate more randomness/complexity.
- Return type:
- 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.
- visibility_graph(**kwargs)[source]
Build visibility graph. See
information.recurrence.visibility_graph.
- 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:
MVDataMultiple 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
- 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
- Plus all attributes from MVData parent class.
- 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”).
- property shape
Return shape of the data for compatibility with numpy-like access.
- 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:
- 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_dSingle discrete variable entropy.
joint_entropy_ddJoint entropy for 2 discrete variables.
ent_gGaussian 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:
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:
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_miMI for univariate time series (called internally)
get_multi_miMI between multiple and single time series
get_tdmiTime-delayed MI for finding optimal embedding delays
conditional_miConditional 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:
- 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:
- 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:
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:
- 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:
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
Data Preparation: - Ensure time series are aligned - Remove artifacts before analysis - Consider normalization for continuous data
Estimator Choice: -
gcmi: Fast, good for continuous Gaussian-like data -ksg: Non-parametric, works for any continuous distribution -discrete: For categorical/integer dataSample Size: - MI estimation requires sufficient data - Rule of thumb: >1000 samples for reliable estimates - More data needed for higher dimensions
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