Coverage for nilearn/interfaces/fmriprep/tests/test_load_confounds_strategy.py: 0%
74 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-16 12:32 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-16 12:32 +0200
1import re
3import pandas as pd
4import pytest
6from nilearn.interfaces.fmriprep import load_confounds, load_confounds_strategy
7from nilearn.interfaces.fmriprep.load_confounds_strategy import (
8 preset_strategies,
9)
10from nilearn.interfaces.fmriprep.tests._testing import create_tmp_filepath
13@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
14@pytest.mark.parametrize(
15 "denoise_strategy,image_type",
16 [
17 ("simple", "regular"),
18 ("scrubbing", "regular"),
19 ("compcor", "regular"),
20 ("ica_aroma", "ica_aroma"),
21 ],
22)
23@pytest.mark.filterwarnings("ignore::RuntimeWarning")
24def test_load_confounds_strategy(
25 tmp_path, denoise_strategy, image_type, fmriprep_version
26):
27 """Smoke test with no extra inputs."""
28 file_nii, _ = create_tmp_filepath(
29 tmp_path,
30 image_type=image_type,
31 copy_confounds=True,
32 copy_json=True,
33 fmriprep_version=fmriprep_version,
34 )
35 confounds, _ = load_confounds_strategy(
36 file_nii, denoise_strategy=denoise_strategy
37 )
38 assert isinstance(confounds, pd.DataFrame)
41@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
42@pytest.mark.parametrize(
43 "denoise_strategy,image_type",
44 [
45 ("simple", "regular"),
46 ("scrubbing", "regular"),
47 ("compcor", "regular"),
48 ("ica_aroma", "ica_aroma"),
49 ],
50)
51@pytest.mark.filterwarnings("ignore::RuntimeWarning")
52def test_strategies(tmp_path, denoise_strategy, image_type, fmriprep_version):
53 """Check defaults setting of each preset strategy."""
54 file_nii, _ = create_tmp_filepath(
55 tmp_path,
56 image_type=image_type,
57 copy_confounds=True,
58 copy_json=True,
59 fmriprep_version=fmriprep_version,
60 )
61 confounds, _ = load_confounds_strategy(
62 file_nii, denoise_strategy=denoise_strategy
63 )
64 # Check that all fixed name model categories have been successfully loaded
65 list_check = _get_headers(denoise_strategy)
66 for col in confounds.columns:
67 # Check that all possible names exists
68 checker = [
69 re.match(keyword, col) is not None for keyword in list_check
70 ]
71 assert sum(checker) == 1
74patterns = {
75 "high_pass": ["cosine+"],
76 "motion": ["trans_[xyz]$", "rot_[xyz]$"],
77 "wm_csf": ["csf$", "white_matter$"],
78 "global": ["global_signal$"],
79 "global_signal": ["global_signal$"],
80 "compcor": ["[tawc]_comp_cor_+"],
81}
84def _get_headers(denoise_strategy):
85 """Retrieve the relevant header pattern for each strategy to check."""
86 default_params = preset_strategies[denoise_strategy].copy()
88 list_check = []
89 noise_components = default_params.pop("strategy")
90 for strategy in noise_components:
91 if strategy in patterns:
92 list_check += patterns[strategy]
94 for key, value in default_params.items():
95 if key in ["motion", "wm_csf", "global_signal"] and value != "basic":
96 for item in patterns[key]:
97 list_check.append(item.replace("$", "_+"))
98 return list_check
101@pytest.mark.parametrize(
102 "fmriprep_version, volumes_left, len_sample_mask",
103 [("1.4.x", 22, 29), ("21.x.x", 0, 5)],
104)
105@pytest.mark.filterwarnings("ignore::RuntimeWarning")
106def test_strategy_scrubbing(
107 tmp_path, fmriprep_version, volumes_left, len_sample_mask
108):
109 """Check user specified input for scrubbing strategy."""
110 file_nii, _ = create_tmp_filepath(
111 tmp_path,
112 image_type="regular",
113 copy_confounds=True,
114 copy_json=True,
115 fmriprep_version=fmriprep_version,
116 )
117 confounds, sample_mask = load_confounds_strategy(
118 file_nii, denoise_strategy="scrubbing", fd_threshold=0.15
119 )
120 # For "1.4.x"
121 # out of 30 vols, should have 6 motion outliers from scrubbing,
122 # and 2 vol removed by scrubbing strategy "full"
123 assert len(sample_mask) == volumes_left
125 # shape of confound regressors untouched
126 assert confounds.shape[0] == 30
128 # also load confounds with very liberal scrubbing thresholds
129 # this should not produce an error
130 confounds, sample_mask = load_confounds_strategy(
131 file_nii,
132 denoise_strategy="scrubbing",
133 fd_threshold=1,
134 std_dvars_threshold=5,
135 )
136 # only non-steady volumes removed
137 assert len(sample_mask) == len_sample_mask
139 # maker sure global signal works
140 confounds, sample_mask = load_confounds_strategy(
141 file_nii, denoise_strategy="scrubbing", global_signal="full"
142 )
143 for check in [
144 "global_signal",
145 "global_signal_derivative1",
146 "global_signal_power2",
147 "global_signal_derivative1_power2",
148 ]:
149 assert check in confounds.columns
152@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
153def test_strategy_compcor(tmp_path, fmriprep_version):
154 """Check user specified input for compcor strategy."""
155 file_nii, _ = create_tmp_filepath(
156 tmp_path,
157 image_type="regular",
158 copy_confounds=True,
159 copy_json=True,
160 fmriprep_version=fmriprep_version,
161 )
162 confounds, _ = load_confounds_strategy(
163 file_nii, denoise_strategy="compcor"
164 )
165 compcor_col_str_anat = "".join(confounds.columns)
166 assert "t_comp_cor_" not in compcor_col_str_anat
167 # this one comes from the white matter mask
168 expected = (
169 "a_comp_cor_57" if fmriprep_version == "1.4.x" else "w_comp_cor_00"
170 )
171 assert expected not in compcor_col_str_anat
172 assert "global_signal" not in compcor_col_str_anat
174 # test the global signal option
175 confounds, _ = load_confounds_strategy(
176 file_nii, denoise_strategy="compcor", global_signal="basic"
177 )
178 compcor_col_str_anat = "".join(confounds.columns)
179 assert "global_signal" in compcor_col_str_anat
182@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
183def test_irrelevant_input(tmp_path, fmriprep_version):
184 """Check invalid input raising correct warning or error message."""
185 file_nii, _ = create_tmp_filepath(
186 tmp_path,
187 image_type="regular",
188 copy_confounds=True,
189 copy_json=True,
190 fmriprep_version=fmriprep_version,
191 )
192 warning_message = (
193 r"parameters accepted: \['motion', 'wm_csf', "
194 "'global_signal', 'demean']"
195 )
196 with pytest.warns(UserWarning, match=warning_message):
197 load_confounds_strategy(
198 file_nii, denoise_strategy="simple", ica_aroma="full"
199 )
200 # invalid strategy
201 with pytest.raises(KeyError, match="blah"):
202 load_confounds_strategy(file_nii, denoise_strategy="blah")
205def test_empty_strategy(tmp_path):
206 """Ensure to return None for confounds and raise a warning
207 when strategy is empty.
208 """
209 file_nii, _ = create_tmp_filepath(
210 tmp_path,
211 image_type="regular",
212 copy_confounds=True,
213 copy_json=True,
214 )
216 warning_message = "strategy is empty, confounds will return None."
217 with pytest.warns(UserWarning, match=warning_message):
218 confounds, sample_mask = load_confounds(file_nii, strategy=[])
220 assert confounds is None