nltools 0.6.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nltools/__init__.py +55 -0
- nltools/algorithms/__init__.py +90 -0
- nltools/algorithms/alignment/__init__.py +21 -0
- nltools/algorithms/alignment/procrustes.py +565 -0
- nltools/algorithms/alignment/srm.py +758 -0
- nltools/algorithms/backends.py +1059 -0
- nltools/algorithms/corrections.py +177 -0
- nltools/algorithms/decoding.py +327 -0
- nltools/algorithms/inference/__init__.py +50 -0
- nltools/algorithms/inference/bootstrap.py +1386 -0
- nltools/algorithms/inference/correlation.py +373 -0
- nltools/algorithms/inference/intersubject.py +422 -0
- nltools/algorithms/inference/isc.py +1554 -0
- nltools/algorithms/inference/matrix.py +602 -0
- nltools/algorithms/inference/one_sample.py +288 -0
- nltools/algorithms/inference/random.py +122 -0
- nltools/algorithms/inference/timeseries.py +347 -0
- nltools/algorithms/inference/two_sample.py +212 -0
- nltools/algorithms/inference/utils.py +58 -0
- nltools/algorithms/inference/validation.py +282 -0
- nltools/algorithms/neighborhoods.py +207 -0
- nltools/algorithms/outliers.py +308 -0
- nltools/algorithms/regression.py +83 -0
- nltools/algorithms/signal.py +303 -0
- nltools/algorithms/similarity.py +234 -0
- nltools/algorithms/validation.py +151 -0
- nltools/cross_validation.py +72 -0
- nltools/data/__init__.py +30 -0
- nltools/data/adjacency/__init__.py +875 -0
- nltools/data/adjacency/io.py +111 -0
- nltools/data/adjacency/modeling.py +569 -0
- nltools/data/adjacency/plotting.py +174 -0
- nltools/data/adjacency/state.py +349 -0
- nltools/data/adjacency/stats.py +596 -0
- nltools/data/adjacency/utils.py +79 -0
- nltools/data/atlases/__init__.py +23 -0
- nltools/data/atlases/labeling.py +158 -0
- nltools/data/atlases/loading.py +76 -0
- nltools/data/atlases/registry.py +96 -0
- nltools/data/atlases/reporting.py +456 -0
- nltools/data/braindata/__init__.py +2170 -0
- nltools/data/braindata/analysis.py +1381 -0
- nltools/data/braindata/bootstrap.py +398 -0
- nltools/data/braindata/io.py +896 -0
- nltools/data/braindata/modeling.py +594 -0
- nltools/data/braindata/plotting.py +501 -0
- nltools/data/braindata/prediction.py +1250 -0
- nltools/data/braindata/utils.py +348 -0
- nltools/data/braindata/validation.py +197 -0
- nltools/data/braindata/viewer.js +266 -0
- nltools/data/braindata/viewer.py +770 -0
- nltools/data/combine.py +27 -0
- nltools/data/designmatrix/__init__.py +1032 -0
- nltools/data/designmatrix/append.py +518 -0
- nltools/data/designmatrix/diagnostics.py +248 -0
- nltools/data/designmatrix/io.py +356 -0
- nltools/data/designmatrix/plotting.py +291 -0
- nltools/data/designmatrix/regressors.py +463 -0
- nltools/data/designmatrix/transforms.py +200 -0
- nltools/data/designmatrix/utils.py +350 -0
- nltools/data/ownership.py +129 -0
- nltools/data/results.py +291 -0
- nltools/data/roc/__init__.py +398 -0
- nltools/data/simulator/__init__.py +927 -0
- nltools/data/simulator/haxby.py +124 -0
- nltools/data/validation.py +83 -0
- nltools/datasets.py +218 -0
- nltools/io/__init__.py +10 -0
- nltools/io/events.py +67 -0
- nltools/io/h5.py +246 -0
- nltools/mask.py +403 -0
- nltools/models/__init__.py +11 -0
- nltools/models/glm.py +543 -0
- nltools/models/results.py +49 -0
- nltools/models/ridge.py +1303 -0
- nltools/models/validation.py +26 -0
- nltools/plotting/__init__.py +32 -0
- nltools/plotting/adjacency.py +421 -0
- nltools/plotting/brain.py +669 -0
- nltools/plotting/decomposition.py +111 -0
- nltools/plotting/prediction.py +110 -0
- nltools/resources/covariates_example.csv +161 -0
- nltools/resources/onsets_example.csv +40 -0
- nltools/templates/__init__.py +51 -0
- nltools/templates/config.py +144 -0
- nltools/templates/fetch.py +260 -0
- nltools/templates/matching.py +183 -0
- nltools/templates/paths.py +106 -0
- nltools/templates/registry.py +25 -0
- nltools/utils.py +230 -0
- nltools/version.py +13 -0
- nltools-0.6.0.dev0.dist-info/METADATA +95 -0
- nltools-0.6.0.dev0.dist-info/RECORD +95 -0
- nltools-0.6.0.dev0.dist-info/WHEEL +4 -0
- nltools-0.6.0.dev0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Permutation test for the correlation between two variables.
|
|
2
|
+
|
|
3
|
+
`correlation_permutation_test` asks whether the Pearson, Spearman, or Kendall
|
|
4
|
+
correlation between two arrays differs from zero, building the null distribution
|
|
5
|
+
by shuffling one array's observations. It assumes observations are independent;
|
|
6
|
+
for autocorrelated time series use `_timeseries_correlation_permutation_test`.
|
|
7
|
+
Multi-feature inputs (2D arrays) test each column pair independently.
|
|
8
|
+
Permutations run on joblib workers; `n_jobs` sets how many, and a given
|
|
9
|
+
`random_state` gives the same result at any worker count.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from scipy.stats import rankdata, kendalltau
|
|
15
|
+
from sklearn.utils import check_random_state
|
|
16
|
+
|
|
17
|
+
from .utils import EPSILON, _maybe_tqdm
|
|
18
|
+
from ..validation import _compute_pvalue, _validate_tail_parameter
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _pearson_correlation(x: np.ndarray, y: np.ndarray) -> np.ndarray | float:
|
|
22
|
+
"""Compute Pearson correlation coefficient(s).
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
x (np.ndarray): Data array, shape (n_samples,) or (n_permute, n_samples).
|
|
26
|
+
y (np.ndarray): Data array, shape (n_samples,).
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
float | np.ndarray: A scalar if `x` is 1D, else one correlation per row
|
|
30
|
+
of `x`, shape (n_permute,).
|
|
31
|
+
|
|
32
|
+
Note:
|
|
33
|
+
Centers the data before computing, and vectorizes across the rows of a
|
|
34
|
+
2D `x`.
|
|
35
|
+
"""
|
|
36
|
+
# Handle dimensions
|
|
37
|
+
if x.ndim == 1:
|
|
38
|
+
x = x[np.newaxis, :] # (1, n_samples)
|
|
39
|
+
squeeze_output = True
|
|
40
|
+
else:
|
|
41
|
+
squeeze_output = False
|
|
42
|
+
|
|
43
|
+
# Center data
|
|
44
|
+
x_centered = x - x.mean(axis=1, keepdims=True) # (n_permute, n_samples)
|
|
45
|
+
y_centered = y - y.mean() # (n_samples,)
|
|
46
|
+
|
|
47
|
+
# Compute correlation
|
|
48
|
+
numerator = (x_centered @ y_centered) / x.shape[1]
|
|
49
|
+
denominator = x_centered.std(axis=1, ddof=0) * y_centered.std(ddof=0)
|
|
50
|
+
|
|
51
|
+
# Handle division by zero (constant data) using EPSILON
|
|
52
|
+
correlations = numerator / (denominator + EPSILON)
|
|
53
|
+
|
|
54
|
+
if squeeze_output:
|
|
55
|
+
return float(correlations[0])
|
|
56
|
+
return correlations
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _spearman_correlation(x: np.ndarray, y: np.ndarray) -> np.ndarray | float:
|
|
60
|
+
"""Compute Spearman rank correlation coefficient(s).
|
|
61
|
+
|
|
62
|
+
Spearman correlation is the Pearson correlation of the rank-transformed data.
|
|
63
|
+
It measures monotonic (not necessarily linear) relationships.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
x (np.ndarray): Data array, shape (n_samples,) or (n_permute, n_samples).
|
|
67
|
+
y (np.ndarray): Data array, shape (n_samples,).
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
float | np.ndarray: A scalar if `x` is 1D, else one correlation per row
|
|
71
|
+
of `x`, shape (n_permute,).
|
|
72
|
+
|
|
73
|
+
Note:
|
|
74
|
+
Ranks with `scipy.stats.rankdata` (ties get their average rank), then
|
|
75
|
+
applies the Pearson correlation to the ranks.
|
|
76
|
+
"""
|
|
77
|
+
# Handle dimensions
|
|
78
|
+
if x.ndim == 1:
|
|
79
|
+
x = x[np.newaxis, :] # (1, n_samples)
|
|
80
|
+
squeeze_output = True
|
|
81
|
+
else:
|
|
82
|
+
squeeze_output = False
|
|
83
|
+
|
|
84
|
+
n_permute, n_samples = x.shape
|
|
85
|
+
|
|
86
|
+
# Rank-transform data (average method for tied ranks)
|
|
87
|
+
# For vectorized case, rank each permutation separately
|
|
88
|
+
x_ranked = np.empty_like(x)
|
|
89
|
+
for i in range(n_permute):
|
|
90
|
+
x_ranked[i] = rankdata(x[i], method="average")
|
|
91
|
+
|
|
92
|
+
y_ranked = rankdata(y, method="average")
|
|
93
|
+
|
|
94
|
+
# Apply Pearson correlation to ranks
|
|
95
|
+
# Center ranked data
|
|
96
|
+
x_centered = x_ranked - x_ranked.mean(axis=1, keepdims=True)
|
|
97
|
+
y_centered = y_ranked - y_ranked.mean()
|
|
98
|
+
|
|
99
|
+
# Compute correlation
|
|
100
|
+
numerator = (x_centered @ y_centered) / n_samples
|
|
101
|
+
denominator = x_centered.std(axis=1, ddof=0) * y_centered.std(ddof=0)
|
|
102
|
+
|
|
103
|
+
# Handle division by zero (constant data - all tied ranks) using EPSILON
|
|
104
|
+
correlations = numerator / (denominator + EPSILON)
|
|
105
|
+
|
|
106
|
+
if squeeze_output:
|
|
107
|
+
return float(correlations[0])
|
|
108
|
+
return correlations
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _kendall_correlation(x: np.ndarray, y: np.ndarray) -> np.ndarray | float:
|
|
112
|
+
"""Compute Kendall rank correlation coefficient(s).
|
|
113
|
+
|
|
114
|
+
Kendall tau correlation measures ordinal association based on concordant
|
|
115
|
+
and discordant pairs. More robust than Spearman for small samples or
|
|
116
|
+
data with many tied ranks.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
x (np.ndarray): Data array, shape (n_samples,) or (n_permute, n_samples).
|
|
120
|
+
y (np.ndarray): Data array, shape (n_samples,).
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
float | np.ndarray: A scalar if `x` is 1D, else one correlation per row
|
|
124
|
+
of `x`, shape (n_permute,). A NaN tau (constant input) is returned
|
|
125
|
+
as 0.0.
|
|
126
|
+
|
|
127
|
+
Note:
|
|
128
|
+
Calls `scipy.stats.kendalltau` once per row; O(n²) per call, so slower
|
|
129
|
+
than Pearson or Spearman.
|
|
130
|
+
"""
|
|
131
|
+
# Handle dimensions
|
|
132
|
+
if x.ndim == 1:
|
|
133
|
+
# Single correlation
|
|
134
|
+
tau, _ = kendalltau(x, y)
|
|
135
|
+
return float(tau) if not np.isnan(tau) else 0.0
|
|
136
|
+
# Vectorized: compute each permutation separately
|
|
137
|
+
n_permute = x.shape[0]
|
|
138
|
+
correlations = np.empty(n_permute)
|
|
139
|
+
for i in range(n_permute):
|
|
140
|
+
tau, _ = kendalltau(x[i], y)
|
|
141
|
+
correlations[i] = tau if not np.isnan(tau) else 0.0
|
|
142
|
+
return correlations
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _select_corr_func(
|
|
146
|
+
metric: str,
|
|
147
|
+
) -> Callable[[np.ndarray, np.ndarray], np.ndarray | float]:
|
|
148
|
+
"""Return the correlation function for a metric name."""
|
|
149
|
+
if metric == "pearson":
|
|
150
|
+
return _pearson_correlation
|
|
151
|
+
if metric == "spearman":
|
|
152
|
+
return _spearman_correlation
|
|
153
|
+
if metric == "kendall":
|
|
154
|
+
return _kendall_correlation
|
|
155
|
+
raise NotImplementedError(f"Metric '{metric}' not yet implemented")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _correlation_permutation_cpu_parallel(
|
|
159
|
+
data1: np.ndarray,
|
|
160
|
+
data2: np.ndarray,
|
|
161
|
+
*,
|
|
162
|
+
n_permute: int,
|
|
163
|
+
metric: str,
|
|
164
|
+
tail: int,
|
|
165
|
+
return_null: bool,
|
|
166
|
+
n_jobs: int,
|
|
167
|
+
random_state: int | None,
|
|
168
|
+
single_feature: bool = False,
|
|
169
|
+
progress_bar: bool = False,
|
|
170
|
+
) -> dict:
|
|
171
|
+
"""Correlation permutation test parallelized across CPU workers with joblib.
|
|
172
|
+
|
|
173
|
+
Each worker handles one permutation: shuffle `data1`, correlate with `data2`.
|
|
174
|
+
Seeds are pre-generated from `random_state`, so results do not depend on
|
|
175
|
+
`n_jobs`.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
data1 (np.ndarray): Data to permute, shape (n_samples, n_features).
|
|
179
|
+
data2 (np.ndarray): Data to correlate with, shape (n_samples, n_features).
|
|
180
|
+
n_permute (int): Number of permutations.
|
|
181
|
+
metric (str): Correlation metric, one of 'pearson', 'spearman', or 'kendall'.
|
|
182
|
+
tail (int | str): `2` or `'two'` for two-tailed; `1` or `'one'` for one-tailed.
|
|
183
|
+
return_null (bool): Whether to return the null distribution.
|
|
184
|
+
n_jobs (int): Number of parallel workers (-1 = all cores).
|
|
185
|
+
random_state (int | None): Random seed for reproducibility.
|
|
186
|
+
single_feature (bool): Whether the caller passed 1D inputs (results are
|
|
187
|
+
returned as scalars).
|
|
188
|
+
progress_bar (bool): Show a progress bar over permutations.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
dict: Same keys as `correlation_permutation_test`.
|
|
192
|
+
"""
|
|
193
|
+
from joblib import Parallel, delayed
|
|
194
|
+
|
|
195
|
+
# Setup random state and generate seeds for workers
|
|
196
|
+
rng = check_random_state(random_state)
|
|
197
|
+
MAX_INT = 2**31 - 1
|
|
198
|
+
seeds = rng.randint(MAX_INT, size=n_permute)
|
|
199
|
+
|
|
200
|
+
# Get dimensions (data already reshaped by caller)
|
|
201
|
+
n_samples, n_features = data1.shape
|
|
202
|
+
|
|
203
|
+
# Select correlation function
|
|
204
|
+
corr_func = _select_corr_func(metric)
|
|
205
|
+
|
|
206
|
+
# Compute observed correlation
|
|
207
|
+
if n_features == 1:
|
|
208
|
+
obs_corr = corr_func(data1[:, 0], data2[:, 0])
|
|
209
|
+
obs_corr = np.array([obs_corr])
|
|
210
|
+
else:
|
|
211
|
+
obs_corr = np.array(
|
|
212
|
+
[corr_func(data1[:, i], data2[:, i]) for i in range(n_features)]
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Define worker function (each processes ONE permutation)
|
|
216
|
+
def _compute_one_perm(seed):
|
|
217
|
+
"""Compute correlation for one permutation."""
|
|
218
|
+
perm_rng = np.random.RandomState(seed)
|
|
219
|
+
# Permute data1 indices
|
|
220
|
+
indices = perm_rng.permutation(n_samples)
|
|
221
|
+
perm_data1 = data1[indices]
|
|
222
|
+
|
|
223
|
+
# Compute correlation for each feature
|
|
224
|
+
if n_features == 1:
|
|
225
|
+
return corr_func(perm_data1[:, 0], data2[:, 0])
|
|
226
|
+
return np.array(
|
|
227
|
+
[corr_func(perm_data1[:, i], data2[:, i]) for i in range(n_features)]
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Execute in parallel with progress bar
|
|
231
|
+
null_dist = Parallel(n_jobs=n_jobs)(
|
|
232
|
+
delayed(_compute_one_perm)(seeds[i])
|
|
233
|
+
for i in _maybe_tqdm(
|
|
234
|
+
range(n_permute),
|
|
235
|
+
progress_bar=progress_bar,
|
|
236
|
+
desc="CPU parallel perms",
|
|
237
|
+
unit="perm",
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
null_dist = np.array(null_dist) # Shape: (n_permute, n_features)
|
|
241
|
+
|
|
242
|
+
# Compute p-values
|
|
243
|
+
p_values = _compute_pvalue(obs_corr, null_dist, tail=tail)
|
|
244
|
+
|
|
245
|
+
# Return to original shape
|
|
246
|
+
if single_feature:
|
|
247
|
+
obs_corr = obs_corr.item() if hasattr(obs_corr, "item") else float(obs_corr[0])
|
|
248
|
+
p_values = p_values.item() if hasattr(p_values, "item") else float(p_values[0])
|
|
249
|
+
|
|
250
|
+
# Build result
|
|
251
|
+
result = {
|
|
252
|
+
"correlation": obs_corr,
|
|
253
|
+
"p": p_values,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if return_null:
|
|
257
|
+
if single_feature:
|
|
258
|
+
null_dist = null_dist.squeeze()
|
|
259
|
+
result["null_dist"] = null_dist
|
|
260
|
+
|
|
261
|
+
return result
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def correlation_permutation_test(
|
|
265
|
+
data1: np.ndarray,
|
|
266
|
+
data2: np.ndarray,
|
|
267
|
+
*,
|
|
268
|
+
n_permute: int = 5000,
|
|
269
|
+
metric: str = "pearson",
|
|
270
|
+
tail: int | str = 2,
|
|
271
|
+
return_null: bool = False,
|
|
272
|
+
n_jobs: int = -1,
|
|
273
|
+
random_state: int | None = None,
|
|
274
|
+
progress_bar: bool = False,
|
|
275
|
+
) -> dict:
|
|
276
|
+
"""Permutation test for whether the correlation between two arrays differs from zero.
|
|
277
|
+
|
|
278
|
+
Builds the null distribution by randomly permuting the observations of `data1`
|
|
279
|
+
and re-correlating with `data2`. Assumes observations are independent (i.i.d.);
|
|
280
|
+
for autocorrelated time series use `_timeseries_correlation_permutation_test`,
|
|
281
|
+
whose `'circle_shift'` and `'phase_randomize'` methods preserve temporal
|
|
282
|
+
structure. With 2D inputs each column of `data1` is tested against the
|
|
283
|
+
matching column of `data2`, independently.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
data1 (np.ndarray): Data to permute, shape (n_samples,) for a single
|
|
287
|
+
feature or (n_samples, n_features) for several.
|
|
288
|
+
data2 (np.ndarray): Data to correlate with, same shape as `data1`.
|
|
289
|
+
n_permute (int): Number of permutations. Defaults to 5000.
|
|
290
|
+
metric (str): 'pearson' (linear), 'spearman' (rank-based, monotonic), or
|
|
291
|
+
'kendall' (tau-b, ordinal association, tie-corrected). Defaults to
|
|
292
|
+
'pearson'.
|
|
293
|
+
tail (int | str): `2` or `'two'` for a two-tailed test (r != 0); `1` or
|
|
294
|
+
`'one'` for a one-tailed test of r > 0 (negate one variable for the
|
|
295
|
+
other direction; the fixed direction keeps multiple-comparison
|
|
296
|
+
correction valid). Defaults to 2.
|
|
297
|
+
return_null (bool): Also return the full null distribution. Defaults to
|
|
298
|
+
False.
|
|
299
|
+
n_jobs (int): Number of joblib workers, -1 = all cores. Defaults to -1.
|
|
300
|
+
Results are identical at every worker count.
|
|
301
|
+
random_state (int | None): Random seed for reproducibility.
|
|
302
|
+
progress_bar (bool): Show a progress bar over permutations. Defaults to
|
|
303
|
+
False.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
dict: Keys 'correlation' (float, or np.ndarray of shape (n_features,) for
|
|
307
|
+
2D inputs: the observed correlation), 'p' (float or np.ndarray, the
|
|
308
|
+
matching p-values), and 'null_dist' (np.ndarray of shape
|
|
309
|
+
(n_permute,) or (n_permute, n_features)) when `return_null=True`.
|
|
310
|
+
|
|
311
|
+
Examples:
|
|
312
|
+
```python
|
|
313
|
+
import numpy as np
|
|
314
|
+
from nltools.algorithms import correlation_permutation_test
|
|
315
|
+
|
|
316
|
+
# Single feature
|
|
317
|
+
x = np.random.randn(100)
|
|
318
|
+
y = x + np.random.randn(100) * 0.5
|
|
319
|
+
result = correlation_permutation_test(x, y, n_permute=5000)
|
|
320
|
+
result["correlation"] # → 0.85 (approximately)
|
|
321
|
+
result["p"] # → 0.0002
|
|
322
|
+
|
|
323
|
+
# Multi-feature: each column pair tested independently
|
|
324
|
+
data1 = np.random.randn(100, 10)
|
|
325
|
+
data2 = data1 + np.random.randn(100, 10) * 0.3
|
|
326
|
+
result = correlation_permutation_test(data1, data2, n_permute=5000)
|
|
327
|
+
result["correlation"].shape # → (10,)
|
|
328
|
+
result["p"].shape # → (10,)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Note:
|
|
332
|
+
Kendall's tau is O(n²) in the number of samples, so it is markedly
|
|
333
|
+
slower than Pearson or Spearman for large samples.
|
|
334
|
+
"""
|
|
335
|
+
# Input validation
|
|
336
|
+
data1 = np.asarray(data1, dtype=np.float64)
|
|
337
|
+
data2 = np.asarray(data2, dtype=np.float64)
|
|
338
|
+
|
|
339
|
+
if data1.ndim not in [1, 2]:
|
|
340
|
+
raise ValueError(f"data1 must be 1D or 2D, got shape {data1.shape}")
|
|
341
|
+
if data2.ndim not in [1, 2]:
|
|
342
|
+
raise ValueError(f"data2 must be 1D or 2D, got shape {data2.shape}")
|
|
343
|
+
tail = _validate_tail_parameter(tail)
|
|
344
|
+
if metric not in ["pearson", "spearman", "kendall"]:
|
|
345
|
+
raise ValueError(
|
|
346
|
+
f"metric must be 'pearson', 'spearman', or 'kendall', got '{metric}'"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Handle shape
|
|
350
|
+
single_feature = data1.ndim == 1 and data2.ndim == 1
|
|
351
|
+
if data1.ndim == 1:
|
|
352
|
+
data1 = data1[:, np.newaxis]
|
|
353
|
+
if data2.ndim == 1:
|
|
354
|
+
data2 = data2[:, np.newaxis]
|
|
355
|
+
|
|
356
|
+
# Check dimensions match
|
|
357
|
+
if data1.shape != data2.shape:
|
|
358
|
+
raise ValueError(
|
|
359
|
+
f"data1 and data2 must have same shape, got {data1.shape} and {data2.shape}"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
return _correlation_permutation_cpu_parallel(
|
|
363
|
+
data1,
|
|
364
|
+
data2,
|
|
365
|
+
n_permute=n_permute,
|
|
366
|
+
metric=metric,
|
|
367
|
+
tail=tail,
|
|
368
|
+
return_null=return_null,
|
|
369
|
+
n_jobs=n_jobs,
|
|
370
|
+
random_state=random_state,
|
|
371
|
+
single_feature=single_feature,
|
|
372
|
+
progress_bar=progress_bar,
|
|
373
|
+
)
|