Cohort features#

How modewise turns many recordings’ results into one table, the handover to statistics (S8b) and to machine learning outside modewise.

Participants#

A dataset spec’s [participants] section reads participants.tsv and any phenotype tables, joined on the subject entity (docs/datasets.md). In Python, Dataset.participants has .columns, .subjects, .value(subject, column) and .to_pandas().

  • Text first: values stay text until a number is needed, so group labels and scores live side by side.

  • Missing codes: empty cells, n/a, NA, and any code listed under missing (such as -9 or 999) mean “no value”.

  • Derived scores stay out of core: a total recomputed from items, or a group defined by thresholds, is a dataset rule. Compute it in Python or an example script (as examples/tcp/make_participants.py does for the thesis’s anhedonia grouping), then join it as one more table.

The feature table#

Results.feature_table(pipeline, output, features, participants=...) builds one table over every recording of a pipeline:

  • Rows: one per recording. When the output was computed on epochs, one per recording and condition (the epochs’ event label, such as face and shape), in order of first appearance. For band_amplitude, one per recording and band.

  • Columns: recording, subject (taken from the recording id), condition, the participant columns, then the features.

features

From

Feature names

Values

edges

functional connectivity

m{k}:{a}~{b}: the upper triangle per component

Pearson r becomes Fisher z, so values can be averaged and tested; Fisher z and PLV stay as they are. A condition’s epochs are averaged.

mode_power

a decomposition, or its epochs

m{k}:{channel} and m{k}:centre_hz

each mode’s variance per channel, and its centre frequency; averaged over a condition’s epochs

mean

epochs of a series

{channel}, with the epoch label as the condition

the mean signal in each condition’s epochs (each epoch’s mean counts once): block averages for task contrasts

band_amplitude

a spectrum or time-frequency map; bands={"slow5": (0.01, 0.027), ...}

{channel}, with the band as the condition

the mean amplitude (√power) over the band’s bins [low, high) and all frames: ALFF. With relative=True, the band’s summed amplitude over that of every frequency above 0: fALFF.

Band amplitude. ALFF is the mean square-root power in a band (Zang et al. 2007); fALFF divides the band’s amplitude by that of the whole spectrum (Zou et al. 2008), which reduces the influence of physiological noise in non-grey-matter regions. Zuo et al. (2010) split the classic 0.01–0.08 Hz range into slow-5 (0.01–0.027 Hz) and slow-4 (0.027–0.073 Hz) and found their balance differs between regions. Bands are half-open, so adjacent bands share no bin. Because bands are conditions, test_paired(table, ("slow4", "slow5")) compares them within each channel, as Zuo et al.’s paired test does. ALFF is in units of √(power per Hz); compare it within a study, or use fALFF across studies.

Resolvable epochs only. By default (resolvable_only=True), an epoch too short to hold a component (docs/epochs.md) is left out of that component’s average. A condition with no usable epoch gives NaN. Pass resolvable_only=False to keep every epoch.

Modes are matched by rank. m0 is each recording’s lowest-frequency mode. Decompositions of different recordings need not share centre frequencies, so m{k}:centre_hz is included for checking alignment. Analyses that need identical bands across subjects should reconstruct fixed bands first (docs/connectivity.md).

Your own features#

Values you compute yourself (ratios between bands, contrasts between regions, anything from numpy) can go to the same tests: build a FeatureTable from them.

import numpy as np
import modewise

ratios = np.random.default_rng(0).standard_normal((8, 2))
table = modewise.FeatureTable(
    ratios, ["striatum", "cortex"], [f"sub-{i}" for i in range(8)],
    subjects=[str(i) for i in range(8)],
    metadata={"group": ["patient", "control"] * 4},
)
report = modewise.test_groups(table, "group", ("patient", "control"), n_permutations=1000)
print(report.feature_names, report.p_permutation.round(3))

Rows labelled with conditions work with test_paired; participants= joins a participants table as feature_table does.

Handover#

Target

How

Python statistics and scikit-learn

table.values (NumPy), table.feature_names, table.metadata, or table.to_pandas()

Parquet

table.to_pandas().to_parquet(path) (pandas writes it)

MATLAB, R, spreadsheets

table.to_tsv(path), with missing values written as n/a (BIDS)

Deep learning on whole arrays (spectra, modes, matrices)

recordings, array = results.stack(pipeline, output) stacks one output across recordings, recording first. Shapes must match.

Statistical tests run directly on the table (docs/statistics.md). Classifiers are not part of modewise: the table is the contract, and models live in the analysis code that uses it.