Skip to content

Commit 8de2af8

Browse files
authored
[ENH] add QA workflow to summarize QA of functional data (#1299)
* start adding QA workflow * start adding QA workflow * update changelog * move files to slow * activate codecov tokens * update doc * fix set up
1 parent 64b14da commit 8de2af8

File tree

19 files changed

+413
-62
lines changed

19 files changed

+413
-62
lines changed

.github/workflows/tests.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ jobs:
117117
unzip download
118118
mv moae_fmriprep fmriprep
119119
120+
- name: Get data for testing QA
121+
if: matrix.os == 'ubuntu-latest'
122+
run: |
123+
cd demos/openneuro/
124+
make data_ds000114_mriqc
125+
make data_ds000114_fmriprep
126+
120127
- name: Prepare test data unix
121128
run: |
122129
cd tests
@@ -156,7 +163,7 @@ jobs:
156163
flags: ${{ matrix.os }}_matlab-${{ matrix.matlab }}_${{ matrix.mode }}
157164
name: codecov-matlab
158165
fail_ci_if_error: false
159-
# token: ${{ secrets.CODECOV_TOKEN }} # not required but might help API rate limits
166+
token: ${{ secrets.CODECOV_TOKEN }}
160167

161168
- name: Run system tests MATLAB ${{ matrix.script }}
162169
if: matrix.test_type == 'system'

.github/workflows/tests_octave.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs:
6161
- name: Install dependencies
6262
run: |
6363
sudo apt-get -y -qq update
64-
sudo apt-get -y install unzip wget
64+
sudo apt-get -y install unzip wget git-annex
6565
6666
- name: Install Node
6767
uses: actions/setup-node@v4
@@ -79,6 +79,11 @@ jobs:
7979
submodules: recursive
8080
fetch-depth: 0
8181

82+
- name: Install datalad
83+
run: |
84+
python -m pip install --upgrade pip setuptools
85+
pip install datalad
86+
8287
- name: Install validators
8388
run: make install
8489

@@ -94,6 +99,12 @@ jobs:
9499
unzip download
95100
mv moae_fmriprep fmriprep
96101
102+
- name: Get data for testing QA
103+
run: |
104+
cd demos/openneuro/
105+
make data_ds000114_mriqc
106+
make data_ds000114_fmriprep
107+
97108
- name: Prepare test data
98109
run: |
99110
cd tests
@@ -149,7 +160,7 @@ jobs:
149160
flags: octave
150161
name: codecov-octave_${{ matrix.mode }}
151162
fail_ci_if_error: false
152-
# token: ${{ secrets.CODECOV_TOKEN }} # not required but might help API rate limits
163+
# token: ${{ secrets.CODECOV_TOKEN }}
153164

154165
- name: Run system tests octave ${{ matrix.script }}
155166
if: matrix.test_type == 'system'

.github/workflows/tests_windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ jobs:
131131
flags: ${{ matrix.os }}_matlab-${{ matrix.matlab }}_${{ matrix.mode }}
132132
name: codecov-matlab
133133
fail_ci_if_error: false
134-
# token: ${{ secrets.CODECOV_TOKEN }} # not required but might help API rate limits
134+
# token: ${{ secrets.CODECOV_TOKEN }}
135135

136136
- name: Run system tests MATLAB ${{ matrix.script }}
137137
if: matrix.test_type == 'system'

CHANGELOG.md

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

2525
### Added
2626

27+
* [ENH] add QA workflow `bidsQA` to find outliers in MRIQC output or to view number of outlier timepoints (for a given metric and threshold) in each functional run #1297 by @Remi-Gau
2728
* [ENH] add support for one-way ANOVA across groups at the group level #1296 by @Remi-Gau
2829
* [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
2930
* [ENH] make `addConfoundsToDesignMatrix` a method of `BidsModel` #1294 by @Remi-Gau

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ then bidspm has some automated workflows to perform amongst other things:
153153
All (well almost all) preprocessed outputs are saved as BIDS derivatives
154154
with BIDS compliant filenames.
155155

156-
### Quality control:
156+
### Quality control
157157

158158
- anatomical data (work in progress)
159159
- functional data (work in progress)

demos/openneuro/Makefile

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,40 @@ clean:
1414

1515
data: data_ds000001 data_ds000114 data_ds001168
1616

17-
data_ds000001:
17+
data_ds000001_fmriprep:
18+
cd inputs && datalad install ///openneuro-derivatives/ds000001-fmriprep
19+
cd inputs/ds000001-fmriprep && datalad get sub-*/func/*tsv -J 12
20+
cd inputs/ds000001-fmriprep && datalad get sub-*/func/*json -J 12
21+
22+
data_ds000001: data_ds000001_fmriprep
1823
mkdir -p inputs
1924
cd inputs && datalad install ///openneuro/ds000001
20-
cd inputs && datalad install ///openneuro-derivatives/ds000001-fmriprep
2125
cd inputs/ds000001 && datalad get sub-0[1-5] -J 3
22-
cd inputs/ds000001-fmriprep && datalad get sub-0[1-5]/func/*tsv -J 12
23-
cd inputs/ds000001-fmriprep && datalad get sub-0[1-5]/func/*json -J 12
2426
cd inputs/ds000001-fmriprep && datalad get sub-0[1-5]/anat/*MNI*desc-preproc*.nii.gz -J 12
2527
cd inputs/ds000001-fmriprep && datalad get sub-0[1-5]/func/*MNI*desc-preproc*.nii.gz -J 12
2628
cd inputs/ds000001-fmriprep && datalad get sub-0[1-3]/func/*MNI*desc-*bold.nii.gz -J 12
2729

2830
data_ds000114_raw:
2931
mkdir -p inputs
3032
cd inputs && datalad install ///openneuro/ds000114
31-
cd inputs/ds000114 && datalad get sub-0[1-2]/ses-*/anat/*T1w*.nii.gz -J 12
32-
cd inputs/ds000114 && datalad get sub-0[1-2]/ses-*/func/*linebisection* -J 12
33+
cd inputs/ds000114 && datalad get sub-0[1-5]/ses-*/anat/*T1w*.nii.gz -J 12
34+
cd inputs/ds000114 && datalad get sub-0[1-5]/ses-*/func/*bold* -J 12
3335

34-
data_ds000114:
36+
data_ds000114_mriqc:
37+
mkdir -p inputs
38+
cd inputs && datalad install ///openneuro-derivatives/ds000114-mriqc
39+
cd inputs/ds000114-mriqc && datalad get *.tsv
40+
41+
data_ds000114_fmriprep:
3542
mkdir -p inputs
36-
cd inputs && datalad install ///openneuro/ds000114
3743
cd inputs && datalad install ///openneuro-derivatives/ds000114-fmriprep
44+
cd inputs/ds000114-fmriprep && datalad get sub-0*/ses-*/func/*tsv -J 12
45+
cd inputs/ds000114-fmriprep && datalad get sub-0*/ses-*/func/*json -J 12
46+
47+
data_ds000114: data_ds000114_fmriprep
48+
mkdir -p inputs
49+
cd inputs && datalad install ///openneuro/ds000114
3850
cd inputs/ds000114-fmriprep && datalad get sub-0[1-2]/anat/*MNI*desc-preproc*.nii.gz -J 12
39-
cd inputs/ds000114-fmriprep && datalad get sub-0[1-2]/ses-*/func/*tsv -J 12
40-
cd inputs/ds000114-fmriprep && datalad get sub-0[1-2]/ses-*/func/*json -J 12
4151
cd inputs/ds000114-fmriprep && datalad get sub-0[1-2]/ses-*/func/*MNI*_mask.nii.gz -J 12
4252
cd inputs/ds000114-fmriprep && datalad get sub-0[1-2]/ses-*/func/*MNI*desc-preproc*bold.nii.gz -J 12
4353

demos/openneuro/ds000114_preproc_run.m

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
% Show how to use filter to only include one session to preprocess.
2+
13
% (C) Copyright 2023 bidspm developers
24

35
clear;
@@ -6,24 +8,29 @@
68
addpath(fullfile(pwd, '..', '..'));
79
bidspm();
810

9-
participant_label = {'01'};
11+
participant_label = {'04', '05'};
1012
TASK = 'linebisection';
13+
session_to_select = {'test'};
1114

1215
root_dir = fileparts(mfilename('fullpath'));
1316
bids_dir = fullfile(root_dir, 'inputs', 'ds000114');
1417
output_dir = fullfile(root_dir, 'outputs', 'ds000114', 'derivatives');
1518
preproc = fullfile(output_dir, 'bidspm-preproc');
1619

20+
%% Copy
21+
% To make sure we still have copied all the data
1722
bidspm(bids_dir, output_dir, 'subject', ...
1823
'participant_label', participant_label, ...
1924
'action', 'copy', ...
20-
'task', TASK, ...
2125
'skip_validation', true, ...
2226
'verbosity', 3);
2327

24-
bids_filter_file = struct( ...
25-
'bold', struct('modality', 'func', 'suffix', 'bold', 'ses', 'retest'), ...
26-
't1w', struct('modality', 'anat', 'suffix', 'T1w'));
28+
%% Set filter and preprocess
29+
bids_filter_file = struct('bold', struct('modality', 'func', ...
30+
'suffix', 'bold', ...
31+
'ses', {session_to_select}), ...
32+
't1w', struct('modality', 'anat', ...
33+
'suffix', 'T1w'));
2734
bidspm(bids_dir, output_dir, 'subject', ...
2835
'participant_label', participant_label, ...
2936
'action', 'preprocess', ...

docs/source/API.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ workflows
3333
.. autofunction:: src.workflows.bidsCheckVoxelSize
3434
.. autofunction:: src.workflows.bidsCopyInputFolder
3535
.. autofunction:: src.workflows.bidsInverseNormalize
36-
.. autofunction:: src.workflows.bidsQApreproc
3736
.. autofunction:: src.workflows.bidsRename
3837
.. autofunction:: src.workflows.bidsReport
3938

@@ -42,6 +41,13 @@ roi
4241
.. autofunction:: src.workflows.roi.bidsCreateROI
4342
.. autofunction:: src.workflows.roi.bidsRoiBasedGLM
4443

44+
QA
45+
--
46+
.. autofunction:: src.workflows.QA.bidsQA
47+
.. autofunction:: src.workflows.QA.bidsQAbidspm
48+
.. autofunction:: src.workflows.QA.bidsQAmriqc
49+
.. autofunction:: src.workflows.QA.bidsQApreproc
50+
4551
lesion
4652
------
4753
.. autofunction:: src.workflows.lesion.bidsLesionAbnormalitiesDetection
@@ -103,7 +109,6 @@ stats
103109
.. autofunction:: src.batches.stats.setBatchSubjectLevelContrasts
104110
.. autofunction:: src.batches.stats.setBatchSubjectLevelGLMSpec
105111
.. autofunction:: src.batches.stats.setBatchSubjectLevelResults
106-
.. autofunction:: src.batches.stats.setBatchTwoSampleTTest
107112

108113
preproc
109114
-------
@@ -157,7 +162,6 @@ QA
157162
.. autofunction:: src.QA.computeFDandRMS
158163
.. autofunction:: src.QA.computeRobustOutliers
159164
.. autofunction:: src.QA.createDesignMatrix
160-
.. autofunction:: src.QA.mriqcQA
161165
.. autofunction:: src.QA.plotConfounds
162166
.. autofunction:: src.QA.plotEvents
163167
.. autofunction:: src.QA.plotRoiTimeCourse
@@ -204,6 +208,9 @@ bidspm
204208
data
205209
----
206210

211+
__pycache__
212+
-----------
213+
207214
cli
208215
===
209216
.. autofunction:: src.cli.baseInputParser

docs/source/QA.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@ Quality control
55

66
The illustrations in this section mix what the files created by each workflow
77
and the functions and are called by it.
8-
In this sense they are not pure DAGs (directed acyclic graphs) as the ``*.m`` files
9-
mentioned in them already exist.
8+
In this sense they are not pure DAGs (directed acyclic graphs)
9+
as the ``*.m`` files mentioned in them already exist.
1010

1111

12-
- :func:`anatQA`
13-
- :func:`bidsQApreproc`
12+
- :func:`bidsQA`
13+
- :func:`bidsQAbidspm`
1414

1515
.. _fig_spatialPrepro-reports:
1616
.. figure:: preprocessing/images/bidsSpatialPrepro/out_report.png
1717
:align: center
1818

1919
workflows for QA as part of the spatial preprocessing workflow
2020

21+
- :func:`anatQA`
2122
- :func:`computeDesignEfficiency`
2223
- :func:`plotEvents`

src/IO/getData.m

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@
88
%
99
% :param opt: Options chosen for the analysis.
1010
% See :func:`checkOptions`.
11-
%
1211
% :type opt: structure
1312
%
1413
% :param bidsDir: the directory where the data is ; defaults to
1514
% ``fullfile(opt.dataDir, '..', 'derivatives', 'bidspm')``
16-
%
1715
% :type bidsDir: char
1816
%
1917
% :param indexDependencies: Use ``'index_dependencies', true``
2018
% in bids.layout.
21-
%
2219
% :type indexDependencies: logical
2320
%
21+
% :param layoutFilter: filter to pass to bids.layout. default = struct()
22+
% :type layoutFilter: struct
23+
%
24+
%
2425
% :return: opt
2526
% :rtype: structure
2627
%
@@ -38,6 +39,7 @@
3839
addRequired(args, 'opt', @isstruct);
3940
addRequired(args, 'bidsDir', isFolder);
4041
addOptional(args, 'indexDependencies', true, @islogical);
42+
addOptional(args, 'layoutFilter', struct(), @isstruct);
4143

4244
try
4345
parse(args, varargin{:});
@@ -60,6 +62,8 @@
6062
indexDependencies = false;
6163
end
6264

65+
layoutFilter = args.Results.layoutFilter;
66+
6367
anatOnly = false;
6468
if isfield(opt, 'anatOnly')
6569
anatOnly = opt.anatOnly;
@@ -72,19 +76,18 @@
7276

7377
validationInputFile(bidsDir, 'dataset_description.json');
7478

75-
layout_filter = struct([]);
7679
if ~isempty(opt.subjects{1}) && ~ismember('', opt.subjects)
77-
layout_filter = struct('sub', {opt.subjects});
80+
layoutFilter.sub = opt.subjects;
7881
end
7982

8083
if anatOnly
81-
layout_filter(1).modality = {'anat'};
84+
layoutFilter(1).modality = {'anat'};
8285
end
8386

8487
BIDS = bids.layout(bidsDir, ...
8588
'use_schema', opt.useBidsSchema, ...
8689
'verbose', opt.verbosity > 1, ...
87-
'filter', layout_filter, ...
90+
'filter', layoutFilter, ...
8891
'index_dependencies', indexDependencies);
8992

9093
if strcmp(opt.pipeline.type, 'stats') && ~opt.pipeline.isBms
@@ -96,15 +99,15 @@
9699

97100
if isempty(fieldnames(tmp))
98101
BIDS.raw = bids.layout(opt.dir.raw, ...
99-
'filter', layout_filter, ...
102+
'filter', layoutFilter, ...
100103
'index_dependencies', indexDependencies);
101104
else
102105
BIDS.raw = tmp.BIDS;
103106
end
104107
else
105108
BIDS.raw = bids.layout(opt.dir.raw, ...
106109
'verbose', opt.verbosity > 1, ...
107-
'filter', layout_filter);
110+
'filter', layoutFilter);
108111
end
109112
end
110113

0 commit comments

Comments
 (0)