Skip to content

Commit cd46252

Browse files
ENH: Add rng to cluster permutation tests
1 parent 2d7db46 commit cd46252

2 files changed

Lines changed: 59 additions & 29 deletions

File tree

mne/stats/cluster_level.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ..utils import (
1313
ProgressBar,
1414
_check_option,
15+
_check_rng_compat,
1516
_pl,
1617
_validate_type,
1718
check_random_state,
@@ -1120,6 +1121,8 @@ def permutation_cluster_test(
11201121
check_disjoint=False,
11211122
buffer_size=1000,
11221123
verbose=None,
1124+
*,
1125+
rng=None,
11231126
):
11241127
"""Cluster-level statistical permutation test.
11251128
@@ -1151,7 +1154,9 @@ def permutation_cluster_test(
11511154
%(stat_fun_clust_f)s
11521155
%(adjacency_clust_n)s
11531156
%(n_jobs)s
1154-
%(seed)s
1157+
%(rng)s
1158+
seed : None | int | instance of ~numpy.random.RandomState
1159+
Deprecated. Use ``rng`` instead.
11551160
%(max_step_clust)s
11561161
%(exclude_clust)s
11571162
%(step_down_p_clust)s
@@ -1181,6 +1186,7 @@ def permutation_cluster_test(
11811186
.. footbibliography::
11821187
"""
11831188
stat_fun, threshold = _check_fun(X, stat_fun, threshold, tail, "between")
1189+
rng = _check_rng_compat(rng, legacy=seed, legacy_name="seed")
11841190
return _permutation_cluster_test(
11851191
X=X,
11861192
threshold=threshold,
@@ -1189,7 +1195,7 @@ def permutation_cluster_test(
11891195
stat_fun=stat_fun,
11901196
adjacency=adjacency,
11911197
n_jobs=n_jobs,
1192-
seed=seed,
1198+
seed=rng,
11931199
max_step=max_step,
11941200
exclude=exclude,
11951201
step_down_p=step_down_p,
@@ -1218,6 +1224,8 @@ def permutation_cluster_1samp_test(
12181224
check_disjoint=False,
12191225
buffer_size=1000,
12201226
verbose=None,
1227+
*,
1228+
rng=None,
12211229
):
12221230
"""Non-parametric cluster-level paired t-test.
12231231
@@ -1238,7 +1246,9 @@ def permutation_cluster_1samp_test(
12381246
%(stat_fun_clust_t)s
12391247
%(adjacency_clust_1)s
12401248
%(n_jobs)s
1241-
%(seed)s
1249+
%(rng)s
1250+
seed : None | int | instance of ~numpy.random.RandomState
1251+
Deprecated. Use ``rng`` instead.
12421252
%(max_step_clust)s
12431253
%(exclude_clust)s
12441254
%(step_down_p_clust)s
@@ -1291,6 +1301,7 @@ def permutation_cluster_1samp_test(
12911301
.. footbibliography::
12921302
"""
12931303
stat_fun, threshold = _check_fun(X, stat_fun, threshold, tail)
1304+
rng = _check_rng_compat(rng, legacy=seed, legacy_name="seed")
12941305
return _permutation_cluster_test(
12951306
X=[X],
12961307
threshold=threshold,
@@ -1299,7 +1310,7 @@ def permutation_cluster_1samp_test(
12991310
stat_fun=stat_fun,
13001311
adjacency=adjacency,
13011312
n_jobs=n_jobs,
1302-
seed=seed,
1313+
seed=rng,
13031314
max_step=max_step,
13041315
exclude=exclude,
13051316
step_down_p=step_down_p,
@@ -1328,6 +1339,8 @@ def spatio_temporal_cluster_1samp_test(
13281339
check_disjoint=False,
13291340
buffer_size=1000,
13301341
verbose=None,
1342+
*,
1343+
rng=None,
13311344
):
13321345
"""Non-parametric cluster-level paired t-test for spatio-temporal data.
13331346
@@ -1351,7 +1364,9 @@ def spatio_temporal_cluster_1samp_test(
13511364
%(stat_fun_clust_t)s
13521365
%(adjacency_clust_st1)s
13531366
%(n_jobs)s
1354-
%(seed)s
1367+
%(rng)s
1368+
seed : None | int | instance of ~numpy.random.RandomState
1369+
Deprecated. Use ``rng`` instead.
13551370
%(max_step_clust)s
13561371
spatial_exclude : list of int or None
13571372
List of spatial indices to exclude from clustering.
@@ -1397,6 +1412,7 @@ def spatio_temporal_cluster_1samp_test(
13971412
adjacency=adjacency,
13981413
n_jobs=n_jobs,
13991414
seed=seed,
1415+
rng=rng,
14001416
max_step=max_step,
14011417
exclude=exclude,
14021418
step_down_p=step_down_p,
@@ -1425,6 +1441,8 @@ def spatio_temporal_cluster_test(
14251441
check_disjoint=False,
14261442
buffer_size=1000,
14271443
verbose=None,
1444+
*,
1445+
rng=None,
14281446
):
14291447
"""Non-parametric cluster-level test for spatio-temporal data.
14301448
@@ -1450,7 +1468,9 @@ def spatio_temporal_cluster_test(
14501468
%(stat_fun_clust_f)s
14511469
%(adjacency_clust_stn)s
14521470
%(n_jobs)s
1453-
%(seed)s
1471+
%(rng)s
1472+
seed : None | int | instance of ~numpy.random.RandomState
1473+
Deprecated. Use ``rng`` instead.
14541474
%(max_step_clust)s
14551475
spatial_exclude : list of int or None
14561476
List of spatial indices to exclude from clustering.
@@ -1496,6 +1516,7 @@ def spatio_temporal_cluster_test(
14961516
adjacency=adjacency,
14971517
n_jobs=n_jobs,
14981518
seed=seed,
1519+
rng=rng,
14991520
max_step=max_step,
15001521
exclude=exclude,
15011522
step_down_p=step_down_p,

mne/stats/tests/test_cluster_level.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ def _get_conditions():
5656
return condition1_1d, condition2_1d, condition1_2d, condition2_2d
5757

5858

59+
def test_cluster_rng_transition():
60+
"""Test the transition from seed to rng."""
61+
X = np.arange(24.0).reshape(8, 3)
62+
with pytest.warns(FutureWarning, match="seed"):
63+
permutation_cluster_1samp_test(X, threshold=0, n_permutations=2, seed=0)
64+
with pytest.raises(TypeError, match="Specify only one"):
65+
permutation_cluster_1samp_test(X, threshold=0, n_permutations=2, seed=0, rng=0)
66+
67+
5968
def test_thresholds(numba_conditional):
6069
"""Test automatic threshold calculations."""
6170
# within subjects
@@ -69,7 +78,7 @@ def test_thresholds(numba_conditional):
6978
with catch_logging() as log:
7079
with pytest.warns(RuntimeWarning, match="threshold is only valid"):
7180
out = permutation_cluster_1samp_test(
72-
X, stat_fun=my_fun, seed=0, verbose=True, out_type="mask"
81+
X, stat_fun=my_fun, rng=0, verbose=True, out_type="mask"
7382
)
7483
log = log.getvalue()
7584
assert str(want_thresh)[:6] in log
@@ -86,12 +95,12 @@ def test_thresholds(numba_conditional):
8695
with catch_logging() as log:
8796
with pytest.warns(RuntimeWarning, match="threshold is only valid"):
8897
out = permutation_cluster_test(
89-
X, tail=1, stat_fun=my_fun, seed=0, verbose=True, out_type="mask"
98+
X, tail=1, stat_fun=my_fun, rng=0, verbose=True, out_type="mask"
9099
)
91100
log = log.getvalue()
92101
assert str(want_thresh)[:6] in log
93102
assert len(out[1]) == 1 # 1 cluster
94-
assert_allclose(out[2], 0.031250, atol=1e-6)
103+
assert_allclose(out[2], 0.03515625, atol=1e-6)
95104
with pytest.warns(RuntimeWarning, match='Ignoring argument "tail"'):
96105
permutation_cluster_test(X, tail=0, out_type="mask")
97106

@@ -103,7 +112,7 @@ def test_thresholds(numba_conditional):
103112
pytest.warns(RuntimeWarning, match="invalid value"),
104113
): # NumPy
105114
out = permutation_cluster_1samp_test(
106-
X, seed=0, threshold=dict(start=0, step=0.1), out_type="mask"
115+
X, rng=0, threshold=dict(start=0, step=0.1), out_type="mask"
107116
)
108117
assert (out[2] < 0.05).any()
109118
assert not (out[2] < 0.05).all()
@@ -112,7 +121,7 @@ def test_thresholds(numba_conditional):
112121
with np.errstate(invalid="ignore"):
113122
permutation_cluster_1samp_test(
114123
X,
115-
seed=0,
124+
rng=0,
116125
threshold=dict(start=0, step=0.1),
117126
buffer_size=None,
118127
out_type="mask",
@@ -137,7 +146,7 @@ def test_cache_dir(tmp_path, numba_conditional):
137146
buffer_size=None,
138147
n_jobs=2,
139148
n_permutations=1,
140-
seed=0,
149+
rng=0,
141150
stat_fun=ttest_1samp_no_p,
142151
verbose=False,
143152
out_type="mask",
@@ -152,7 +161,7 @@ def test_cache_dir(tmp_path, numba_conditional):
152161
buffer_size=10,
153162
n_jobs=2,
154163
n_permutations=1,
155-
seed=random_state,
164+
rng=random_state,
156165
stat_fun=stat_fun,
157166
verbose=False,
158167
out_type="mask",
@@ -175,7 +184,7 @@ def test_permutation_large_n_samples(numba_conditional):
175184
tails = (0, 1) if n_samples <= 20 else (0,)
176185
for tail in tails:
177186
H0 = permutation_cluster_1samp_test(
178-
X[:n_samples], threshold=1e-4, tail=tail, seed=0, out_type="mask"
187+
X[:n_samples], threshold=1e-4, tail=tail, rng=0, out_type="mask"
179188
)[-1]
180189
assert H0.shape == (1024,)
181190
assert len(np.unique(H0)) >= 1024 - (H0 == 0).sum()
@@ -221,7 +230,7 @@ def test_cluster_permutation_test(numba_conditional):
221230
[condition1, condition2],
222231
n_permutations=100,
223232
tail=1,
224-
seed=1,
233+
rng=1,
225234
buffer_size=None,
226235
out_type="mask",
227236
)
@@ -235,7 +244,7 @@ def test_cluster_permutation_test(numba_conditional):
235244
[condition1, condition2],
236245
n_permutations=100,
237246
tail=1,
238-
seed=1,
247+
rng=1,
239248
n_jobs=2,
240249
buffer_size=buffer_size,
241250
out_type="mask",
@@ -268,7 +277,7 @@ def test_cluster_permutation_t_test(numba_conditional, stat_fun):
268277
condition1,
269278
n_permutations=100,
270279
tail=0,
271-
seed=1,
280+
rng=1,
272281
out_type="mask",
273282
buffer_size=None,
274283
)
@@ -281,7 +290,7 @@ def test_cluster_permutation_t_test(numba_conditional, stat_fun):
281290
n_permutations=100,
282291
tail=1,
283292
threshold=1.67,
284-
seed=1,
293+
rng=1,
285294
stat_fun=stat_fun,
286295
out_type="mask",
287296
buffer_size=None,
@@ -292,7 +301,7 @@ def test_cluster_permutation_t_test(numba_conditional, stat_fun):
292301
n_permutations=100,
293302
tail=-1,
294303
threshold=-1.67,
295-
seed=1,
304+
rng=1,
296305
stat_fun=stat_fun,
297306
buffer_size=None,
298307
out_type="mask",
@@ -314,7 +323,7 @@ def test_cluster_permutation_t_test(numba_conditional, stat_fun):
314323
tail=-1,
315324
out_type="mask",
316325
threshold=-1.67,
317-
seed=1,
326+
rng=1,
318327
n_jobs=2,
319328
stat_fun=stat_fun,
320329
buffer_size=buffer_size,
@@ -347,7 +356,7 @@ def test_cluster_permutation_with_adjacency(numba_conditional, monkeypatch):
347356
n_pts = condition1_1d.shape[1]
348357
# we don't care about p-values in any of these, so do fewer permutations
349358
args = dict(
350-
seed=None,
359+
rng=None,
351360
max_step=1,
352361
exclude=None,
353362
out_type="mask",
@@ -610,7 +619,7 @@ def test_permutation_adjacency_equiv(numba_conditional):
610619
n_jobs=2,
611620
max_step=max_step,
612621
stat_fun=stat_fun,
613-
seed=0,
622+
rng=0,
614623
out_type="mask",
615624
)
616625
# make sure our output datatype is correct
@@ -677,7 +686,7 @@ def test_spatio_temporal_cluster_chain_merge():
677686
max_step=1,
678687
n_permutations=20,
679688
out_type="indices",
680-
seed=0,
689+
rng=0,
681690
verbose=False,
682691
)
683692
assert len(clusters) == 1
@@ -760,7 +769,7 @@ def test_spatio_temporal_cluster_adjacency(numba_conditional):
760769
adjacency=adj,
761770
n_permutations=50,
762771
tail=1,
763-
seed=1,
772+
rng=1,
764773
threshold=threshold,
765774
buffer_size=None,
766775
)
@@ -770,7 +779,7 @@ def test_spatio_temporal_cluster_adjacency(numba_conditional):
770779
[data1_2d, data2_2d],
771780
n_permutations=50,
772781
tail=1,
773-
seed=1,
782+
rng=1,
774783
threshold=threshold,
775784
n_jobs=2,
776785
buffer_size=buffer_size,
@@ -783,7 +792,7 @@ def test_spatio_temporal_cluster_adjacency(numba_conditional):
783792
[data1_2d, data2_2d],
784793
n_permutations=50,
785794
tail=1,
786-
seed=1,
795+
rng=1,
787796
threshold=threshold,
788797
n_jobs=2,
789798
buffer_size=None,
@@ -866,19 +875,19 @@ def test_permutation_test_H0(numba_conditional):
866875
data = rng.random((7, 10, 1)) - 0.5
867876
with pytest.warns(RuntimeWarning, match="No clusters found"):
868877
t, clust, p, h0 = spatio_temporal_cluster_1samp_test(
869-
data, threshold=100, n_permutations=1024, seed=rng
878+
data, threshold=100, n_permutations=1024, rng=rng
870879
)
871880
assert_equal(len(h0), 0)
872881

873882
for n_permutations in (1024, 65, 64, 63):
874883
t, clust, p, h0 = spatio_temporal_cluster_1samp_test(
875-
data, threshold=0.1, n_permutations=n_permutations, seed=rng
884+
data, threshold=0.1, n_permutations=n_permutations, rng=rng
876885
)
877886
assert_equal(len(h0), min(n_permutations, 64))
878887
assert isinstance(clust[0], tuple) # sets of indices
879888
for tail, thresh in zip((-1, 0, 1), (-0.1, 0.1, 0.1)):
880889
t, clust, p, h0 = spatio_temporal_cluster_1samp_test(
881-
data, threshold=thresh, seed=rng, tail=tail, out_type="mask"
890+
data, threshold=thresh, rng=rng, tail=tail, out_type="mask"
882891
)
883892
assert isinstance(clust[0], np.ndarray) # bool mask
884893
# same as "128 if tail else 64"

0 commit comments

Comments
 (0)