Skip to content

Commit 64b14da

Browse files
authored
[ENH] implement one-way ANOVA at group level to compare groups (#1296)
* start adding a 3 group dataset * [ENH] use sub commands for python CLI (#1292) * start adding sub commands * preproc subcommand * deal with create roi parser * deal with default_model sub command * deal with bms action * deal with stats actions * start adapting cli * add test for command building * add test for CLI for preproc, smooth... * add test for command building for stats * add test for command building for bms * rename function * start switching CLI * several fixes * more fix * keep switchingµ * fix * fix * linti * fixes * fixes * minor fixes * additional fixes * fix output ROI * visualize output in circle ci * add temp script for boutiques and bids model graph * start adding tweaks * try 3 groups * rename file * update code and tests * add multi group demo * fixes * adapt example * fix contrasts * deal with results * update doc * minor changes * tmp * refactor * refactor * refactor * several fixes * several fixes * fix and refactor * fix and refactor * fix and refactor * fix models * fix * fix * fix fast tests * fixes * fix and implement F test * run group level node by node * add F test to demo * octave fix
1 parent 9e3d18d commit 64b14da

File tree

87 files changed

+2417
-1136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+2417
-1136
lines changed

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ exclude =
99
tests/*
1010
_version.py
1111
demos/*
12+
WIP/*
1213
count = True
1314
show-source = True
1415
statistics = True

.github/workflows/run_tests_cli.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ jobs:
6060
coverage run --source src -m pytest
6161
coverage xml
6262
63-
- name: Code coverage
64-
uses: codecov/codecov-action@v4
65-
with:
66-
file: coverage.xml
67-
flags: cli
68-
name: codecov-cli
69-
fail_ci_if_error: false
63+
# - name: Code coverage
64+
# uses: codecov/codecov-action@v4
65+
# with:
66+
# file: coverage.xml
67+
# flags: cli
68+
# name: codecov-cli
69+
# fail_ci_if_error: false

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424

2525
### Added
2626

27+
* [ENH] add support for one-way ANOVA across groups at the group level #1296 by @Remi-Gau
28+
* [ENH] allow for 2 sample T-Test, within group T-Test and one-way ANOVA to ne more flexible with respect to what praticipants.tsv column to use to allocate subjects in each group #1296 by @Remi-Gau
2729
* [ENH] make `addConfoundsToDesignMatrix` a method of `BidsModel` #1294 by @Remi-Gau
2830
* [ENH] add Apptainer definition #1254 by @Remi-Gau and @monique2208
2931
* [ENH] allow to copy anat only on raw datasets #1181 by @Remi-Gau
@@ -43,7 +45,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4345

4446
### Changed
4547

46-
4748
* [ENH] align specification of F contrasts on the BIDS stats model: they should now be specified as a 2D matrix and not a 1D vector. #1276 @Remi-Gau
4849
* [DOC] change theme and structure of the documentation #1256 @Remi-Gau
4950
* [REF] Refactor and update CLI in #1096 @Remi-Gau

WIP/find_data_set_with_3_groups.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from pathlib import Path
2+
3+
import pandas as pd
4+
from pandas.errors import ParserError
5+
6+
openneuro = Path("/home/remi/datalad/datasets.datalad.org/openneuro")
7+
8+
for ds in openneuro.iterdir():
9+
file = ds / "participants.tsv"
10+
11+
if file.exists():
12+
13+
try:
14+
df = pd.read_csv(file, sep="\t")
15+
except ParserError:
16+
...
17+
18+
if len(df) < 10:
19+
continue
20+
if "group" in df.columns:
21+
col = "group"
22+
elif "Group" in df.columns:
23+
col = "Group"
24+
else:
25+
continue
26+
if len(df[col].value_counts()) < 3:
27+
continue
28+
if "age" in df.columns and df["age"].mean() < 18:
29+
continue
30+
bold_files = list(ds.glob("**/*bold*"))
31+
32+
has_func = len(bold_files) > 0
33+
if not has_func:
34+
continue
35+
36+
tasks = {x.name.split("task-")[1].split("_")[0] for x in bold_files}
37+
if len(tasks) == 1 and "rest" in tasks:
38+
continue
39+
40+
print()
41+
print(file)
42+
print(df[col].value_counts())
43+
print(tasks)

demos/openneuro/Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,13 @@ data_ds002799:
7474
datalad get -d inputs/ds002799 inputs/ds002799/derivatives/fmriprep/sub-292/*/func/*tsv
7575
datalad get -d inputs/ds002799 inputs/ds002799/derivatives/fmriprep/sub-30[27]/*/func/*MNI152NLin2009cAsym* -J 2
7676
datalad get -d inputs/ds002799 inputs/ds002799/derivatives/fmriprep/sub-30[27]/*/func/*tsv -J 2
77+
78+
data_ds003397:
79+
mkdir -p inputs
80+
cd inputs && datalad install ///openneuro/ds003397
81+
cd inputs && datalad install https://github.com/OpenNeuroDerivatives/ds003397-fmriprep
82+
cd inputs/ds003397-fmriprep && datalad get sub-[01][1267]/anat/*MNI152NLin2009cAsym*T1w.nii.gz -J 12
83+
cd inputs/ds003397-fmriprep && datalad get sub-[01][1267]/anat/*mask*.nii.gz -J 12
84+
cd inputs/ds003397-fmriprep && datalad get sub-[01][1267]/func/*time*tsv -J 12
85+
cd inputs/ds003397-fmriprep && datalad get sub-[01][1267]/func/*json -J 12
86+
cd inputs/ds003397-fmriprep && datalad get sub-[01][1267]/func/*MNI152NLin2009cAsym*desc-preproc*bold.nii.gz -J 12

demos/openneuro/ds000114_run.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
% Demo to compare activations across sessions.
2+
13
% (C) Copyright 2023 bidspm developers
24

35
clear;

demos/openneuro/ds003397_run.m

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
% Run a one-way ANOVA across group
2+
%
3+
% Only a few subjects are run because of the large size of each run.
4+
5+
% (C) Copyright 2024 bidspm developers
6+
7+
clear;
8+
clc;
9+
10+
addpath(fullfile(pwd, '..', '..'));
11+
bidspm();
12+
13+
task = 'checkerboard';
14+
15+
% The directory where the data are located
16+
root_dir = fileparts(mfilename('fullpath'));
17+
bids_dir = fullfile(root_dir, 'inputs', 'ds003397');
18+
fmriprep_dir = fullfile(root_dir, 'inputs', 'ds003397-fmriprep');
19+
output_dir = fullfile(root_dir, 'outputs', 'ds003397', 'derivatives');
20+
21+
space = {'MNI152NLin2009cAsym'};
22+
participant_label = {'01', '06', '11', '12'};
23+
24+
% Copy (& smooth)
25+
FWHM = 0;
26+
bidspm(fmriprep_dir, output_dir, 'subject', ...
27+
'participant_label', participant_label, ...
28+
'action', 'smooth', ...
29+
'task', task, ...
30+
'space', space, ...
31+
'fwhm', FWHM, ...
32+
'verbosity', 3);
33+
34+
%% Stats
35+
preproc_dir = fullfile(output_dir, 'bidspm-preproc');
36+
37+
model_file = fullfile(root_dir, ...
38+
'models', ...
39+
'model-ds003397_smdl.json');
40+
41+
bidspm(bids_dir, output_dir, 'subject', ...
42+
'participant_label', participant_label, ...
43+
'action', 'stats', ...
44+
'preproc_dir', preproc_dir, ...
45+
'model_file', model_file, ...
46+
'roi_atlas', 'hcpex', ...
47+
'space', space, ...
48+
'fwhm', 0, ...
49+
'skip_validation', true, ...
50+
'verbosity', 3);
51+
52+
bidspm(bids_dir, output_dir, 'dataset', ...
53+
'participant_label', participant_label, ...
54+
'action', 'stats', ...
55+
'preproc_dir', preproc_dir, ...
56+
'model_file', model_file, ...
57+
'roi_atlas', 'hcpex', ...
58+
'space', space, ...
59+
'fwhm', 0, ...
60+
'skip_validation', true, ...
61+
'verbosity', 3);
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
{
2+
"Name": "1_way_ANOVA",
3+
"BIDSModelVersion": "1.0.0",
4+
"Input": {
5+
"task": [
6+
"checkerboard"
7+
],
8+
"space": [
9+
"MNI152NLin2009cAsym"
10+
]
11+
},
12+
"Nodes": [
13+
{
14+
"Level": "Run",
15+
"Name": "run_level",
16+
"GroupBy": [
17+
"run",
18+
"subject"
19+
],
20+
"Model": {
21+
"Type": "glm",
22+
"X": [
23+
"trial_type.flashing checkerboard",
24+
"trans_?",
25+
"rot_?"
26+
],
27+
"HRF": {
28+
"Variables": [
29+
"trial_type.flashing checkerboard"
30+
],
31+
"Model": "spm"
32+
}
33+
},
34+
"Contrasts": [
35+
{
36+
"Name": "flashing checkerboard",
37+
"ConditionList": [
38+
"trial_type.flashing checkerboard"
39+
],
40+
"Weights": [
41+
1
42+
],
43+
"Test": "t"
44+
}
45+
]
46+
},
47+
{
48+
"Level": "Subject",
49+
"Name": "subject_level",
50+
"GroupBy": [
51+
"contrast",
52+
"subject"
53+
],
54+
"Model": {
55+
"Type": "glm",
56+
"X": [
57+
1
58+
],
59+
"Software": {
60+
"bidspm": {
61+
"Results": [
62+
{
63+
"name": [
64+
"flashing checkerboard"
65+
],
66+
"p": 0.05,
67+
"MC": "FWE",
68+
"png": true,
69+
"binary": false,
70+
"nidm": false,
71+
"montage": {
72+
"do": true,
73+
"slices": [
74+
-4,
75+
0,
76+
4,
77+
8,
78+
16
79+
],
80+
"background": {
81+
"suffix": "T1w",
82+
"desc": "preproc",
83+
"modality": "anat"
84+
}
85+
}
86+
}
87+
]
88+
}
89+
}
90+
},
91+
"DummyContrasts": {
92+
"Test": "t"
93+
}
94+
},
95+
{
96+
"Level": "Dataset",
97+
"Name": "dataset_level",
98+
"Description": "average across all subjects",
99+
"GroupBy": [
100+
"contrast"
101+
],
102+
"Model": {
103+
"Type": "glm",
104+
"X": [
105+
1
106+
]
107+
}
108+
},
109+
{
110+
"Level": "Dataset",
111+
"Name": "between_groups",
112+
"Description": "one way anova",
113+
"GroupBy": [
114+
"contrast"
115+
],
116+
"Model": {
117+
"Type": "glm",
118+
"X": [
119+
1,
120+
"group"
121+
],
122+
"Software": {
123+
"bidspm": {
124+
"Results": [
125+
{
126+
"name": [
127+
"B > I",
128+
"average across groups"
129+
],
130+
"p": 0.01,
131+
"MC": "FWE",
132+
"png": true,
133+
"binary": false,
134+
"nidm": false,
135+
"montage": {
136+
"do": false
137+
}
138+
}
139+
]
140+
}
141+
}
142+
},
143+
"Contrasts": [
144+
{
145+
"Name": "B > I",
146+
"ConditionList": [
147+
"group.B",
148+
"group.I"
149+
],
150+
"Weights": [
151+
1,
152+
-1
153+
],
154+
"Test": "t"
155+
},
156+
{
157+
"Name": "average across groups",
158+
"ConditionList": [
159+
"group.B",
160+
"group.I",
161+
"group.BI"
162+
],
163+
"Weights": [
164+
1,
165+
1,
166+
1
167+
],
168+
"Test": "t"
169+
},
170+
{
171+
"Name": "some F test",
172+
"ConditionList": [
173+
"group.B",
174+
"group.BI",
175+
"group.I"
176+
],
177+
"Weights": [
178+
[
179+
1,
180+
0,
181+
0
182+
],
183+
[
184+
0,
185+
1,
186+
0
187+
],
188+
[
189+
0,
190+
0,
191+
1
192+
]
193+
],
194+
"Test": "F"
195+
}
196+
]
197+
}
198+
],
199+
"Edges": [
200+
{
201+
"Source": "run_level",
202+
"Destination": "subject_level"
203+
},
204+
{
205+
"Source": "subject_level",
206+
"Destination": "dataset_level"
207+
},
208+
{
209+
"Source": "subject_level",
210+
"Destination": "between_groups",
211+
"Filter": {
212+
"contrast": [
213+
"flashing checkerboard"
214+
]
215+
}
216+
}
217+
]
218+
}

0 commit comments

Comments
 (0)