timeseries¶
Time-series permutation test implementations.
This module provides GPU-accelerated implementations of time-series permutation tests that preserve temporal structure:
circle_shift: Circular shift permutation (preserves autocorrelation)
phase_randomize: FFT-based phase randomization (preserves power spectrum)
timeseries_correlation_permutation_test: Correlation test with timeseries methods
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:
| Name | Description |
|---|---|
circle_shift | Circular shift for time-series data. |
phase_randomize | FFT-based phase randomization for time-series data. |
timeseries_correlation_permutation_test | Time-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.ndarrayCircular 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:
| Name | Type | Description | Default |
|---|---|---|---|
data | ndarray | Time series data, shape (n_samples,) or (n_samples, n_features) | required |
shift_amount | int | ndarray | None | Shift amount(s). If None, random shift is used. For 1D: int specifying shift amount For 2D: array of length n_features with shift per feature | None |
random_state | int | RandomState | None | Random seed for reproducibility (if shift_amount is None) | None |
Returns:
| Type | Description |
|---|---|
ndarray | Circularly 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.ndarrayFFT-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
Compute FFT of input signal
Generate random phases [0, 2π] for positive frequencies
Apply phase shifts to positive frequencies: multiply by exp(i*φ)
Apply conjugate phase shifts to negative frequencies (for real output)
Compute inverse FFT to get phase-randomized signal
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data | ndarray | Time series data, shape (n_samples,) or (n_samples, n_features) | required |
device | str | None | Compute 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_state | int | RandomState | None | Random seed for reproducibility | None |
Returns:
| Type | Description |
|---|---|
ndarray | Phase-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) -> dictTime-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:
| Name | Type | Description | Default |
|---|---|---|---|
data1 | ndarray | First time series, shape (n_samples,) or (n_samples, 1) | required |
data2 | ndarray | Second time series, shape (n_samples,) or (n_samples, 1) | required |
method | Literal [‘circle_shift’, ‘phase_randomize’] | Permutation method: - ‘circle_shift’: Circular shift (preserves autocorrelation) - ‘phase_randomize’: FFT-based (preserves power spectrum) | ‘circle_shift’ |
n_permute | int | Number of permutations | 5000 |
metric | Literal [‘pearson’, ‘spearman’, ‘kendall’] | Correlation type (‘pearson’, ‘spearman’, ‘kendall’) | ‘pearson’ |
tail | int | str | Test 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 |
device | str | None | Parallelization 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_jobs | int | Number of parallel jobs (-1 = all cores) Only used when device=‘cpu’ | -1 |
max_gpu_memory_gb | float | None | Explicit 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_null | bool | Whether to return null distribution | False |
random_state | int | RandomState | None | Random seed for reproducibility | None |
progress_bar | bool | Show a progress bar over permutations (default: False) | False |
Returns:
| Type | Description |
|---|---|
dict | Dictionary 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)