Skip to content

Commit e1b6819

Browse files
authored
Add orthogonalized motion components to confounds file (#56)
* Add orthogonalized components to confounds. * Replace unstable with main.
1 parent 8e0fc45 commit e1b6819

File tree

10 files changed

+41
-17
lines changed

10 files changed

+41
-17
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,8 @@ jobs:
274274
command: |
275275
if [[ -n "$DOCKER_PASS" ]]; then
276276
docker login -u $DOCKER_USER -p $DOCKER_PASS
277-
docker tag nipreps/fmripost_aroma nipreps/fmripost_aroma:unstable
278-
docker push nipreps/fmripost_aroma:unstable
277+
docker tag nipreps/fmripost_aroma nipreps/fmripost_aroma:main
278+
docker push nipreps/fmripost_aroma:main
279279
if [[ -n "$CIRCLE_TAG" ]]; then
280280
docker push nipreps/fmripost_aroma:latest
281281
docker tag nipreps/fmripost_aroma nipreps/fmripost_aroma:$CIRCLE_TAG

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ to MNI152NLin6Asym at 2 mm3 resolution.
2020
## Installation
2121

2222
```console
23-
docker pull nipreps/fmripost-aroma:unstable
23+
docker pull nipreps/fmripost-aroma:main
2424
```
2525

2626
## License

docs/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Installation
66
*fMRIPost-AROMA* should be installed using container technologies.
77

88
.. code-block:: bash
9-
docker pull nipreps/fmripost-aroma:unstable
9+
docker pull nipreps/fmripost-aroma:main
1010
1111
1212
Containerized execution (Docker and Singularity)

docs/outputs.rst

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ upcoming `BEP 011`_ and `BEP 012`_).
2424
Denoised fMRI data in the requested output spaces and resolutions.
2525

2626
4. **Confounds**:
27-
Time series of ICA components,
28-
as well as labels indicating if the components are accepted or rejected.
27+
Time series of ICA components classified as noise.
2928

3029

3130
Layout
@@ -107,19 +106,23 @@ Confounds_ are saved as a :abbr:`TSV (tab-separated value)` file::
107106
func/
108107
sub-<label>_[specifiers]_desc-aroma_metrics.tsv
109108
sub-<label>_[specifiers]_desc-aroma_metrics.json
110-
sub-<label>_[specifiers]_desc-melodic_timeseries.tsv
111-
sub-<label>_[specifiers]_desc-melodic_timeseries.json
109+
sub-<label>_[specifiers]_desc-aroma_timeseries.tsv
110+
sub-<label>_[specifiers]_desc-aroma_timeseries.json
112111

113112
Confounds
114113
---------
115114

116115
*fMRIPost-AROMA* outputs a set of confounds that can be used to denoise the data.
117-
These are stored in a TSV file (``desc-melodic_timeseries.tsv``) and a JSON file
118-
(``desc-melodic_timeseries.json``) that contains metadata about the confounds.
116+
These are stored in a TSV file (``desc-aroma_timeseries.tsv``) and a JSON file
117+
(``desc-aroma_timeseries.json``) that contains metadata about the confounds.
119118

120119
The confounds generated by *fMRIPost-AROMA* are ICA component time series
121120
classified as "rejected" by ICA-AROMA.
122121

122+
Columns starting with ``aroma_motion_`` are the raw noise ICA component time series.
123+
Columns starting with ``aroma_orth_motion_`` are the noise ICA component time series,
124+
after z-scoring and orthogonalization with respect to the signal ICA component time series.
125+
123126
Confounds and "carpet"-plot on the visual reports
124127
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125128

src/fmripost_aroma/interfaces/confounds.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def _get_ica_confounds(mixing, aroma_features, skip_vols, newpath=None):
6868
motion_ics = aroma_features_df.loc[
6969
aroma_features_df['classification'] == 'rejected'
7070
].index.values
71+
signal_ics = aroma_features_df.loc[
72+
aroma_features_df['classification'] != 'rejected'
73+
].index.values
7174
mixing_arr = np.loadtxt(mixing, ndmin=2)
7275

7376
# Prepare output paths
@@ -96,11 +99,29 @@ def _get_ica_confounds(mixing, aroma_features, skip_vols, newpath=None):
9699
# Select the mixing matrix rows corresponding to the motion ICs
97100
aggr_mixing_arr = mixing_arr[motion_ics, :].T
98101

102+
signal_mixing_arr = mixing_arr[signal_ics, :].T
103+
orthaggr_mixing_arr = aggr_mixing_arr.copy()
104+
orthaggr_mixing_arr = aggr_mixing_arr - np.dot(
105+
np.dot(np.linalg.pinv(good_ic_arr), good_ic_arr), aggr_mixing_arr
106+
)
107+
# Regress the good components out of the bad time series to get "pure evil" regressors
108+
aggr_mixing_arr_z = stats.zscore(aggr_mixing_arr, axis=0)
109+
signal_mixing_arr_z = stats.zscore(signal_mixing_arr, axis=0)
110+
betas = np.linalg.lstsq(signal_mixing_arr_z, aggr_mixing_arr_z, rcond=None)[0]
111+
pred_bad_timeseries = np.dot(signal_mixing_arr_z, betas)
112+
orthaggr_mixing_arr = aggr_mixing_arr_z - pred_bad_timeseries
113+
99114
# add one to motion_ic_indices to match melodic report.
100-
pd.DataFrame(
115+
aggr_confounds_df = pd.DataFrame(
101116
aggr_mixing_arr,
102117
columns=[f'aroma_motion_{x + 1:02d}' for x in motion_ics],
103-
).to_csv(aroma_confounds, sep='\t', index=None)
118+
)
119+
orthaggr_confounds_df = pd.DataFrame(
120+
orthaggr_mixing_arr,
121+
columns=[f'aroma_orth_motion_{x + 1:02d}' for x in motion_ics],
122+
)
123+
confounds_df = pd.concat([aggr_confounds_df, orthaggr_confounds_df], axis=1)
124+
confounds_df.to_csv(aroma_confounds, sep='\t', index=None)
104125

105126
return aroma_confounds, mixing_out
106127

src/fmripost_aroma/tests/data/test_ds005115_deriv_and_raw_outputs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ sub-01/ses-01
44
sub-01/ses-01/func
55
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_metrics.json
66
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_metrics.tsv
7-
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-melodic_timeseries.tsv
7+
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_timeseries.tsv
88
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-aggrDenoised_bold.nii.gz
99
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-melodic_components.nii.gz
1010
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-melodic_mixing.tsv

src/fmripost_aroma/tests/data/test_ds005115_deriv_only_outputs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ sub-01/ses-01
44
sub-01/ses-01/func
55
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_metrics.json
66
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_metrics.tsv
7-
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-melodic_timeseries.tsv
7+
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_timeseries.tsv
88
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-aggrDenoised_bold.nii.gz
99
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-melodic_components.nii.gz
1010
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-melodic_mixing.tsv

src/fmripost_aroma/tests/data/test_ds005115_resampling_and_raw_outputs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ sub-01/ses-01
44
sub-01/ses-01/func
55
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_metrics.json
66
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_metrics.tsv
7-
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-melodic_timeseries.tsv
7+
sub-01/ses-01/func/sub-01_ses-01_task-rest_desc-aroma_timeseries.tsv
88
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-aggrDenoised_bold.nii.gz
99
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-melodic_components.nii.gz
1010
sub-01/ses-01/func/sub-01_ses-01_task-rest_space-MNI152NLin6Asym_res-2_desc-melodic_mixing.tsv

src/fmripost_aroma/tests/run_local_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def run_tests(test_regex, test_mark):
7373
run_str = 'docker run --rm -ti '
7474
run_str += f'-v {local_patch}:{mounted_code} '
7575
run_str += '--entrypoint pytest '
76-
run_str += 'nipreps/fmripost_aroma:unstable '
76+
run_str += 'nipreps/fmripost_aroma:main '
7777
run_str += (
7878
f'{mounted_code}/fmripost_aroma '
7979
f'--data_dir={mounted_code}/fmripost_aroma/tests/test_data '

src/fmripost_aroma/workflows/aroma.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ def init_ica_aroma_wf(
352352
base_directory=config.execution.output_dir,
353353
source_file=bold_file,
354354
datatype='func',
355-
desc='melodic',
355+
desc='aroma',
356356
suffix='timeseries',
357357
extension='tsv',
358358
dismiss_entities=('echo', 'den', 'res'),

0 commit comments

Comments
 (0)