@@ -130,14 +130,27 @@ def _estimate_rank_from_s(s, tol="auto", tol_kind="absolute"):
130
130
131
131
132
132
def _estimate_rank_raw (
133
- raw , picks = None , tol = 1e-4 , scalings = "norm" , with_ref_meg = False , tol_kind = "absolute"
133
+ raw ,
134
+ picks = None ,
135
+ tol = 1e-4 ,
136
+ scalings = "norm" ,
137
+ with_ref_meg = False ,
138
+ tol_kind = "absolute" ,
139
+ on_few_samples = "warn" ,
134
140
):
135
141
"""Aid the transition away from raw.estimate_rank."""
136
142
if picks is None :
137
143
picks = _picks_to_idx (raw .info , picks , with_ref_meg = with_ref_meg )
138
144
# conveniency wrapper to expose the expert "tol" option + scalings options
139
145
return _estimate_rank_meeg_signals (
140
- raw [picks ][0 ], pick_info (raw .info , picks ), scalings , tol , False , tol_kind
146
+ raw [picks ][0 ],
147
+ pick_info (raw .info , picks ),
148
+ scalings ,
149
+ tol ,
150
+ False ,
151
+ tol_kind ,
152
+ log_ch_type = None ,
153
+ on_few_samples = on_few_samples ,
141
154
)
142
155
143
156
@@ -150,6 +163,7 @@ def _estimate_rank_meeg_signals(
150
163
return_singular = False ,
151
164
tol_kind = "absolute" ,
152
165
log_ch_type = None ,
166
+ on_few_samples = "warn" ,
153
167
):
154
168
"""Estimate rank for M/EEG data.
155
169
@@ -173,6 +187,10 @@ def _estimate_rank_meeg_signals(
173
187
to determine the rank.
174
188
tol_kind : str
175
189
Tolerance kind. See ``estimate_rank``.
190
+ on_few_samples : str
191
+ Can be 'warn' (default), 'ignore', or 'raise' to control behavior when
192
+ there are fewer samples than channels, which can lead to inaccurate rank
193
+ estimates.
176
194
177
195
Returns
178
196
-------
@@ -183,11 +201,14 @@ def _estimate_rank_meeg_signals(
183
201
thresholded to determine the rank are also returned.
184
202
"""
185
203
picks_list = _picks_by_type (info )
186
- if data .shape [1 ] < data .shape [0 ]:
187
- ValueError (
188
- "You've got fewer samples than channels, your "
189
- "rank estimate might be inaccurate."
204
+ assert data .ndim == 2 , data .shape
205
+ n_channels , n_samples = data .shape
206
+ if n_samples < n_channels :
207
+ msg = (
208
+ f"Too few samples ({ n_samples = } is less than { n_channels = } ), "
209
+ "rank estimate may be unreliable"
190
210
)
211
+ _on_missing (on_few_samples , msg , "on_few_samples" )
191
212
with _scaled_array (data , picks_list , scalings ):
192
213
out = estimate_rank (
193
214
data ,
@@ -214,6 +235,7 @@ def _estimate_rank_meeg_cov(
214
235
return_singular = False ,
215
236
* ,
216
237
log_ch_type = None ,
238
+ on_few_samples = "warn" ,
217
239
verbose = None ,
218
240
):
219
241
"""Estimate rank of M/EEG covariance data, given the covariance.
@@ -236,6 +258,10 @@ def _estimate_rank_meeg_cov(
236
258
return_singular : bool
237
259
If True, also return the singular values that were used
238
260
to determine the rank.
261
+ on_few_samples : str
262
+ Can be 'warn' (default), 'ignore', or 'raise' to control behavior when
263
+ there are fewer samples than channels, which can lead to inaccurate rank
264
+ estimates.
239
265
240
266
Returns
241
267
-------
@@ -249,10 +275,11 @@ def _estimate_rank_meeg_cov(
249
275
scalings = _handle_default ("scalings_cov_rank" , scalings )
250
276
_apply_scaling_cov (data , picks_list , scalings )
251
277
if data .shape [1 ] < data .shape [0 ]:
252
- ValueError (
278
+ msg = (
253
279
"You've got fewer samples than channels, your "
254
280
"rank estimate might be inaccurate."
255
281
)
282
+ _on_missing (on_few_samples , msg , "on_few_samples" )
256
283
out = estimate_rank (data , tol = tol , norm = False , return_singular = return_singular )
257
284
rank = out [0 ] if isinstance (out , tuple ) else out
258
285
if log_ch_type is None :
@@ -325,7 +352,7 @@ def _compute_rank_int(inst, *args, **kwargs):
325
352
# XXX eventually we should unify how channel types are handled
326
353
# so that we don't need to do this, or we do it everywhere.
327
354
# Using pca=True in compute_whitener might help.
328
- return sum (compute_rank (inst , * args , ** kwargs ).values ())
355
+ return sum (compute_rank (inst , * args , on_few_samples = "ignore" , ** kwargs ).values ())
329
356
330
357
331
358
@verbose
@@ -335,9 +362,11 @@ def compute_rank(
335
362
scalings = None ,
336
363
info = None ,
337
364
tol = "auto" ,
365
+ * ,
338
366
proj = True ,
339
367
tol_kind = "absolute" ,
340
368
on_rank_mismatch = "ignore" ,
369
+ on_few_samples = None ,
341
370
verbose = None ,
342
371
):
343
372
"""Compute the rank of data or noise covariance.
@@ -363,6 +392,13 @@ def compute_rank(
363
392
considered when ``rank=None`` or ``rank='info'``.
364
393
%(tol_kind_rank)s
365
394
%(on_rank_mismatch)s
395
+ on_few_samples : str | None
396
+ Can be 'warn', 'ignore', or 'raise' to control behavior when
397
+ there are fewer samples than channels, which can lead to inaccurate rank
398
+ estimates. None (default) means "ignore" if ``inst`` is a
399
+ :class:`mne.Covariance` or ``rank in ("info", "full")``, and "warn" otherwise.
400
+
401
+ .. versionadded:: 1.11
366
402
%(verbose)s
367
403
368
404
Returns
@@ -384,6 +420,7 @@ def compute_rank(
384
420
proj = proj ,
385
421
tol_kind = tol_kind ,
386
422
on_rank_mismatch = on_rank_mismatch ,
423
+ on_few_samples = on_few_samples ,
387
424
)
388
425
389
426
@@ -398,6 +435,7 @@ def _compute_rank(
398
435
proj = True ,
399
436
tol_kind = "absolute" ,
400
437
on_rank_mismatch = "ignore" ,
438
+ on_few_samples = None ,
401
439
log_ch_type = None ,
402
440
verbose = None ,
403
441
):
@@ -441,6 +479,12 @@ def _compute_rank(
441
479
if rank is None :
442
480
rank = dict ()
443
481
482
+ if on_few_samples is None :
483
+ if inst_type != "covariance" and rank_type == "estimated" :
484
+ on_few_samples = "warn"
485
+ else :
486
+ on_few_samples = "ignore"
487
+
444
488
simple_info = _simplify_info (info )
445
489
picks_list = _picks_by_type (info , meg_combined = True , ref_meg = False , exclude = "bads" )
446
490
for ch_type , picks in picks_list :
@@ -503,6 +547,7 @@ def _compute_rank(
503
547
False ,
504
548
tol_kind ,
505
549
log_ch_type = log_ch_type ,
550
+ on_few_samples = on_few_samples ,
506
551
)
507
552
else :
508
553
assert isinstance (inst , Covariance )
@@ -520,6 +565,7 @@ def _compute_rank(
520
565
tol ,
521
566
return_singular = True ,
522
567
log_ch_type = log_ch_type ,
568
+ on_few_samples = on_few_samples ,
523
569
verbose = est_verbose ,
524
570
)
525
571
if ch_type in rank :
0 commit comments