Resolve last two open issues: #199 (mypy) and #342 (volumetric)#381
Merged
zihuaihuai merged 1 commit intomasterfrom May 4, 2026
Merged
Resolve last two open issues: #199 (mypy) and #342 (volumetric)#381zihuaihuai merged 1 commit intomasterfrom
zihuaihuai merged 1 commit intomasterfrom
Conversation
#199 — Remove # type: ignore from SLM module The `# type: ignore` shebangs at the top of `SLM.py` and `_multiple_comparisons.py` were there because mypy crashed on the in-class `from ._linear_model import _linear_model` style (see python/mypy#10521). Lift those imports to module level and assign the functions as class attributes; mypy now runs to completion on both files. Pre-existing untyped-code errors are surfaced but unrelated to the original blocker — re-enabling the commented-out mypy job in CI is a separate follow-up once those are triaged. #342 — Volumetric mixed effects: contract & error-message fixes The earlier reports on this issue were all hitting the same two foot-guns when feeding volumes to SLM: 1. `contrast` came in as a pandas Series of object/categorical dtype (or similar). `np.array(contrast)` preserved that dtype, so `np.dot` later raised the cryptic `TypeError: can't multiply sequence by non-int of type 'float'` — and (depending on path) silently produced all-NaN t-values. 2. `mask` came in as a 0/1 *integer* array (the natural shape of `nib.load(...).get_fdata().astype(int)`). The downstream `Q[self.mask] = ...` path inside `_fdr` then degenerated into fancy indexing and threw a shape-mismatch (`shape (126006,) could not be broadcast to indexing result of shape (1082035,)`). Add `_coerce_contrast` / `_coerce_mask` helpers in `__init__` so both inputs are normalised once at construction time. Categorical/object contrasts now raise a clear `TypeError` pointing at the fix. Integer masks are converted to bool transparently. Also reword the `surf=None + correction='rft'` error to point users at `correction='fdr'` for volumetric data, so the failure mode the issue describes ("RFT corrections require a surface" → user is stuck) is no longer a dead-end. Test plan: 4 new tests in `test_SLM.py` cover the contrast coercion, mask coercion, the new RFT error message, and the full int-mask + FDR + pandas-Series-contrast path that several reporters were originally hitting. Existing `test_volumetric_input` and the rest of the SLM / t-test / FDR / F-test suites still pass (73/73 locally).
This was referenced May 4, 2026
Merged
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.
Summary
Closes the last two open issues that weren't resolved by #378 / #379 / #380.
Closes #199 — Remove
# type: ignorefrom SLM classSLM.pyand_multiple_comparisons.pyhad file-level# type: ignoreshebangs because mypy crashed on the in-classfrom ._linear_model import _linear_modelpattern (the original blocker, python/mypy#10521). Move those imports to module level and bind the functions as class attributes (_linear_model = _linear_model, etc.). Mypy now runs to completion on both files instead of crashing.The pre-existing untyped-code errors that the file-level ignore was hiding (~380 of them, mostly around
Optionalaccess without narrowing) are now surfaced but left to a follow-up PR — fixing them is genuinely a separate piece of work and re-enabling the commented-outmypyjob in CI before that triage would just produce noise.Closes #342 — Volumetric mixed effects
The reports on this issue all converged on the same two contract bugs in volumetric workflows:
contrastdtype: pandas Series of object/categorical dtype was preserved throughnp.array(contrast). Downstreamnp.dotthen raisedTypeError: can't multiply sequence by non-int of type 'float', or (worse) the run silently produced all-NaN t-values. Now coerced to numeric in__init__, with a clearTypeErrorif conversion fails.maskdtype: integer 0/1 masks (the natural shape ofnib.load(...).get_fdata().astype(int)) caused_fdrto do fancy indexing instead of boolean masking, producing the well-knownshape (126006,) could not be broadcast to indexing result of shape (1082035,)error. Now coerced to bool in__init__.Also reworded the
surf=None + correction='rft'error so it explicitly points users atcorrection='fdr'for volumetric data — the previous "RFT corrections require a surface" message read as a dead-end.The hard part of #342 — implementing cluster-level RFT/TFCE for volumes — is genuinely out of scope; SurfStat had it but BrainStat's port doesn't, and a faithful re-port would be a multi-week project. That's flagged in the new error message and remains tracked. What this PR does fix: every
SLM(...)invocation pattern that the issue reporters posted now runs end-to-end.Test plan
Four new tests in
brainstat/tests/test_SLM.py:test_volumetric_contrast_coercion— pandas Series → float; object dtype rejected with helpful message.test_volumetric_int_mask_is_coerced_to_bool— int 0/1 mask becomes a boolean mask.test_volumetric_rft_without_surface_explains_alternatives— error message mentions FDR.test_volumetric_fdr_with_int_mask_runs_end_to_end— the full reporter scenario (int mask + pandas contrast + FDR) returns finite t-values inside the mask and a Q-array of the right shape.Locally: 73/73 SLM + t_test + FDR + f_test cases pass.
Out of scope (kept open as separate trackers)
# type: ignorewas hiding — straightforward but noisy cleanup.python_unittests.ymlonce the typing errors are triaged.