bidsaid 0.20.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bidsaid-0.20.0/LICENSE +21 -0
- bidsaid-0.20.0/PKG-INFO +229 -0
- bidsaid-0.20.0/README.md +186 -0
- bidsaid-0.20.0/bidsaid/__init__.py +27 -0
- bidsaid-0.20.0/bidsaid/_constants.py +24 -0
- bidsaid-0.20.0/bidsaid/_decorators.py +97 -0
- bidsaid-0.20.0/bidsaid/_exceptions.py +60 -0
- bidsaid-0.20.0/bidsaid/_helpers.py +14 -0
- bidsaid-0.20.0/bidsaid/audit.py +583 -0
- bidsaid-0.20.0/bidsaid/bids.py +3152 -0
- bidsaid-0.20.0/bidsaid/io.py +219 -0
- bidsaid-0.20.0/bidsaid/logging.py +90 -0
- bidsaid-0.20.0/bidsaid/metadata.py +1188 -0
- bidsaid-0.20.0/bidsaid/parsers.py +355 -0
- bidsaid-0.20.0/bidsaid/qc/__init__.py +12 -0
- bidsaid-0.20.0/bidsaid/qc/censoring.py +403 -0
- bidsaid-0.20.0/bidsaid/qc/nuisance.py +50 -0
- bidsaid-0.20.0/bidsaid/simulate.py +220 -0
- bidsaid-0.20.0/bidsaid.egg-info/PKG-INFO +229 -0
- bidsaid-0.20.0/bidsaid.egg-info/SOURCES.txt +32 -0
- bidsaid-0.20.0/bidsaid.egg-info/dependency_links.txt +1 -0
- bidsaid-0.20.0/bidsaid.egg-info/requires.txt +20 -0
- bidsaid-0.20.0/bidsaid.egg-info/top_level.txt +1 -0
- bidsaid-0.20.0/pyproject.toml +82 -0
- bidsaid-0.20.0/setup.cfg +4 -0
- bidsaid-0.20.0/tests/test_audit.py +113 -0
- bidsaid-0.20.0/tests/test_bids.py +596 -0
- bidsaid-0.20.0/tests/test_decorators.py +45 -0
- bidsaid-0.20.0/tests/test_io.py +54 -0
- bidsaid-0.20.0/tests/test_logging.py +30 -0
- bidsaid-0.20.0/tests/test_metadata.py +469 -0
- bidsaid-0.20.0/tests/test_parsers.py +87 -0
- bidsaid-0.20.0/tests/test_qc.py +167 -0
- bidsaid-0.20.0/tests/test_simulate.py +36 -0
bidsaid-0.20.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Donisha Smith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
bidsaid-0.20.0/PKG-INFO
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bidsaid
|
|
3
|
+
Version: 0.20.0
|
|
4
|
+
Summary: Post-hoc BIDS conversion for NIfTI datasets and neurobehavioral logs.
|
|
5
|
+
Author-email: Donisha Smith <dsmit420@jhu.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://bidsaid.readthedocs.io
|
|
8
|
+
Project-URL: Github, https://github.com/donishadsmith/bidsaid
|
|
9
|
+
Project-URL: Issues, https://github.com/donishadsmith/bidsaid/issues
|
|
10
|
+
Project-URL: Changelog, https://bidsaid.readthedocs.io/en/stable/changelog.html
|
|
11
|
+
Keywords: python,neuroimaging,fMRI,MRI,BIDS,NIfTI
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
21
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
22
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
|
23
|
+
Classifier: Development Status :: 3 - Alpha
|
|
24
|
+
Requires-Python: >=3.10.0
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: numpy>=1.26.3
|
|
28
|
+
Requires-Dist: nibabel>=5.0.0
|
|
29
|
+
Requires-Dist: rich>=14.2.0
|
|
30
|
+
Requires-Dist: nilearn>=0.10.4
|
|
31
|
+
Requires-Dist: pandas>=2.1.0
|
|
32
|
+
Requires-Dist: pybids>=0.16.5; platform_system != "Windows"
|
|
33
|
+
Requires-Dist: tqdm>=4.65.0
|
|
34
|
+
Requires-Dist: joblib>=1.3.0
|
|
35
|
+
Provides-Extra: all
|
|
36
|
+
Requires-Dist: bidsaid[test,windows]; extra == "all"
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: pytest; extra == "test"
|
|
39
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
40
|
+
Provides-Extra: windows
|
|
41
|
+
Requires-Dist: pybids>=0.16.5; extra == "windows"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# BIDSAID
|
|
45
|
+
|
|
46
|
+
[](https://pypi.python.org/pypi/bidsaid/)
|
|
47
|
+
[](https://pypi.python.org/pypi/bidsaid/)
|
|
48
|
+
[](https://github.com/donishadsmith/bidsaid)
|
|
49
|
+
[](https://opensource.org/licenses/MIT)
|
|
50
|
+
[](https://github.com/donishadsmith/bidsaid/actions/workflows/testing.yaml)
|
|
51
|
+
[](https://codecov.io/gh/donishadsmith/bidsaid)
|
|
52
|
+
[](https://github.com/psf/black)
|
|
53
|
+
[](http://bidsaid.readthedocs.io/en/stable/?badge=stable)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
A toolkit for post-hoc BIDS conversion of legacy or unstructured NIfTI datasets. Intended for cases that require custom code and flexibility, such as when NIfTI source files lack consistent naming conventions, organized folder hierarchies, or sidecar metadata. Includes utilities for metadata reconstruction from NIfTI headers, file renaming, neurobehavioral log parsing (for E-Prime and Presentation), and JSON sidecar generation.
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
### Standard Installation
|
|
61
|
+
```bash
|
|
62
|
+
pip install bidsaid[all]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Development Version
|
|
66
|
+
```bash
|
|
67
|
+
git clone --depth 1 https://github.com/donishadsmith/bidsaid/
|
|
68
|
+
cd bidsaid
|
|
69
|
+
pip install -e .[all]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
- **File renaming**: Convert arbitrary filenames to BIDS-compliant naming
|
|
75
|
+
- **File creation**: Generate `dataset_description.json` and `participants.tsv`
|
|
76
|
+
- **Metadata utilities**: Extract header metadata (e.g., TR, orientation, scanner info) and generate slice timing for singleband and multiband acquisitions
|
|
77
|
+
- **Log parsing**: Load Presentation (e.g., `.log`) and E-Prime 3 (e.g, `.edat3`, `.txt`) files as DataFrames, or use extractor classes to generate BIDS events for block and event designs:
|
|
78
|
+
|
|
79
|
+
| Class | Software | Design | Description |
|
|
80
|
+
|-------|----------|--------|-------------|
|
|
81
|
+
| `PresentationBlockExtractor` | Presentation | Block | Extracts block-level timing with mean RT and accuracy |
|
|
82
|
+
| `PresentationEventExtractor` | Presentation | Event | Extracts trial-level timing with individual responses |
|
|
83
|
+
| `EPrimeBlockExtractor` | E-Prime 3 | Block | Extracts block-level timing with mean RT and accuracy |
|
|
84
|
+
| `EPrimeEventExtractor` | E-Prime 3 | Event | Extracts trial-level timing with individual responses |
|
|
85
|
+
|
|
86
|
+
- **Auditing**: Generate a table of showing the presence or abscence of certain files for each subject and session
|
|
87
|
+
- **QC**: Creation and computation of certain quality control metrics (e.g., framewise displacement)
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
### Creating BIDS-Compliant Filenames
|
|
92
|
+
```python
|
|
93
|
+
from bidsaid.bids import create_bids_file
|
|
94
|
+
|
|
95
|
+
create_bids_file(
|
|
96
|
+
src_file="101_mprage.nii.gz",
|
|
97
|
+
subj_id="101",
|
|
98
|
+
ses_id="01",
|
|
99
|
+
desc="T1w",
|
|
100
|
+
dst_dir="/data/bids/sub-101/ses-01/anat",
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Extracting Metadata from NIfTI Headers
|
|
105
|
+
```python
|
|
106
|
+
from bidsaid.metadata import get_tr, create_slice_timing, get_image_orientation
|
|
107
|
+
|
|
108
|
+
tr = get_tr("sub-01_bold.nii.gz")
|
|
109
|
+
slice_timing = create_slice_timing(
|
|
110
|
+
"sub-01_bold.nii.gz",
|
|
111
|
+
slice_acquisition_method="interleaved",
|
|
112
|
+
multiband_factor=4,
|
|
113
|
+
)
|
|
114
|
+
orientation_map, orientation = get_image_orientation("sub-01_bold.nii.gz")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Loading Raw Log Files
|
|
118
|
+
```python
|
|
119
|
+
from bidsaid.parsers import (
|
|
120
|
+
load_presentation_log,
|
|
121
|
+
load_eprime_log,
|
|
122
|
+
convert_edat3_to_txt,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
presentation_df = load_presentation_log("sub-01_task.log", convert_to_seconds=["Time"])
|
|
126
|
+
|
|
127
|
+
# E-Prime 3: convert .edat3 to text first, or load .txt directly
|
|
128
|
+
eprime_txt_path = convert_edat3_to_txt("sub-01_task.edat3")
|
|
129
|
+
eprime_df = load_eprime_log(eprime_txt_path, convert_to_seconds=["Stimulus.OnsetTime"])
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Creating BIDS Events from Presentation Logs
|
|
133
|
+
```python
|
|
134
|
+
from bidsaid.bids import PresentationBlockExtractor
|
|
135
|
+
import pandas as pd
|
|
136
|
+
|
|
137
|
+
extractor = PresentationBlockExtractor(
|
|
138
|
+
"sub-01_task-faces.log",
|
|
139
|
+
block_cue_names=("Face", "Place"), # Can use regex ("Fa.*", "Pla.*")
|
|
140
|
+
scanner_event_type="Pulse",
|
|
141
|
+
scanner_trigger_code="99",
|
|
142
|
+
convert_to_seconds=["Time"],
|
|
143
|
+
rest_block_codes="crosshair",
|
|
144
|
+
rest_code_frequency="fixed",
|
|
145
|
+
split_cue_as_instruction=True,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
events_df = pd.DataFrame(
|
|
149
|
+
{
|
|
150
|
+
"onset": extractor.extract_onsets(),
|
|
151
|
+
"duration": extractor.extract_durations(),
|
|
152
|
+
"trial_type": extractor.extract_trial_types(),
|
|
153
|
+
"mean_rt": extractor.extract_mean_reaction_times(),
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Creating BIDS Events from E-Prime Logs
|
|
159
|
+
```python
|
|
160
|
+
from bidsaid.bids import EPrimeEventExtractor
|
|
161
|
+
import pandas as pd
|
|
162
|
+
|
|
163
|
+
extractor = EPrimeEventExtractor(
|
|
164
|
+
"sub-01_task-gonogo.txt",
|
|
165
|
+
trial_types="Go|NoGo", # Can also use ("Go", "NoGo")
|
|
166
|
+
onset_column_name="Stimulus.OnsetTime",
|
|
167
|
+
procedure_column_name="Procedure",
|
|
168
|
+
trigger_column_name="ScannerTrigger.RTTime",
|
|
169
|
+
convert_to_seconds=[
|
|
170
|
+
"Stimulus.OnsetTime",
|
|
171
|
+
"Stimulus.OffsetTime",
|
|
172
|
+
"ScannerTrigger.RTTime",
|
|
173
|
+
],
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
events_df = pd.DataFrame(
|
|
177
|
+
{
|
|
178
|
+
"onset": extractor.extract_onsets(),
|
|
179
|
+
"duration": extractor.extract_durations(
|
|
180
|
+
offset_column_name="Stimulus.OffsetTime"
|
|
181
|
+
),
|
|
182
|
+
"trial_type": extractor.extract_trial_types(),
|
|
183
|
+
"reaction_time": extractor.extract_reaction_times(
|
|
184
|
+
reaction_time_column_name="Stimulus.RT"
|
|
185
|
+
),
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Audit BIDS Dataset
|
|
191
|
+
```python
|
|
192
|
+
from bidsaid.audit import BIDSAuditor
|
|
193
|
+
from bidsaid.simulate import simulate_bids_dataset
|
|
194
|
+
|
|
195
|
+
bids_root = simulate_bids_dataset()
|
|
196
|
+
|
|
197
|
+
auditor = BIDSAuditor(bids_root)
|
|
198
|
+
auditor.check_raw_nifti_availability()
|
|
199
|
+
auditor.check_raw_sidecar_availability()
|
|
200
|
+
auditor.check_events_availability()
|
|
201
|
+
auditor.check_preprocessed_nifti_availability()
|
|
202
|
+
|
|
203
|
+
analysis_dir = bids_root / "first_level"
|
|
204
|
+
analysis_sub_dir = analysis_dir / "sub-1" / "ses-1"
|
|
205
|
+
analysis_sub_dir.mkdir(parents=True, exist_ok=True)
|
|
206
|
+
|
|
207
|
+
with open(analysis_sub_dir / "sub-1_task-rest_desc-betas.nii.gz", "w") as f:
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
auditor.check_first_level_availability(analysis_dir=analysis_dir, desc="betas")
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Compute QC
|
|
214
|
+
```python
|
|
215
|
+
from bidsaid.qc import create_censor_mask, compute_consecutive_censor_stats
|
|
216
|
+
|
|
217
|
+
censor_mask = create_censor_mask(
|
|
218
|
+
"confounds.tsv",
|
|
219
|
+
column_name="framewise_displacement",
|
|
220
|
+
threshold=0.5,
|
|
221
|
+
n_dummy_scans=4,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
consecutive_censor_mean, consecutive_censor_std = compute_consecutive_censor_stats(
|
|
225
|
+
censor_mask, n_dummy_scans=4
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
See the [API documentation](https://bidsaid.readthedocs.io/en/latest/api.html) for full parameter details and additional utilities.
|
bidsaid-0.20.0/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# BIDSAID
|
|
2
|
+
|
|
3
|
+
[](https://pypi.python.org/pypi/bidsaid/)
|
|
4
|
+
[](https://pypi.python.org/pypi/bidsaid/)
|
|
5
|
+
[](https://github.com/donishadsmith/bidsaid)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://github.com/donishadsmith/bidsaid/actions/workflows/testing.yaml)
|
|
8
|
+
[](https://codecov.io/gh/donishadsmith/bidsaid)
|
|
9
|
+
[](https://github.com/psf/black)
|
|
10
|
+
[](http://bidsaid.readthedocs.io/en/stable/?badge=stable)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
A toolkit for post-hoc BIDS conversion of legacy or unstructured NIfTI datasets. Intended for cases that require custom code and flexibility, such as when NIfTI source files lack consistent naming conventions, organized folder hierarchies, or sidecar metadata. Includes utilities for metadata reconstruction from NIfTI headers, file renaming, neurobehavioral log parsing (for E-Prime and Presentation), and JSON sidecar generation.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### Standard Installation
|
|
18
|
+
```bash
|
|
19
|
+
pip install bidsaid[all]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Development Version
|
|
23
|
+
```bash
|
|
24
|
+
git clone --depth 1 https://github.com/donishadsmith/bidsaid/
|
|
25
|
+
cd bidsaid
|
|
26
|
+
pip install -e .[all]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- **File renaming**: Convert arbitrary filenames to BIDS-compliant naming
|
|
32
|
+
- **File creation**: Generate `dataset_description.json` and `participants.tsv`
|
|
33
|
+
- **Metadata utilities**: Extract header metadata (e.g., TR, orientation, scanner info) and generate slice timing for singleband and multiband acquisitions
|
|
34
|
+
- **Log parsing**: Load Presentation (e.g., `.log`) and E-Prime 3 (e.g, `.edat3`, `.txt`) files as DataFrames, or use extractor classes to generate BIDS events for block and event designs:
|
|
35
|
+
|
|
36
|
+
| Class | Software | Design | Description |
|
|
37
|
+
|-------|----------|--------|-------------|
|
|
38
|
+
| `PresentationBlockExtractor` | Presentation | Block | Extracts block-level timing with mean RT and accuracy |
|
|
39
|
+
| `PresentationEventExtractor` | Presentation | Event | Extracts trial-level timing with individual responses |
|
|
40
|
+
| `EPrimeBlockExtractor` | E-Prime 3 | Block | Extracts block-level timing with mean RT and accuracy |
|
|
41
|
+
| `EPrimeEventExtractor` | E-Prime 3 | Event | Extracts trial-level timing with individual responses |
|
|
42
|
+
|
|
43
|
+
- **Auditing**: Generate a table of showing the presence or abscence of certain files for each subject and session
|
|
44
|
+
- **QC**: Creation and computation of certain quality control metrics (e.g., framewise displacement)
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
### Creating BIDS-Compliant Filenames
|
|
49
|
+
```python
|
|
50
|
+
from bidsaid.bids import create_bids_file
|
|
51
|
+
|
|
52
|
+
create_bids_file(
|
|
53
|
+
src_file="101_mprage.nii.gz",
|
|
54
|
+
subj_id="101",
|
|
55
|
+
ses_id="01",
|
|
56
|
+
desc="T1w",
|
|
57
|
+
dst_dir="/data/bids/sub-101/ses-01/anat",
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Extracting Metadata from NIfTI Headers
|
|
62
|
+
```python
|
|
63
|
+
from bidsaid.metadata import get_tr, create_slice_timing, get_image_orientation
|
|
64
|
+
|
|
65
|
+
tr = get_tr("sub-01_bold.nii.gz")
|
|
66
|
+
slice_timing = create_slice_timing(
|
|
67
|
+
"sub-01_bold.nii.gz",
|
|
68
|
+
slice_acquisition_method="interleaved",
|
|
69
|
+
multiband_factor=4,
|
|
70
|
+
)
|
|
71
|
+
orientation_map, orientation = get_image_orientation("sub-01_bold.nii.gz")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Loading Raw Log Files
|
|
75
|
+
```python
|
|
76
|
+
from bidsaid.parsers import (
|
|
77
|
+
load_presentation_log,
|
|
78
|
+
load_eprime_log,
|
|
79
|
+
convert_edat3_to_txt,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
presentation_df = load_presentation_log("sub-01_task.log", convert_to_seconds=["Time"])
|
|
83
|
+
|
|
84
|
+
# E-Prime 3: convert .edat3 to text first, or load .txt directly
|
|
85
|
+
eprime_txt_path = convert_edat3_to_txt("sub-01_task.edat3")
|
|
86
|
+
eprime_df = load_eprime_log(eprime_txt_path, convert_to_seconds=["Stimulus.OnsetTime"])
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Creating BIDS Events from Presentation Logs
|
|
90
|
+
```python
|
|
91
|
+
from bidsaid.bids import PresentationBlockExtractor
|
|
92
|
+
import pandas as pd
|
|
93
|
+
|
|
94
|
+
extractor = PresentationBlockExtractor(
|
|
95
|
+
"sub-01_task-faces.log",
|
|
96
|
+
block_cue_names=("Face", "Place"), # Can use regex ("Fa.*", "Pla.*")
|
|
97
|
+
scanner_event_type="Pulse",
|
|
98
|
+
scanner_trigger_code="99",
|
|
99
|
+
convert_to_seconds=["Time"],
|
|
100
|
+
rest_block_codes="crosshair",
|
|
101
|
+
rest_code_frequency="fixed",
|
|
102
|
+
split_cue_as_instruction=True,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
events_df = pd.DataFrame(
|
|
106
|
+
{
|
|
107
|
+
"onset": extractor.extract_onsets(),
|
|
108
|
+
"duration": extractor.extract_durations(),
|
|
109
|
+
"trial_type": extractor.extract_trial_types(),
|
|
110
|
+
"mean_rt": extractor.extract_mean_reaction_times(),
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Creating BIDS Events from E-Prime Logs
|
|
116
|
+
```python
|
|
117
|
+
from bidsaid.bids import EPrimeEventExtractor
|
|
118
|
+
import pandas as pd
|
|
119
|
+
|
|
120
|
+
extractor = EPrimeEventExtractor(
|
|
121
|
+
"sub-01_task-gonogo.txt",
|
|
122
|
+
trial_types="Go|NoGo", # Can also use ("Go", "NoGo")
|
|
123
|
+
onset_column_name="Stimulus.OnsetTime",
|
|
124
|
+
procedure_column_name="Procedure",
|
|
125
|
+
trigger_column_name="ScannerTrigger.RTTime",
|
|
126
|
+
convert_to_seconds=[
|
|
127
|
+
"Stimulus.OnsetTime",
|
|
128
|
+
"Stimulus.OffsetTime",
|
|
129
|
+
"ScannerTrigger.RTTime",
|
|
130
|
+
],
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
events_df = pd.DataFrame(
|
|
134
|
+
{
|
|
135
|
+
"onset": extractor.extract_onsets(),
|
|
136
|
+
"duration": extractor.extract_durations(
|
|
137
|
+
offset_column_name="Stimulus.OffsetTime"
|
|
138
|
+
),
|
|
139
|
+
"trial_type": extractor.extract_trial_types(),
|
|
140
|
+
"reaction_time": extractor.extract_reaction_times(
|
|
141
|
+
reaction_time_column_name="Stimulus.RT"
|
|
142
|
+
),
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Audit BIDS Dataset
|
|
148
|
+
```python
|
|
149
|
+
from bidsaid.audit import BIDSAuditor
|
|
150
|
+
from bidsaid.simulate import simulate_bids_dataset
|
|
151
|
+
|
|
152
|
+
bids_root = simulate_bids_dataset()
|
|
153
|
+
|
|
154
|
+
auditor = BIDSAuditor(bids_root)
|
|
155
|
+
auditor.check_raw_nifti_availability()
|
|
156
|
+
auditor.check_raw_sidecar_availability()
|
|
157
|
+
auditor.check_events_availability()
|
|
158
|
+
auditor.check_preprocessed_nifti_availability()
|
|
159
|
+
|
|
160
|
+
analysis_dir = bids_root / "first_level"
|
|
161
|
+
analysis_sub_dir = analysis_dir / "sub-1" / "ses-1"
|
|
162
|
+
analysis_sub_dir.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
|
|
164
|
+
with open(analysis_sub_dir / "sub-1_task-rest_desc-betas.nii.gz", "w") as f:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
auditor.check_first_level_availability(analysis_dir=analysis_dir, desc="betas")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Compute QC
|
|
171
|
+
```python
|
|
172
|
+
from bidsaid.qc import create_censor_mask, compute_consecutive_censor_stats
|
|
173
|
+
|
|
174
|
+
censor_mask = create_censor_mask(
|
|
175
|
+
"confounds.tsv",
|
|
176
|
+
column_name="framewise_displacement",
|
|
177
|
+
threshold=0.5,
|
|
178
|
+
n_dummy_scans=4,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
consecutive_censor_mean, consecutive_censor_std = compute_consecutive_censor_stats(
|
|
182
|
+
censor_mask, n_dummy_scans=4
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
See the [API documentation](https://bidsaid.readthedocs.io/en/latest/api.html) for full parameter details and additional utilities.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Post-hoc BIDS toolkit for NIfTI datasets without original DICOMs.
|
|
3
|
+
-----------------------------------------------------------------
|
|
4
|
+
Documentation can be found at https://bidsaid.readthedocs.io.
|
|
5
|
+
|
|
6
|
+
Submodules
|
|
7
|
+
----------
|
|
8
|
+
audit -- Contains the ``BIDSAuditor`` class to check for certain file availability
|
|
9
|
+
|
|
10
|
+
bids -- Operations related to initializing and creating BIDS compliant files
|
|
11
|
+
|
|
12
|
+
io -- Generic operations related to loading NIfTI data
|
|
13
|
+
|
|
14
|
+
logging -- Set up a logger using ``RichHandler`` as the default handler if a root or
|
|
15
|
+
module specific handler is not available
|
|
16
|
+
|
|
17
|
+
metadata -- Operations related to extracting or creating metadata information from NIfTI images
|
|
18
|
+
|
|
19
|
+
parsers -- Operations related to standardizing and parsing information logs created by stimulus
|
|
20
|
+
neurobehavioral software such as Presentation and EPrime
|
|
21
|
+
|
|
22
|
+
qc -- Quality control utilities for fMRI data (motion censoring, framewise displacement, etc.)
|
|
23
|
+
|
|
24
|
+
simulate -- Simulate a basic NIfTI image or BIDS dataset for testing purposes
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__version__ = "0.20.0"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for constants.
|
|
3
|
+
|
|
4
|
+
Note: The CLI command for converting edat3 files to text files can be found at the
|
|
5
|
+
following link: https://support.pstnet.com/hc/en-us/articles/360020316014-DATA-Using-E-DataAid-exe-with-Command-Line-Interpreters-25323
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
EDATAAID_PATH = r"C:\Program Files (x86)\PST\E-Prime 3.0\Program\E-DataAid.exe"
|
|
9
|
+
|
|
10
|
+
EDATAAID_CONTROL_FILE = """Inheritance=NULL
|
|
11
|
+
InFile={edat_path}
|
|
12
|
+
OutFile={dst_path}
|
|
13
|
+
ColFlags=0
|
|
14
|
+
ColNames=1
|
|
15
|
+
Comments=0
|
|
16
|
+
BegCommentLine=0
|
|
17
|
+
EndCommentLine=0
|
|
18
|
+
DataSeparator={delimiter}
|
|
19
|
+
VarSeparator={delimiter}
|
|
20
|
+
BegDataLine=0
|
|
21
|
+
EndDataLine=0
|
|
22
|
+
MissingData=nan
|
|
23
|
+
Unicode=0
|
|
24
|
+
"""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Decorator functions."""
|
|
2
|
+
|
|
3
|
+
import functools, inspect
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
from ._helpers import iterable_to_str
|
|
8
|
+
from .io import get_nifti_header
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def check_all_none(parameter_names: list[str]) -> Callable:
|
|
12
|
+
"""
|
|
13
|
+
Checks if specific parameters are assigned ``None``.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
parameter_names : :obj:`list[str]`
|
|
18
|
+
List of parameter names to check.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
Callable
|
|
23
|
+
Decorator function wrapping target function.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def decorator(func: Callable) -> Callable:
|
|
27
|
+
signature = inspect.signature(func)
|
|
28
|
+
if invalid_params := [
|
|
29
|
+
param
|
|
30
|
+
for param in parameter_names
|
|
31
|
+
if param not in signature.parameters.keys()
|
|
32
|
+
]:
|
|
33
|
+
raise NameError(
|
|
34
|
+
"Error in ``parameter_names`` of decorator. The following "
|
|
35
|
+
f"parameters are not in the signature of '{func.__name__}': "
|
|
36
|
+
f"{iterable_to_str(invalid_params)}."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@functools.wraps(func)
|
|
40
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
41
|
+
bound_args = signature.bind(*args, **kwargs)
|
|
42
|
+
bound_args.apply_defaults()
|
|
43
|
+
all_param_values = [bound_args.arguments[name] for name in parameter_names]
|
|
44
|
+
if all(value is None for value in all_param_values):
|
|
45
|
+
raise ValueError(
|
|
46
|
+
"All of the following arguments cannot be None, "
|
|
47
|
+
f"one must be specified: {iterable_to_str(parameter_names)}."
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return func(*args, **kwargs)
|
|
51
|
+
|
|
52
|
+
return wrapper
|
|
53
|
+
|
|
54
|
+
return decorator
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def check_nifti(nifti_param_name: str | None = None) -> Callable:
|
|
58
|
+
"""
|
|
59
|
+
Checks if input NIfTI has qform or sform codes set to scanner.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
nifti_param_name : :obj:`list[str]`
|
|
64
|
+
Name of the NIfTI parameter. If None, assumes the
|
|
65
|
+
NIfTI parameter is "nifti_file_or_img".
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
Callable
|
|
70
|
+
Decorator function wrapping target function.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def decorator(func: Callable) -> Callable:
|
|
74
|
+
@functools.wraps(func)
|
|
75
|
+
def wrapper(*args, **kwargs) -> Any:
|
|
76
|
+
bound_args = inspect.signature(func).bind(*args, **kwargs)
|
|
77
|
+
bound_args.apply_defaults()
|
|
78
|
+
|
|
79
|
+
param_name = (
|
|
80
|
+
"nifti_file_or_img" if not nifti_param_name else nifti_param_name
|
|
81
|
+
)
|
|
82
|
+
img_val = bound_args.arguments.get(param_name)
|
|
83
|
+
if img_val:
|
|
84
|
+
hdr = get_nifti_header(img_val)
|
|
85
|
+
|
|
86
|
+
# sform takes precedence over qform
|
|
87
|
+
code = "sform_code" if hdr["sform_code"] else "qform_code"
|
|
88
|
+
if hdr[code] != 1:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"The {code} is not set to 'scanner' and is not a raw NIfTI image."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return func(*args, **kwargs)
|
|
94
|
+
|
|
95
|
+
return wrapper
|
|
96
|
+
|
|
97
|
+
return decorator
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Custom exceptions."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SliceAxisError(Exception):
|
|
7
|
+
"""
|
|
8
|
+
Incorrect slice axis.
|
|
9
|
+
|
|
10
|
+
Raised when the number of slices does not match "slice_end" plus one.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
slice_axis : :obj:`Literal["x", "y", "z"]`
|
|
15
|
+
The specified slice dimension.
|
|
16
|
+
|
|
17
|
+
n_slices : :obj:`int`
|
|
18
|
+
The number of slices from the specified ``slice_axis``.
|
|
19
|
+
|
|
20
|
+
slice_end : :obj:`int`
|
|
21
|
+
The number of slices specified by "slice_end" in the NIfTI header.
|
|
22
|
+
|
|
23
|
+
message : :obj:`str` or :obj:`None`:
|
|
24
|
+
The error message. If None, a default error message is used.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
slice_axis: Literal["x", "y", "z"],
|
|
30
|
+
n_slices: int,
|
|
31
|
+
slice_end: int,
|
|
32
|
+
message: str | None = None,
|
|
33
|
+
):
|
|
34
|
+
if not message:
|
|
35
|
+
self.message = (
|
|
36
|
+
"Incorrect slice axis. Number of slices for "
|
|
37
|
+
f"{slice_axis} dimension is {n_slices} but "
|
|
38
|
+
f"'slice_end' in NIfTI header is {slice_end}."
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
self.message = message
|
|
42
|
+
|
|
43
|
+
super().__init__(self.message)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class DataDimensionError(Exception):
|
|
47
|
+
"""
|
|
48
|
+
Incorrect data dimensionality.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PathDoesNotExist(Exception):
|
|
55
|
+
"""Exception when path does not exist."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, path):
|
|
58
|
+
self.message = f"The following path does not exist: {path}"
|
|
59
|
+
|
|
60
|
+
super().__init__(self.message)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Helper functions."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def iterable_to_str(str_list: list[str]) -> None:
|
|
8
|
+
"""Converts an iterable containing strings to strings."""
|
|
9
|
+
return ", ".join(["'{a}'".format(a=x) for x in str_list])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_path(object: Any) -> bool:
|
|
13
|
+
"Determine if input is a Path or string."
|
|
14
|
+
return isinstance(object, (str, Path))
|