Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

timeseries

timeseries

Time-series permutation test implementations.

This module provides GPU-accelerated implementations of time-series permutation tests that preserve temporal structure:

References

Theiler, J., Galdrikian, B., Longtin, A., Eubank, S., & Farmer, J. D. (1991). Testing for nonlinearity in time series: the method of surrogate data (No. LA-UR-91-3343; CONF-9108181-1). Los Alamos National Lab., NM (United States).

Lancaster, G., Iatsenko, D., Pidde, A., Ticcinelli, V., & Stefanovska, A. (2018). Surrogate data for hypothesis testing of physical systems. Physics Reports, 748, 1-60.

Methods:

NameDescription
circle_shiftCircular shift for time-series data.
phase_randomizeFFT-based phase randomization for time-series data.
timeseries_correlation_permutation_testTime-series correlation permutation test.

Classes

Methods

circle_shift

circle_shift(data: np.ndarray, shift_amount: int | np.ndarray | None = None, random_state: int | np.random.RandomState | None = None) -> np.ndarray

Circular shift for time-series data.

Performs a circular shift that preserves autocorrelation structure. Useful for permutation tests on autocorrelated time series (e.g., fMRI). For 1D data, shifts by a single amount. For 2D data, shifts each feature (column) independently.

Parameters:

NameTypeDescriptionDefault
datandarrayTime series data, shape (n_samples,) or (n_samples, n_features)required
shift_amountint | ndarray | NoneShift amount(s). If None, random shift is used. For 1D: int specifying shift amount For 2D: array of length n_features with shift per featureNone
random_stateint | RandomState | NoneRandom seed for reproducibility (if shift_amount is None)None

Returns:

TypeDescription
ndarrayCircularly shifted data with same shape as input

Examples:

>>> x = np.array([1, 2, 3, 4, 5])
>>> circle_shift(x, shift_amount=2)
array([4, 5, 1, 2, 3])
>>> X = np.array([[1, 10], [2, 20], [3, 30], [4, 40]])
>>> circle_shift(X, shift_amount=np.array([1, 2]))
array([[ 4, 30],
       [ 1, 40],
       [ 2, 10],
       [ 3, 20]])

phase_randomize

phase_randomize(data: np.ndarray, *, device: str | None = 'cpu', random_state: int | np.random.RandomState | None = None) -> np.ndarray

FFT-based phase randomization for time-series data.

Preserves the power spectrum (autocorrelation) but destroys nonlinear temporal structure by randomizing Fourier phases. Used to test whether data was generated by a linear Gaussian process or contains nonlinear dynamics.

Algorithm
  1. Compute FFT of input signal

  2. Generate random phases [0, 2π] for positive frequencies

  3. Apply phase shifts to positive frequencies: multiply by exp(i*φ)

  4. Apply conjugate phase shifts to negative frequencies (for real output)

  5. Compute inverse FFT to get phase-randomized signal

Parameters:

NameTypeDescriptionDefault
datandarrayTime series data, shape (n_samples,) or (n_samples, n_features)required
devicestr | NoneCompute device. - ‘cpu’ / None: NumPy FFT (default, float64 precision) - ‘gpu’: PyTorch FFT on CUDA/MPS (float32 precision, 5-20× faster for large data) - ‘auto’: use a GPU if present, else CPU‘cpu’
random_stateint | RandomState | NoneRandom seed for reproducibilityNone

Returns:

TypeDescription
ndarrayPhase-randomized data with same shape as input
Notes
  • CRITICAL: Preserves power spectrum exactly (within numerical precision)

  • Precision: the CPU path uses float64, the GPU path float32

  • Conjugate symmetry is maintained for real-valued output

Examples:

>>> x = np.sin(np.linspace(0, 10*np.pi, 100))  # Sine wave
>>> x_rand = phase_randomize(x, random_state=42)
>>> # Power spectrum preserved:
>>> np.allclose(np.abs(np.fft.rfft(x))**2, np.abs(np.fft.rfft(x_rand))**2)
True
>>> # GPU acceleration for large datasets:
>>> x_large = np.random.randn(10000)
>>> x_rand_gpu = phase_randomize(x_large, device='gpu', random_state=42)

timeseries_correlation_permutation_test

timeseries_correlation_permutation_test(data1: np.ndarray, data2: np.ndarray, *, method: Literal['circle_shift', 'phase_randomize'] = 'circle_shift', n_permute: int = 5000, metric: Literal['pearson', 'spearman', 'kendall'] = 'pearson', tail: int | str = 2, device: str | None = 'cpu', n_jobs: int = -1, max_gpu_memory_gb: float | None = None, return_null: bool = False, random_state: int | np.random.RandomState | None = None, progress_bar: bool = False) -> dict

Time-series correlation permutation test.

Unlike standard permutation tests that shuffle data independently, this test uses time-series-aware permutation methods that preserve temporal structure (circle_shift) or power spectrum (phase_randomize).

Use this test when data contains temporal autocorrelation. Standard permutation tests inflate Type I error for autocorrelated data.

Parameters:

NameTypeDescriptionDefault
data1ndarrayFirst time series, shape (n_samples,) or (n_samples, 1)required
data2ndarraySecond time series, shape (n_samples,) or (n_samples, 1)required
methodLiteral [‘circle_shift’, ‘phase_randomize’]Permutation method: - ‘circle_shift’: Circular shift (preserves autocorrelation) - ‘phase_randomize’: FFT-based (preserves power spectrum)‘circle_shift’
n_permuteintNumber of permutations5000
metricLiteral [‘pearson’, ‘spearman’, ‘kendall’]Correlation type (‘pearson’, ‘spearman’, ‘kendall’)‘pearson’
tailint | strTest type (default: 2) - 2 or ‘two’: Two-tailed test (default) - 1 or ‘one’: One-tailed test in the test’s positive direction (to test the negative direction, negate the data / swap groups)2
devicestr | NoneParallelization method (default: ‘cpu’) - None: Single-threaded NumPy (for debugging/small problems) - ‘cpu’: CPU parallelization via joblib (default, 4-8× speedup) - ‘gpu’: GPU acceleration via PyTorch (fastest for large problems)‘cpu’
n_jobsintNumber of parallel jobs (-1 = all cores) Only used when device=‘cpu’-1
max_gpu_memory_gbfloat | NoneExplicit GPU memory budget in GB. None (default) measures the device’s available memory. Controls automatic batching to prevent OOM errors. Only used with device=‘gpu’. Larger values allow more permutations per batch but risk OOM on smaller GPUs.None
return_nullboolWhether to return null distributionFalse
random_stateint | RandomState | NoneRandom seed for reproducibilityNone
progress_barboolShow a progress bar over permutations (default: False)False

Returns:

TypeDescription
dictDictionary with keys: - ‘correlation’: Observed correlation coefficient - ‘p’: P-value - ‘null_dist’: (if return_null=True) Null distribution - ‘device’: Parallelization method used

Examples:

>>> x = np.sin(np.linspace(0, 10*np.pi, 100))
>>> y = np.cos(np.linspace(0, 10*np.pi, 100))
>>> result = timeseries_correlation_permutation_test(
...     x, y, method='circle_shift', n_permute=1000, random_state=42
... )
>>> result['correlation']  # Strong negative correlation
-0.999...
>>> result['p'] < 0.05  # Significant
True
>>> # GPU acceleration
>>> result = timeseries_correlation_permutation_test(
...     x, y, method='phase_randomize', device='gpu', n_permute=5000
... )
Notes
  • Default (device=‘cpu’): CPU parallelization with joblib (4-8× speedup)

  • GPU parallelization (‘gpu’): 5-20× faster for large problems (n_samples > 1000)

  • Single-threaded (device=None): Use for small problems or debugging

  • For independent data, use regular correlation_permutation_test

  • circle_shift is faster and suitable for most fMRI time series

  • phase_randomize preserves power spectrum exactly (tests nonlinearity)

  • Only data1 is randomized; data2 remains fixed to test correlation

  • phase_randomize benefits most from GPU (FFT acceleration)