Fix NaN and inf in compute_dataset_statistics - #1134
Open
adityasingh2400 wants to merge 1 commit into
Open
Conversation
compute_pvals_wilcoxon called scipy.stats.wilcoxon on paired differences
that are all zero, where the test is undefined. Old SciPy raised
ValueError, which is the traceback in the report. Current SciPy returns
1.0 from its exact method but NaN from the normal approximation it
switches to on larger samples, so the result silently depended on the
number of subjects. Report the degenerate pair as a one-tailed p-value of
0.5 in both directions, which is what the exact method already yielded,
and keep p strictly inside (0, 1) the way the permutation branch does so
Stouffer's method cannot see an infinite z-score.
compute_effect divided the mean paired difference by its standard
deviation without checking for zero spread, giving 0/0 for identical
pipelines and c/0 for a constant offset. Report identical pipelines as a
zero effect and make the unbounded case an explicit signed infinity
instead of a division accident.
find_significant_differences logged 'NaN p-value found, turned to 1' but
the assignment that would have done so was commented out, with a bare
print('NaN') in its place. Apply the fallback and drop the print.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #678.
The traceback in the report is a hard
ValueErrorfromscipy.stats.wilcoxon. That crash no longer happens on current SciPy, but the underlying defect did not go away, it got quieter and became sample size dependent. Verified on SciPy 1.18.0, NumPy 2.5.1, pandas 3.0.5:SciPy's exact method returns 1.0, and the normal approximation it switches to on larger samples returns NaN.
compute_dataset_statisticsonly takes the Wilcoxon branch when there are at leastperm_cutoffsubjects, so in practice this straddles the crossover point and the answer silently depends on the subject count.Reproducer with two pipelines that score identically, no dataset download needed:
and
find_significant_differenceson that table writes a bareNaNto stdout twice and leaves the NaN in the matrix.Three sites, one root cause, all of them degenerate paired differences:
compute_pvals_wilcoxoncalledstats.wilcoxonon differences that are all zero, where the test is undefined. The pair is now reported as a one tailed p value of 0.5 in both directions, which is exactly what the exact method already yields, so nothing changes where the function currently works. The branch also keeps p strictly inside(0, 1), which is the invariant_pairedttest_exactand_pairedttest_randomalready maintain and which the two existingcannot_be_zerotests pin down. It matters becausecombine_pvaluesuses Stouffer's method, and I confirmedcombine_pvaluesmaps p=0 to z=+inf and p=1 to z=-inf while the adjacent representable values give finite z.compute_effectdivided the mean paired difference by its standard deviation with no zero check, giving 0/0 for identical pipelines and c/0 for a constant offset. Identical pipelines now report a zero effect. The constant offset case really is an unbounded effect, so it keeps its sign and becomes an explicit infinity rather than a division accident, and no RuntimeWarning is emitted either way.find_significant_differenceslogged "NaN p-value found, turned to 1" but the line that would have done so,P[i, j] = 1.0, was commented out, withprint("NaN")standing in for it. The fallback is applied now and the print is gone.One judgement call worth flagging: I kept the constant offset effect as a signed infinity rather than clamping it, because that is the honest answer and it stays contained to the degenerate pair instead of spreading across the matrix. Happy to change the convention if you would rather see something finite there.
Coverage added to
TestStats, 8 new tests. Note thatstd == 0is a knife edge condition.0.9 - 0.7repeated 10 times gives a std of 2.9e-17 while the same values at n=25 give exactly 0, which matches @toncho11's remark about the non deterministic nature of this. The tests use0.75 - 0.5, which is exact in binary.Reverting
moabb/analysis/meta_analysis.pyto develop makes 7 of the new tests fail. All 14 pass after, and the whole file is green at 23 passed.ruff check moabb/reports all checks passed, andruff formatis applied. A changelog entry is added under Bugs indocs/source/whats_new.rstalong with an author link.Disclosure: this change was prepared with AI assistance. I have reviewed and tested it.