INTENSE: Information-Theoretic Evaluation of Neuronal Selectivity

INTENSE is a computational framework for analyzing neuronal selectivity to behavioral and environmental variables using information theory. It quantifies the relationship between neural signals (calcium imaging, spike trains) and external variables through mutual information analysis with rigorous statistical testing.

Overview

INTENSE addresses a fundamental question in neuroscience: which neurons encode specific behavioral or environmental variables? By using mutual information (MI) as the core metric, INTENSE can detect both linear and nonlinear relationships between neural activity and external variables.

The key advantage of MI over traditional correlation measures is its ability to capture any statistical dependency:

  • Linear relationships (captured by Pearson correlation)

  • Monotonic relationships (captured by Spearman correlation)

  • Complex nonlinear dependencies (only captured by MI)

Key features

  • Mutual Information Analysis: Quantifies statistical dependencies between neural signals and behavioral variables

  • Two-Stage Statistical Testing: Efficient hypothesis testing with multiple comparison correction

  • Optimal Delay Detection: Accounts for temporal delays between neural activity and behavior

  • Mixed Selectivity Analysis: Disentangles relationships when neurons respond to multiple correlated variables

  • Multiple Metrics Support: MI, Spearman correlation, and other similarity metrics

  • Parallel Processing: Efficient computation for large-scale neural datasets

Quick start

Analyze neuronal selectivity with synthetic data:

import driada

# Generate synthetic experiment with place cells and behavioral features
exp = driada.generate_synthetic_exp(
    n_dfeats=2,      # discrete features (e.g., trial type)
    n_cfeats=2,      # continuous features (e.g., x, y position)  
    nneurons=20,     # number of neurons
    duration=300,    # 5 minutes recording
    seed=42
)

# Discover significant neuron-feature relationships
stats, significance, info, results, _ = driada.compute_cell_feat_significance(
    exp,
    mode='two_stage',
    n_shuffles_stage1=50,   # preliminary screening
    n_shuffles_stage2=1000, # validation (use 10000+ for publication)
    verbose=True
)

# Use Experiment method to get significant neurons
significant_neurons = exp.get_significant_neurons()
print(f"Found {len(significant_neurons)} significant neuron-feature pairs")

# Examine results using Experiment methods
for cell_id in list(significant_neurons.keys())[:3]:  # Show first 3 neurons
    for feat_name in significant_neurons[cell_id]:
        pair_stats = exp.get_neuron_feature_pair_stats(cell_id, feat_name)
        
        print(f"Neuron {cell_id} - Feature '{feat_name}':")
        print(f"  Rank value (Stage 1): {pair_stats['pre_rval']:.4f}")
        if 'pval' in pair_stats:
            print(f"  P-value: {pair_stats['pval']:.2e}")
        print(f"  Optimal delay: {pair_stats.get('opt_delay', 0)} frames")

Expected output:

Found 8 significant neuron-feature pairs
Neuron 3 - Feature 'c_feat_0':
  Rank value (Stage 1): 0.9901
  P-value: 2.45e-05
  Optimal delay: 3 frames

Visualization:

import matplotlib.pyplot as plt

# Plot neuron-feature relationship for first significant pair
if significant_neurons:
    cell_id = list(significant_neurons.keys())[0]
    feat_name = significant_neurons[cell_id][0]
    fig, ax = plt.subplots(figsize=(8, 5))
    driada.intense.plot_neuron_feature_pair(exp, cell_id, feat_name, ax=ax)
    plt.title(f"Neuron {cell_id} selectivity to {feat_name}")
    plt.show()

Using your own data

The Experiment class is the main data container throughout DRIADA. It holds neural recordings, behavioral variables, and analysis results in a unified structure.

Creating an Experiment from your data

import numpy as np
import driada

# Your neural data
calcium_traces = np.random.randn(100, 12000)  # 100 neurons, 12000 timepoints
spike_times = None  # Optional: spike trains (same shape as calcium)

# Your behavioral variables (dynamic features)
x_position = np.random.randn(12000)           # x coordinate over time
y_position = np.random.randn(12000)           # y coordinate over time  
trial_type = np.random.randint(0, 2, 12000)   # discrete: 0=left, 1=right trials
speed = np.abs(np.random.randn(12000))        # continuous: movement speed

dynamic_features = {
    'x': x_position,
    'y': y_position, 
    'trial_type': trial_type,
    'speed': speed
}

# Experimental metadata (static features)
static_features = {
    'fps': 20.0,        # sampling rate (frames per second)
    't_rise_sec': 0.5,  # calcium rise time
    't_off_sec': 2.0    # calcium decay time
}

# Experiment identifiers
exp_identificators = {
    'session': 'session_001',
    'animal': 'mouse_123'
}

# Create Experiment object
exp = driada.Experiment(
    signature='MyExperiment',
    calcium=calcium_traces,
    spikes=spike_times,  # Can be None
    exp_identificators=exp_identificators,
    static_features=static_features,
    dynamic_features=dynamic_features
)

print(f"Created experiment with {exp.n_cells} neurons and {exp.n_frames} timepoints")

# Now analyze with INTENSE
stats, significance, info, results, _ = driada.compute_cell_feat_significance(exp)

Key points:

  • calcium: Neural activity traces (numpy array, shape: n_neurons × n_timepoints)

  • dynamic_features: Behavioral variables that change over time (dict of numpy arrays)

  • static_features: Experimental parameters (dict with ‘fps’, ‘t_rise_sec’, ‘t_off_sec’)

  • spikes: Optional spike trains (can be None to auto-detect from calcium using wavelets)

  • The Experiment automatically converts numpy arrays to TimeSeries objects internally

  • All subsequent DRIADA analysis uses this unified Experiment container

Mathematical framework

Mutual information computation

INTENSE uses Gaussian Copula Mutual Information (GCMI) for robust MI estimation. The mutual information between two random variables X and Y is defined as:

I(X;Y) = ∫∫ p(x,y) log[p(x,y)/(p(x)p(y))] dx dy

Equivalently, using entropy notation:

I(X;Y) = H(X) + H(Y) - H(X,Y)

where H(X) = -∫ p(x) log p(x) dx is the differential entropy.

Gaussian copula transformation

The GCMI method leverages the copula representation of joint distributions. The key insight is that MI is invariant under monotonic transformations, allowing us to transform to a convenient representation:

  1. Empirical CDF transformation: For each variable, compute the empirical cumulative distribution function:

    F_X(x) = (rank(x) + 0.5) / N
    
  2. Gaussian transformation: Apply the inverse normal CDF:

     = Φ^(-1)(F_X(X))
     = Φ^(-1)(F_Y(Y))
    
  3. MI estimation: For the Gaussian copula, MI has a simple form:

    I_GCMI(X;Y) = -0.5 log|R|
    

    where R is the correlation matrix of (X̃, Ỹ).

Handling different variable types

Continuous-Continuous Case: Direct application of GCMI formula above.

Discrete-Continuous Case: For discrete X with K states and continuous Y:

I(X;Y) = Σ_{k=1}^K p(X=k) · H(Y) - Σ_{k=1}^K p(X=k) · H(Y|X=k)

Each conditional entropy H(Y|X=k) is computed using GCMI on the subset where X=k.

Statistical significance testing

Significance is assessed by comparing observed MI against a null distribution generated from time-shuffled signals:

MI_shuffle^(i) = I(X(t); Y(t + τᵢ))

where τᵢ represents random circular shifts preserving the temporal structure of individual signals while destroying their correlation.

Parametric null distribution modeling

The null distribution of shuffled MI values is modeled using a parametric distribution. The gamma distribution is the default choice due to:

  • Non-negativity of MI values

  • Right-skewed nature of MI distributions

  • Theoretical support from information theory

The gamma distribution parameters are estimated via maximum likelihood:

MI_shuffle ~ Γ(α, β)
p(MI|α,β) = (β^α/Γ(α)) · MI^(α-1) · exp(-β·MI)

The p-value is computed as:

p-value = P(MI_shuffle ≥ MI_observed) = 1 - F_Γ(MI_observed; α̂, β̂)

where F_Γ is the cumulative distribution function of the fitted gamma distribution.

Noise addition for numerical stability

When using the standard gamma distribution, a small amount of noise (10^-3) is added to MI values to prevent numerical issues with identical values and improve distribution fitting. When using the default gamma_zi distribution, noise is not added because the zero-inflated model handles zero MI values explicitly.

Rank values (r-vals) and non-parametric testing

INTENSE employs a dual approach combining parametric and non-parametric methods:

Rank Values (r-vals): For each neuron-feature pair, INTENSE computes the rank of the observed MI value within the null distribution:

r-val = rank(MI_observed) / (n_shuffles + 1)

The r-val represents the proportion of shuffled MI values that are lower than the observed MI. This is equivalent to computing an empirical p-value:

empirical_p_value = 1 - r-val

Advantages of r-vals:

  • Distribution-free: No assumptions about the shape of the MI distribution

  • Robust: Unaffected by outliers or distribution fitting failures

  • Conservative: The (n_shuffles + 1) denominator prevents p-values of exactly 0

Dual Criterion for Significance: INTENSE requires both criteria to be met:

  1. Non-parametric criterion: r-val > (1 - k/(n_shuffles + 1)), typically k=5

  2. Parametric criterion: p-value < threshold (after multiple comparison correction)

This dual approach provides robustness:

  • The r-val criterion ensures the observed MI is genuinely in the extreme tail

  • The parametric p-value provides a continuous measure of significance

  • Both must pass, preventing false positives from poor distribution fits

Two-stage testing procedure

The two-stage approach balances computational efficiency with statistical rigor:

Stage 1: Screening (100 shuffles)

  • Criterion: MI_observed > max{MI_shuffle^(i)}_{i=1}^{100}

  • Purpose: Early rejection of non-significant pairs

  • False negative rate: < 1% for truly significant pairs

  • Computational savings: ~100x for rejected pairs

Stage 2: Validation (10,000 shuffles)

  • Applied to: Pairs passing Stage 1

  • Criteria:

    1. Rank criterion: MI_observed > 99.95th percentile of shuffles

    2. Parametric test: p-value < threshold (after multiple comparison correction)

Multiple comparison correction

The Holm-Bonferroni method is used to control family-wise error rate (FWER):

  1. Order p-values: p₁ ≤ p₂ ≤ … ≤ pₘ

  2. For the k-th hypothesis, use threshold:

    α_k = α / (m - k + 1)
    

    where α = 0.01 (FWER) and m = number of hypotheses from Stage 1

  3. Reject hypotheses while p_k < α_k; stop at first failure

This provides strong FWER control while being more powerful than standard Bonferroni correction.

Optimal delay detection

Neural activity often shows temporal delays relative to behavior due to:

  • Calcium indicator dynamics (~1-2s delay for GCaMP)

  • Predictive coding (activity preceding behavior)

  • Sensory processing delays

INTENSE finds the optimal delay by maximizing MI:

δ_optimal = argmax_{δ} I(X(t); Y(t + δ))
MI_optimal = I(X(t); Y(t + δ_optimal))

where:

  • δ ∈ [-W, +W], typically W = 2 seconds

  • Resolution: Δδ = 0.05 seconds (or 1/fps)

  • Search space: 80 delay values for 2s window

The significance testing uses the optimally delayed signals, accounting for the multiple comparison issue in delay selection.

FFT-accelerated permutation testing

The two-stage procedure requires evaluating MI at thousands of circular shifts per neuron-behavior pair. A naive implementation recomputes MI independently for each shift, yielding a per-pair cost of $O(n_{\text{sh}} \cdot n)$ where $n$ is the number of time frames and $n_{\text{sh}}$ the number of shuffles. For a typical experiment with $N$ neurons, $M$ behavioral features, $D$ delay steps, and $n_{\text{sh}} = 10{,}000$ shuffles, this becomes prohibitive.

INTENSE exploits the fact that circular time shifts correspond to circular cross-correlations, which can be computed for all $n$ possible shifts simultaneously via the convolution theorem:

$$C_{XY}[s] = \sum_{i=0}^{n-1} X[i] , Y[(i+s) \bmod n] = \mathcal{F}^{-1}!\bigl[\mathcal{F}[X] \cdot \mathcal{F}[Y]^{*}\bigr][s]$$

where $\mathcal{F}$ denotes the discrete Fourier transform and $^{*}$ complex conjugation. This reduces the cost from $O(n_{\text{sh}} \cdot n)$ to $O(n \log n)$ for computing MI at all shifts, after which any specific shift is an $O(1)$ array lookup. The particular form of MI at each shift depends on the variable types:

Continuous–continuous case. For copula-normalized Gaussian variables, MI reduces to $I(X; Y_s) = -\tfrac{1}{2}\log_2(1 - r(s)^2)$ where $r(s)$ is the Pearson correlation at shift $s$. The correlation for all shifts is obtained as $r(s) = C_{XY}[s] / [(n-1),\sigma_X \sigma_Y]$, requiring a single pair of real-valued FFTs.

Continuous–discrete case. For a continuous variable $Z$ and a discrete variable $X$ with $K$ classes, class-conditional sums at every shift are circular correlations with indicator functions:

$$\sum_i Z[i] \cdot \mathbb{1}[X_{(i+s)} = k] = \mathcal{F}^{-1}[\mathcal{F}[Z] \cdot \mathcal{F}[\mathbb{1}_{X=k}]^{*}][s]$$

From these sums and the analogous sums of $Z^2$, class-conditional variances and Gaussian entropies are computed for all shifts simultaneously, yielding MI via $I = H(Z) - \sum_k p(k),H(Z | X!=!k)$. This requires $2K+2$ FFTs (for $Z$, $Z^2$, and $K$ indicator functions), independent of the number of shuffles.

Discrete–discrete case. Contingency tables for all shifts are obtained via indicator cross-correlations: $n_{ij}(s) = \mathcal{F}^{-1}[\mathcal{F}[\mathbb{1}{X=i}] \cdot \mathcal{F}[\mathbb{1}{Y=j}]^{*}][s]$, requiring $K_X \cdot K_Y$ FFT pairs.

Multivariate case. For $d$-dimensional behavioral features (e.g., 2D spatial position), the joint covariance matrix separates into shift-invariant within-variable blocks and shift-dependent cross-covariance blocks computed via $d$ FFTs. MI is then evaluated through closed-form vectorized determinant formulas ($d \leq 3$).

FFT cache strategy

INTENSE precomputes MI for all $n$ shifts once per neuron-feature pair and stores the result in an FFT cache. This cache is reused across delay optimization, Stage 1 screening, and Stage 2 validation without recomputation. The total cost is $O(N \cdot M \cdot n \log n)$ for the cache build plus $O(N \cdot M \cdot n_{\text{sh}})$ for shift lookups, compared to $O(N \cdot M \cdot n_{\text{sh}} \cdot n)$ without acceleration.

Mixed selectivity analysis

Many neurons respond to multiple, often correlated behavioral variables. INTENSE disentangles these relationships using information-theoretic decomposition.

Conditional mutual information

For neural activity A and behavioral variables X, Y:

I(A;X|Y) = H(A|Y) - H(A|X,Y) = I(A;X,Y) - I(A;Y)

This quantifies information about A in X that is not present in Y.

Interaction information

The three-way interaction (synergy/redundancy) is measured using the Williams & Beer (2010) convention:

II(A;X;Y) = I(A;X|Y) - I(A;X) = I(A;Y|X) - I(A;Y)
  • II < 0: Redundancy (X and Y provide overlapping information about A)

  • II > 0: Synergy (X and Y together provide more information than separately)

Note: Interaction information (II) provides the net redundancy or synergy but does not decompose mutual information into unique, redundant, and synergistic components. A future Partial Information Decomposition (PID) module will provide this full decomposition.

Variable type handling

  1. Both continuous: Use multivariate GCMI with Cholesky decomposition

  2. X continuous, Y discrete: Compute I(A;X|Y=y) for each y, then average

  3. X discrete, Y continuous: Use the identity I(A;X|Y) = I(A;X) - [I(A;Y) - I(A;Y|X)]

  4. Both discrete: Standard discrete MI formulas with empirical probabilities

Interpretation framework

For negative II (common in neural data), we identify the “weakest” link:

  • If |I(A;X)| < |II|: X is redundant given Y

  • If |I(A;Y)| < |II|: Y is redundant given X

  • Otherwise: Both variables contribute independently

Usage example

from driada.intense.pipelines import compute_cell_feat_significance

# Analyze neuronal selectivity in an experiment
stats, significance, info, results, _ = compute_cell_feat_significance(
    exp,                    # Experiment object
    cell_bunch=None,        # Analyze all neurons
    feat_bunch=['speed', 'head_direction', 'x', 'y'],  # Behavioral variables
    metric='mi',            # Use mutual information
    mode='two_stage',       # Two-stage testing
    n_shuffles_stage1=100,  # Stage 1 shuffles
    n_shuffles_stage2=10000,# Stage 2 shuffles
    pval_thr=0.01,         # Significance threshold
    find_optimal_delays=True,
    shift_window=2          # ±2 second delay window
)

Output

INTENSE provides:

  • Statistical measures: MI values, p-values, optimal delays

  • Significance results: Which neuron-behavior pairs are significant

  • Diagnostic information: Shuffle distributions, test statistics

Applications

  • Place cell identification in navigation experiments

  • Feature selectivity in sensory neurons

  • Task-relevant neural encoding in cognitive experiments

  • Population-level analysis of neural representations

  • Validation of computational models against neural data

Limitations and considerations

Statistical power

  • Stage 1 screening may miss weak but real effects (1% false negative rate)

  • Multiple comparison correction reduces power for large neuron populations

  • Minimum recommended recording length: 1000 timepoints per behavioral state

Computational considerations

  • Memory usage scales as O(N × M × S) for N neurons, M behaviors, S shuffles

  • Optimal delay search adds 40-80x computational overhead

  • Parallel processing recommended for >100 neurons

Methodological assumptions

  • Stationarity: Assumes stable neuron-behavior relationships

  • Temporal resolution: Limited by sampling rate (calcium imaging: ~10-30 Hz)

  • Shuffle validity: Assumes temporal shifts destroy all dependencies

References

Key publications underlying INTENSE methodology:

  1. Gaussian Copula MI: Ince, R. A., Giordano, B. L., Kayser, C., Rousselet, G. A., Gross, J., & Schyns, P. G. (2017). A statistical framework for neuroimaging data analysis based on mutual information estimated via a gaussian copula. Human brain mapping, 38(3), 1541-1573.

  2. Information Theory in Neuroscience: Timme, N., Alford, W., Flecker, B., & Beggs, J. M. (2014). Synergy, redundancy, and multivariate information measures: an experimentalist’s perspective. Journal of computational neuroscience, 36(2), 119-140.

  3. Interaction Information: Ghassami, A., Kiyavash, N., Huang, B., & Zhang, K. (2017). Multi-domain causal structure learning in linear systems. Advances in neural information processing systems, 30.

  4. MI Distribution Theory: Hutter, M. (2001). Distribution of mutual information. Advances in neural information processing systems, 14.

Performance considerations

  • Parallel processing supported for large datasets

  • Downsampling available to reduce computational load

  • Caching of precomputed statistics for iterative analysis

  • Memory-efficient implementations for long recordings