nispace.stats.misc.permute_groups

nispace.stats.misc.permute_groups(groups, strategy='shuffle', paired=False, subjects=None, n_perm=1, n_proc=1, seed=None, verbose=False)[source]

Permute group-membership labels for group-comparison null distributions.

Plain numpy/pandas + joblib (no numba); operates on discrete group labels, not continuous data, so NaN handling doesn’t apply. Used internally by api.py’s group-comparison null generation (groups_* kwargs of permute()).

Parameters:
  • groups (array-like) – Group/session labels, length n_samples.

  • strategy (str, default="shuffle") –

    Must contain one of "shuff", "draw" (unpaired only), or "prop". What each does depends on paired:

    • "shuff"unpaired: one free random permutation of the whole groups vector (rng.permutation) – the classic permutation-test null, exchanging labels with no constraint beyond the overall label counts staying fixed. paired: an independent random permutation of each subject’s own labels across their sessions (a free per-subject sign-flip) – every one of the n_labels! orderings is equally likely for every subject, independently of every other subject.

    • "prop" (proportional) – unpaired: iteratively assigns each new group the same proportion of samples from every original group as that original group’s overall share of the sample, so the permuted groups reproduce the original composition as closely as floor-rounding allows. paired: splits subjects into exactly floor(n_subjects / n_labels!) blocks, one per possible session-label ordering (for 2 sessions: exactly half the subjects keep the original order, half get the fully swapped order) – a fixed, balanced split, not a free per-subject choice. This mirrors JuSpace’s own exact-permutation scheme [2] (compute_exact_pvalue.m): the unpaired “es between” case (options(1)==1) draws a fixed, round()-determined count from each original group’s pool into each new group (not a free permutation), and the paired “es within” case (options(1)==2) uses a fixed vector v = [ones(floor(N/2),1); 2*ones(floor(N/2)+1,1)] to force an exact half-swap/half-keep split, randomized only in which subjects land in which half – the same restriction as NiSpace’s paired "prop" above.

    • "draw"unpaired only: draws groups with replacement (rng.choice(..., replace=True)), a bootstrap-style resample rather than a strict relabeling (label counts are not preserved exactly, and repeats are possible).

    Default is ``”shuffle”``, not ``”prop”``, because ``”prop”``’s fixed (unpaired) / exactly-balanced (paired) restriction shrinks the space of achievable permutations relative to the free ``”shuffle”`` scheme, which narrows the resulting null distribution and inflates the false positive rate. This was empirically confirmed via docs/nb_benchmarks/bench03_group_permutation_fpr.ipynb (GRF-based FPR benchmark for permute()’s what="groups" mode): with strategy="proportional", FPR was inflated for both paired and unpaired designs, worst for paired at small n_subjects (e.g. FPR ~0.09 at n_subjects=10 vs. nominal 0.05, shrinking towards ~0.06 by n_subjects=30 but never fully resolving) – consistent with the paired case’s exact 50/50 split being the more restrictive of the two "prop" variants. Switching both paired and unpaired to "shuffle" resolved this completely (FPR ~0.05 across all tested n_subjects, with no residual trend). "prop"/"draw" remain available for cases that specifically need a fixed-composition or bootstrap-style null.

  • paired (bool, default=False) – If True, permute within each subject (across that subject’s own group/session labels) instead of across the whole sample; requires subjects.

  • subjects (array-like, optional) – Subject identifier per sample, same length as groups. Required if paired=True.

  • n_perm (int, default=1) – Number of permuted label vectors to generate.

  • n_proc (int, default=1) – Number of parallel jobs (joblib).

  • seed (int, optional) – Base random seed; each permutation draws from seed + i.

  • verbose (bool or "debug", optional) – Show a progress bar; “debug” additionally prints per-group sample counts for the “prop” strategy (permutation 0 only).

Returns:

Permuted group-label vector(s), same dtype as groups. A single array if n_perm == 1, otherwise a list of length n_perm.

Return type:

ndarray or list of ndarray

Raises:

ValueError – If paired=True without subjects, if group sizes are unequal across sessions in the paired case, or if strategy doesn’t match a known mode.

References

[2].