Shuffled Data

SpikeLab provides methods for generating shuffled null distributions from spike train data using degree-preserving double-edge swaps (Okun et al. 2012). These are useful for testing whether observed patterns (e.g. correlations, synchrony metrics) exceed what would be expected by chance.

  • Okun, M., Yger, P., Marguet, S. L. et al. Population rate dynamics and multineuron firing patterns in sensory cortex. J Neurosci 32, 17108–17119 (2012).

Single Shuffle

spike_shuffle() produces a single shuffled copy of a SpikeData object using degree-preserving double-edge swaps on the binned spike matrix (Okun et al. 2012). This preserves each neuron’s spike count and each time bin’s population rate while disrupting the precise spike timing:

sd_shuffled = sd.spike_shuffle(
    swap_per_spike=5,   # number of swap attempts per spike
    seed=42,            # random seed for reproducibility
    bin_size=1,         # raster bin size (ms)
)

The returned SpikeData has the same number of units and the same total spike counts per unit as the original, but the temporal correlations between units are destroyed.

Shuffle Stacks

To build a null distribution of shuffled copies, use spike_shuffle_stack(). This calls spike_shuffle repeatedly and collects the results into a SpikeSliceStack:

shuffle_stack = sd.spike_shuffle_stack(
    n_shuffles=100,
    seed=42,             # base seed; incremented per shuffle
    swap_per_spike=5,
)

# Each element is an independent shuffled SpikeData
print(len(shuffle_stack.spike_stack))  # 100

You can then compute a metric on each shuffled copy and compare to the observed value — for example using apply():

import numpy as np

# Compute mean pairwise STTC for each shuffle
def mean_sttc(sd):
    pcm = sd.spike_time_tilings(delt=20)
    return np.nanmean(pcm.extract_lower_triangle())

shuffle_values = shuffle_stack.apply(mean_sttc)  # shape (100,)
observed = mean_sttc(sd)

Unit Subset Stacks

subset_stack() generates random subsets of units, useful for sub-sampling analyses or bootstrap confidence intervals:

subset_stack = sd.subset_stack(
    n_subsets=50,
    units_per_subset=10,
    seed=42,
)

Each element in the stack is a SpikeData with units_per_subset randomly selected units (sampled without replacement within each subset). The same unit may appear in multiple subsets.

Comparing to the Null Distribution

SpikeLab provides two functions for comparing an observed value against a shuffle distribution:

from spikelab.spikedata.utils import shuffle_z_score, shuffle_percentile

# Z-score: (observed - mean) / std of shuffles
z = shuffle_z_score(observed, shuffle_values)

# Percentile rank: fraction of shuffle values <= observed
p = shuffle_percentile(observed, shuffle_values)

Both functions operate element-wise and accept arrays, so you can compare entire matrices or vectors of observed values against their corresponding shuffle distributions at once.