eegprep 0.0.2__tar.gz → 0.1.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.
- eegprep-0.1.0/LICENSE +25 -0
- eegprep-0.1.0/PKG-INFO +204 -0
- eegprep-0.1.0/README.md +141 -0
- eegprep-0.1.0/pyproject.toml +70 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/ICL_feature_extractor.py +8 -6
- eegprep-0.1.0/src/eegprep/__init__.py +44 -0
- eegprep-0.1.0/src/eegprep/bids_list_eeg_files.py +108 -0
- eegprep-0.1.0/src/eegprep/bids_preproc.py +1093 -0
- eegprep-0.1.0/src/eegprep/clean_artifacts.py +310 -0
- eegprep-0.1.0/src/eegprep/clean_asr.py +190 -0
- eegprep-0.1.0/src/eegprep/clean_channels.py +179 -0
- eegprep-0.1.0/src/eegprep/clean_channels_nolocs.py +150 -0
- eegprep-0.1.0/src/eegprep/clean_drifts.py +52 -0
- eegprep-0.1.0/src/eegprep/clean_flatlines.py +73 -0
- eegprep-0.1.0/src/eegprep/clean_windows.py +233 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_autocorr.py +3 -3
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_autocorr_fftw.py +1 -1
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_autocorr_welch.py +4 -4
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_checkset.py +121 -4
- eegprep-0.1.0/src/eegprep/eeg_compare.py +241 -0
- eegprep-0.1.0/src/eegprep/eeg_decodechan.py +102 -0
- eegprep-0.0.2/src/eegprep/eeg_eeglab2mne.py → eegprep-0.1.0/src/eegprep/eeg_eeg2mne.py +7 -7
- eegprep-0.1.0/src/eegprep/eeg_eegrej.py +356 -0
- eegprep-0.1.0/src/eegprep/eeg_findboundaries.py +57 -0
- eegprep-0.1.0/src/eegprep/eeg_interp.py +479 -0
- eegprep-0.1.0/src/eegprep/eeg_lat2point.py +75 -0
- eegprep-0.1.0/src/eegprep/eeg_mne2eeg.py +77 -0
- eegprep-0.0.2/src/eegprep/eeg_mne2eeglab_epochs.py → eegprep-0.1.0/src/eegprep/eeg_mne2eeg_epochs.py +4 -4
- eegprep-0.1.0/src/eegprep/eeg_options.py +48 -0
- eegprep-0.1.0/src/eegprep/eeg_picard.py +109 -0
- eegprep-0.1.0/src/eegprep/eeg_point2lat.py +61 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_rpsd.py +1 -1
- eegprep-0.1.0/src/eegprep/eeglabcompat.py +359 -0
- eegprep-0.1.0/src/eegprep/eegobj.py +238 -0
- eegprep-0.1.0/src/eegprep/eegrej.py +201 -0
- eegprep-0.1.0/src/eegprep/epoch.py +153 -0
- eegprep-0.1.0/src/eegprep/iclabel.py +104 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/iclabel_net.py +25 -25
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/iclabel_net_load_py_measures.py +1 -1
- eegprep-0.1.0/src/eegprep/pinv.py +69 -0
- eegprep-0.1.0/src/eegprep/pop_epoch.py +279 -0
- eegprep-0.1.0/src/eegprep/pop_load_frombids.py +1010 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/pop_loadset.py +49 -31
- eegprep-0.1.0/src/eegprep/pop_loadset_h5.py +266 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/pop_reref.py +20 -10
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/pop_reref_helper.py +4 -4
- eegprep-0.1.0/src/eegprep/pop_resample.py +322 -0
- eegprep-0.1.0/src/eegprep/pop_rmbase.py +241 -0
- eegprep-0.1.0/src/eegprep/pop_saveset.py +257 -0
- eegprep-0.1.0/src/eegprep/pop_select.py +508 -0
- eegprep-0.1.0/src/eegprep/pymat.py +412 -0
- eegprep-0.1.0/src/eegprep/redefine_functions.py +93 -0
- eegprep-0.1.0/src/eegprep/resources/eeg_options_64bit.m +33 -0
- eegprep-0.1.0/src/eegprep/resources/montages/README.md +10 -0
- eegprep-0.1.0/src/eegprep/resources/montages/biosemi-128.locs +0 -0
- eegprep-0.1.0/src/eegprep/resources/montages/biosemi-sccn-128v1.locs +0 -0
- eegprep-0.1.0/src/eegprep/resources/montages/biosemi-sccn-128v2.locs +0 -0
- eegprep-0.1.0/src/eegprep/resources/montages/biosemi-sccn-256v1.locs +0 -0
- eegprep-0.1.0/src/eegprep/resources/montages/egi-gsn-hydrocell-129.locs +0 -0
- eegprep-0.1.0/src/eegprep/resources/montages/egi-gsn-hydrocell-257.locs +0 -0
- eegprep-0.1.0/src/eegprep/resources/montages/standard-10-5-342ch.locs +0 -0
- eegprep-0.1.0/src/eegprep/utils/__init__.py +2 -0
- eegprep-0.1.0/src/eegprep/utils/asr.py +568 -0
- eegprep-0.1.0/src/eegprep/utils/bids.py +170 -0
- eegprep-0.1.0/src/eegprep/utils/coords.py +123 -0
- eegprep-0.1.0/src/eegprep/utils/covariance.py +205 -0
- eegprep-0.1.0/src/eegprep/utils/git.py +66 -0
- eegprep-0.1.0/src/eegprep/utils/logs.py +119 -0
- eegprep-0.1.0/src/eegprep/utils/misc.py +251 -0
- eegprep-0.1.0/src/eegprep/utils/ransac.py +76 -0
- eegprep-0.1.0/src/eegprep/utils/sigproc.py +394 -0
- eegprep-0.1.0/src/eegprep/utils/spatial.py +195 -0
- eegprep-0.1.0/src/eegprep/utils/stats.py +404 -0
- eegprep-0.1.0/src/eegprep/utils/testing.py +126 -0
- eegprep-0.1.0/src/eegprep.egg-info/PKG-INFO +204 -0
- eegprep-0.1.0/src/eegprep.egg-info/SOURCES.txt +127 -0
- eegprep-0.1.0/src/eegprep.egg-info/requires.txt +20 -0
- eegprep-0.1.0/tests/test_ICL_feature_extractor.py +530 -0
- eegprep-0.1.0/tests/test_bids_preproc.py +157 -0
- eegprep-0.1.0/tests/test_bids_preproc_old.py +40 -0
- eegprep-0.1.0/tests/test_clean_artifacts.py +868 -0
- eegprep-0.1.0/tests/test_clean_asr.py +411 -0
- eegprep-0.1.0/tests/test_clean_drifts.py +366 -0
- eegprep-0.1.0/tests/test_clean_flatlines.py +499 -0
- eegprep-0.1.0/tests/test_clean_rawdata.py +293 -0
- eegprep-0.1.0/tests/test_clean_windows.py +478 -0
- eegprep-0.1.0/tests/test_eeg_autocorr.py +325 -0
- eegprep-0.1.0/tests/test_eeg_autocorr_fftw.py +367 -0
- eegprep-0.1.0/tests/test_eeg_autocorr_welch.py +334 -0
- eegprep-0.1.0/tests/test_eeg_compare.py +593 -0
- eegprep-0.1.0/tests/test_eeg_decodechan.py +82 -0
- eegprep-0.1.0/tests/test_eeg_eeg2mne.py +312 -0
- eegprep-0.1.0/tests/test_eeg_eegrej.py +508 -0
- eegprep-0.1.0/tests/test_eeg_interp.py +1022 -0
- eegprep-0.1.0/tests/test_eeg_lat2point.py +115 -0
- eegprep-0.1.0/tests/test_eeg_mne2eeg.py +610 -0
- eegprep-0.1.0/tests/test_eeg_mne2eeg_epochs.py +484 -0
- eegprep-0.1.0/tests/test_eeg_picard.py +422 -0
- eegprep-0.1.0/tests/test_eeg_point2lat.py +284 -0
- eegprep-0.1.0/tests/test_eegfindboundaries.py +369 -0
- eegprep-0.1.0/tests/test_eeglabcompat.py +452 -0
- eegprep-0.1.0/tests/test_eegobj.py +392 -0
- eegprep-0.1.0/tests/test_eegrej.py +77 -0
- eegprep-0.1.0/tests/test_epoch.py +228 -0
- eegprep-0.1.0/tests/test_fixtures.py +235 -0
- eegprep-0.1.0/tests/test_iclabel.py +26 -0
- eegprep-0.1.0/tests/test_import_edf.py +18 -0
- eegprep-0.1.0/tests/test_logs.py +125 -0
- eegprep-0.1.0/tests/test_pinv.py +333 -0
- eegprep-0.1.0/tests/test_pipeline.py +55 -0
- eegprep-0.1.0/tests/test_pop_epoch.py +695 -0
- eegprep-0.1.0/tests/test_pop_loadset_h5.py +557 -0
- eegprep-0.1.0/tests/test_pop_reref.py +315 -0
- eegprep-0.1.0/tests/test_pop_resample.py +76 -0
- eegprep-0.1.0/tests/test_pop_rmbase.py +144 -0
- eegprep-0.1.0/tests/test_pop_saveset.py +98 -0
- eegprep-0.1.0/tests/test_pop_select.py +552 -0
- eegprep-0.1.0/tests/test_pymat.py +838 -0
- eegprep-0.1.0/tests/test_spatial.py +211 -0
- eegprep-0.1.0/tests/test_topoplot.py +548 -0
- eegprep-0.1.0/tests/test_utils_asr.py +672 -0
- eegprep-0.1.0/tests/test_utils_covariance.py +471 -0
- eegprep-0.1.0/tests/test_utils_ransac.py +493 -0
- eegprep-0.1.0/tests/test_utils_testing.py +209 -0
- eegprep-0.0.2/LICENSE +0 -20
- eegprep-0.0.2/PKG-INFO +0 -56
- eegprep-0.0.2/README.md +0 -14
- eegprep-0.0.2/pyproject.toml +0 -33
- eegprep-0.0.2/src/eegprep/ICL_feature_extractor_compare_helper.py +0 -19
- eegprep-0.0.2/src/eegprep/__init__.py +0 -1
- eegprep-0.0.2/src/eegprep/eeg_autocorr_compare_helper.py +0 -13
- eegprep-0.0.2/src/eegprep/eeg_autocorr_fftw_compare_helper.py +0 -20
- eegprep-0.0.2/src/eegprep/eeg_autocorr_welch_compare_helper.py +0 -13
- eegprep-0.0.2/src/eegprep/eeg_mne2eeglab.py +0 -46
- eegprep-0.0.2/src/eegprep/eeg_rpsd_compare_helper.py +0 -17
- eegprep-0.0.2/src/eegprep/eeglabcompat.py +0 -117
- eegprep-0.0.2/src/eegprep/iclabel.py +0 -51
- eegprep-0.0.2/src/eegprep/iclabel_helper.py +0 -26
- eegprep-0.0.2/src/eegprep/pop_reref_compare_helper.py +0 -31
- eegprep-0.0.2/src/eegprep/pop_resample.py +0 -42
- eegprep-0.0.2/src/eegprep/pop_saveset.py +0 -201
- eegprep-0.0.2/src/eegprep/topoplot_compare_helper.py +0 -29
- eegprep-0.0.2/src/eegprep.egg-info/PKG-INFO +0 -56
- eegprep-0.0.2/src/eegprep.egg-info/SOURCES.txt +0 -37
- eegprep-0.0.2/src/eegprep.egg-info/requires.txt +0 -7
- {eegprep-0.0.2 → eegprep-0.1.0}/setup.cfg +0 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/save_struct_as_hdf5.py +0 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/topoplot.py +0 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep.egg-info/dependency_links.txt +0 -0
- {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep.egg-info/top_level.txt +0 -0
eegprep-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Swartz Center for Computational Neuroscience
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
19
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
20
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
21
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
22
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
23
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
24
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
25
|
+
THE POSSIBILITY OF SUCH DAMAGE.
|
eegprep-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eegprep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: EEG preprocessing pipeline on Python
|
|
5
|
+
Author-email: Arnaud Delorme <adelorme@gmail.com>, Young Truong <dt.young112@gmail.com>
|
|
6
|
+
License: BSD 2-Clause License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025, Swartz Center for Computational Neuroscience
|
|
9
|
+
|
|
10
|
+
Redistribution and use in source and binary forms, with or without
|
|
11
|
+
modification, are permitted provided that the following conditions are met:
|
|
12
|
+
|
|
13
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer.
|
|
15
|
+
|
|
16
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
17
|
+
this list of conditions and the following disclaimer in the documentation
|
|
18
|
+
and/or other materials provided with the distribution.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
23
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
24
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
25
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
26
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
27
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
28
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
29
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
30
|
+
THE POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
|
|
32
|
+
Project-URL: Homepage, https://github.com/sccn/eegprep
|
|
33
|
+
Project-URL: Issues, https://github.com/sccn/eegprep/issues
|
|
34
|
+
Keywords: eeg,preprocessing,EEGLAB
|
|
35
|
+
Classifier: Development Status :: 3 - Alpha
|
|
36
|
+
Classifier: Intended Audience :: Science/Research
|
|
37
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
40
|
+
Classifier: Operating System :: OS Independent
|
|
41
|
+
Requires-Python: >=3.10
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
License-File: LICENSE
|
|
44
|
+
Requires-Dist: colorama>=0.4.6
|
|
45
|
+
Requires-Dist: h5py>=3.3.0
|
|
46
|
+
Requires-Dist: matplotlib>=3.4.0
|
|
47
|
+
Requires-Dist: mne>=1.10.0
|
|
48
|
+
Requires-Dist: neo>=0.14.2
|
|
49
|
+
Requires-Dist: numpy<2
|
|
50
|
+
Requires-Dist: oct2py>=5.5.0
|
|
51
|
+
Requires-Dist: psutil>=7.0.0
|
|
52
|
+
Requires-Dist: pybids>=0.4
|
|
53
|
+
Requires-Dist: pyedflib>=0.1.42
|
|
54
|
+
Requires-Dist: python-picard<0.9,>=0.8
|
|
55
|
+
Requires-Dist: scipy>=1.7.0
|
|
56
|
+
Requires-Dist: sympy>=1.14.0
|
|
57
|
+
Requires-Dist: threadpoolctl>=3.6.0
|
|
58
|
+
Provides-Extra: torch
|
|
59
|
+
Requires-Dist: torch>=1.10.0; extra == "torch"
|
|
60
|
+
Provides-Extra: all
|
|
61
|
+
Requires-Dist: torch>=1.10.0; extra == "all"
|
|
62
|
+
Dynamic: license-file
|
|
63
|
+
|
|
64
|
+
# EEGPrep
|
|
65
|
+
|
|
66
|
+
EEGPrep is a Python package that reproduces the EEGLAB default preprocessing pipeline with numerical accuracy down to 1e-5 uV, including clean_rawdata and ICLabel, enabling MATLAB-to-Python equivalence for EEG analysis. It takes BIDS data as input and produces BIDS derivative dataset as output, which can then be reimported into other packages as needed (EEGLAB, Fieldtrip, Brainstorm, MNE). It does produce plots. The package will be fully documented for conversion, packaging, and testing workflows, with installation available via PyPI.
|
|
67
|
+
|
|
68
|
+
## Pre-Release
|
|
69
|
+
|
|
70
|
+
EEGPrep is currently in a pre-release phase. It functions end-to-end (bids branch) but has not yet been tested with multiple BIDS datasets. The documentation is incomplete, and use is at your own risk. The planned release is scheduled for the end of 2025.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## Install
|
|
74
|
+
|
|
75
|
+
To install the complete EEGPrep including the ICLabel classifier (which can pull in ~7GB of binaries on Linux), use the following line:
|
|
76
|
+
```
|
|
77
|
+
pip install eegprep[all]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
To install the lean version:
|
|
81
|
+
```
|
|
82
|
+
pip install eegprep
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You can then manually install a lightweight CPU-only version of PyTorch if desired by
|
|
86
|
+
your operating system.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Comparing MATLAB and Python implementations
|
|
90
|
+
|
|
91
|
+
The MATLAB and Python implementations were compared using the first two subjects from the BIDS datasets [ds003061](https://nemar.org/dataexplorer/detail?dataset_id=ds003061) and [ds002680](https://nemar.org/dataexplorer/detail?dataset_id=ds002680) available on NEMAR. The observed differences were extremely small, with the largest (during HighpassFilter) below 0.002, indicating excellent numerical consistency between the two implementations.
|
|
92
|
+
|
|
93
|
+
<img width="1744" height="1049" alt="Screenshot 2025-10-02 at 11 43 03" src="https://github.com/user-attachments/assets/79c17151-e2e3-4acc-b144-accdf34ae4c5" />
|
|
94
|
+
|
|
95
|
+
# Docker (SCCN Power Users)
|
|
96
|
+
|
|
97
|
+
## Build Docker
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
docker run --rm -it -v $(pwd):/usr/src/project dtyoung/eegprep /bin/bash
|
|
101
|
+
docker run -u root --rm -it -v $(pwd):/usr/src/project dtyoung/eegprep /bin/bash
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Remove Docker
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
docker rmi dtyoung/eegprep
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Mounted folder in /usr/src/project
|
|
111
|
+
|
|
112
|
+
# PYPI Release Process (Maintainers Only)
|
|
113
|
+
|
|
114
|
+
## Quick Release Workflow
|
|
115
|
+
|
|
116
|
+
Use the release script for streamlined releases:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python scripts/make_release.py
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The script will:
|
|
123
|
+
1. Check prerequisites (build, twine, git status)
|
|
124
|
+
2. Confirm the version from `pyproject.toml`
|
|
125
|
+
3. Let you choose: test release, production release, or both
|
|
126
|
+
4. Build and upload the package (automatically uses `eegprep_test` name for TestPyPI)
|
|
127
|
+
5. Create and push git tags for production releases
|
|
128
|
+
|
|
129
|
+
> **Note:** The script automatically handles a TestPyPI naming conflict by building a package
|
|
130
|
+
> with the name `eegprep_test` for test releases.
|
|
131
|
+
|
|
132
|
+
## Prerequisites
|
|
133
|
+
|
|
134
|
+
Install build tools:
|
|
135
|
+
```bash
|
|
136
|
+
pip install build twine
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## API Tokens
|
|
140
|
+
- Get API token for PyPI and TestPyPI (both maintainers should have these)
|
|
141
|
+
- Twine will prompt for them during upload
|
|
142
|
+
- Store them in `~/.pypirc` for convenience
|
|
143
|
+
|
|
144
|
+
## Manual Release Process
|
|
145
|
+
|
|
146
|
+
**Recommended:** Use `scripts/make_release.py` instead to avoid manual errors with package naming.
|
|
147
|
+
|
|
148
|
+
If you need to release manually:
|
|
149
|
+
|
|
150
|
+
**1. Update version in `pyproject.toml`**
|
|
151
|
+
|
|
152
|
+
**2. Test release (staging):**
|
|
153
|
+
|
|
154
|
+
> **Note:** A former maintainer owns the `eegprep` package name on TestPyPI, so you will not be able to post a
|
|
155
|
+
> package named `eegprep` there at this time.
|
|
156
|
+
> To work around this when performing the build manually (note the `make_release.py` script handles this for you), temporarily change the package name to `eegprep_test` in `pyproject.toml` before building.
|
|
157
|
+
> Remember to change it back to `eegprep` after uploading!
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# In pyproject.toml, temporarily change: name = "eegprep" to name = "eegprep_test"
|
|
161
|
+
python -m build
|
|
162
|
+
python -m twine upload --repository testpypi dist/*
|
|
163
|
+
# Change name back to "eegprep" in pyproject.toml
|
|
164
|
+
|
|
165
|
+
# Test the installation:
|
|
166
|
+
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ eegprep_test==X.Y.Z
|
|
167
|
+
# (imports still work as 'import eegprep')
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**3. Production release:**
|
|
171
|
+
```bash
|
|
172
|
+
python -m twine upload dist/*
|
|
173
|
+
git tag -a vX.Y.Z -m "Release version X.Y.Z"
|
|
174
|
+
git push origin vX.Y.Z
|
|
175
|
+
pip install eegprep==X.Y.Z
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Documentation
|
|
179
|
+
https://packaging.python.org/en/latest/tutorials/packaging-projects/
|
|
180
|
+
|
|
181
|
+
## Install Package
|
|
182
|
+
Packaging was done following the tutorial: https://packaging.python.org/en/latest/tutorials/packaging-projects/ with setuptools
|
|
183
|
+
|
|
184
|
+
To install the package with all optional dependencies, run:
|
|
185
|
+
```
|
|
186
|
+
pip install eegprep[all]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Running Tests
|
|
190
|
+
|
|
191
|
+
Install MATLAB interface `pip install /your/path/to/matlab/extern/engines/python`
|
|
192
|
+
Use tests/main_compare.m
|
|
193
|
+
|
|
194
|
+
This project uses `unittest`. You can run tests from the project root via the command:
|
|
195
|
+
```
|
|
196
|
+
python -m unittest discover -s tests
|
|
197
|
+
```
|
|
198
|
+
...or use the unittest integration in your IDE (e.g., PyCharm, VS Code, or Cursor).
|
|
199
|
+
|
|
200
|
+
## Core maintainers
|
|
201
|
+
|
|
202
|
+
- Arnaud Delorme, UCSD, CA, USA
|
|
203
|
+
- Christian Kothe, Intheon, CA, USA
|
|
204
|
+
- Bruno Aristimunha Pinto, Inria, France
|
eegprep-0.1.0/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# EEGPrep
|
|
2
|
+
|
|
3
|
+
EEGPrep is a Python package that reproduces the EEGLAB default preprocessing pipeline with numerical accuracy down to 1e-5 uV, including clean_rawdata and ICLabel, enabling MATLAB-to-Python equivalence for EEG analysis. It takes BIDS data as input and produces BIDS derivative dataset as output, which can then be reimported into other packages as needed (EEGLAB, Fieldtrip, Brainstorm, MNE). It does produce plots. The package will be fully documented for conversion, packaging, and testing workflows, with installation available via PyPI.
|
|
4
|
+
|
|
5
|
+
## Pre-Release
|
|
6
|
+
|
|
7
|
+
EEGPrep is currently in a pre-release phase. It functions end-to-end (bids branch) but has not yet been tested with multiple BIDS datasets. The documentation is incomplete, and use is at your own risk. The planned release is scheduled for the end of 2025.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
To install the complete EEGPrep including the ICLabel classifier (which can pull in ~7GB of binaries on Linux), use the following line:
|
|
13
|
+
```
|
|
14
|
+
pip install eegprep[all]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
To install the lean version:
|
|
18
|
+
```
|
|
19
|
+
pip install eegprep
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You can then manually install a lightweight CPU-only version of PyTorch if desired by
|
|
23
|
+
your operating system.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Comparing MATLAB and Python implementations
|
|
27
|
+
|
|
28
|
+
The MATLAB and Python implementations were compared using the first two subjects from the BIDS datasets [ds003061](https://nemar.org/dataexplorer/detail?dataset_id=ds003061) and [ds002680](https://nemar.org/dataexplorer/detail?dataset_id=ds002680) available on NEMAR. The observed differences were extremely small, with the largest (during HighpassFilter) below 0.002, indicating excellent numerical consistency between the two implementations.
|
|
29
|
+
|
|
30
|
+
<img width="1744" height="1049" alt="Screenshot 2025-10-02 at 11 43 03" src="https://github.com/user-attachments/assets/79c17151-e2e3-4acc-b144-accdf34ae4c5" />
|
|
31
|
+
|
|
32
|
+
# Docker (SCCN Power Users)
|
|
33
|
+
|
|
34
|
+
## Build Docker
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
docker run --rm -it -v $(pwd):/usr/src/project dtyoung/eegprep /bin/bash
|
|
38
|
+
docker run -u root --rm -it -v $(pwd):/usr/src/project dtyoung/eegprep /bin/bash
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Remove Docker
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
docker rmi dtyoung/eegprep
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Mounted folder in /usr/src/project
|
|
48
|
+
|
|
49
|
+
# PYPI Release Process (Maintainers Only)
|
|
50
|
+
|
|
51
|
+
## Quick Release Workflow
|
|
52
|
+
|
|
53
|
+
Use the release script for streamlined releases:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python scripts/make_release.py
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The script will:
|
|
60
|
+
1. Check prerequisites (build, twine, git status)
|
|
61
|
+
2. Confirm the version from `pyproject.toml`
|
|
62
|
+
3. Let you choose: test release, production release, or both
|
|
63
|
+
4. Build and upload the package (automatically uses `eegprep_test` name for TestPyPI)
|
|
64
|
+
5. Create and push git tags for production releases
|
|
65
|
+
|
|
66
|
+
> **Note:** The script automatically handles a TestPyPI naming conflict by building a package
|
|
67
|
+
> with the name `eegprep_test` for test releases.
|
|
68
|
+
|
|
69
|
+
## Prerequisites
|
|
70
|
+
|
|
71
|
+
Install build tools:
|
|
72
|
+
```bash
|
|
73
|
+
pip install build twine
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Tokens
|
|
77
|
+
- Get API token for PyPI and TestPyPI (both maintainers should have these)
|
|
78
|
+
- Twine will prompt for them during upload
|
|
79
|
+
- Store them in `~/.pypirc` for convenience
|
|
80
|
+
|
|
81
|
+
## Manual Release Process
|
|
82
|
+
|
|
83
|
+
**Recommended:** Use `scripts/make_release.py` instead to avoid manual errors with package naming.
|
|
84
|
+
|
|
85
|
+
If you need to release manually:
|
|
86
|
+
|
|
87
|
+
**1. Update version in `pyproject.toml`**
|
|
88
|
+
|
|
89
|
+
**2. Test release (staging):**
|
|
90
|
+
|
|
91
|
+
> **Note:** A former maintainer owns the `eegprep` package name on TestPyPI, so you will not be able to post a
|
|
92
|
+
> package named `eegprep` there at this time.
|
|
93
|
+
> To work around this when performing the build manually (note the `make_release.py` script handles this for you), temporarily change the package name to `eegprep_test` in `pyproject.toml` before building.
|
|
94
|
+
> Remember to change it back to `eegprep` after uploading!
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# In pyproject.toml, temporarily change: name = "eegprep" to name = "eegprep_test"
|
|
98
|
+
python -m build
|
|
99
|
+
python -m twine upload --repository testpypi dist/*
|
|
100
|
+
# Change name back to "eegprep" in pyproject.toml
|
|
101
|
+
|
|
102
|
+
# Test the installation:
|
|
103
|
+
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ eegprep_test==X.Y.Z
|
|
104
|
+
# (imports still work as 'import eegprep')
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**3. Production release:**
|
|
108
|
+
```bash
|
|
109
|
+
python -m twine upload dist/*
|
|
110
|
+
git tag -a vX.Y.Z -m "Release version X.Y.Z"
|
|
111
|
+
git push origin vX.Y.Z
|
|
112
|
+
pip install eegprep==X.Y.Z
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
https://packaging.python.org/en/latest/tutorials/packaging-projects/
|
|
117
|
+
|
|
118
|
+
## Install Package
|
|
119
|
+
Packaging was done following the tutorial: https://packaging.python.org/en/latest/tutorials/packaging-projects/ with setuptools
|
|
120
|
+
|
|
121
|
+
To install the package with all optional dependencies, run:
|
|
122
|
+
```
|
|
123
|
+
pip install eegprep[all]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Running Tests
|
|
127
|
+
|
|
128
|
+
Install MATLAB interface `pip install /your/path/to/matlab/extern/engines/python`
|
|
129
|
+
Use tests/main_compare.m
|
|
130
|
+
|
|
131
|
+
This project uses `unittest`. You can run tests from the project root via the command:
|
|
132
|
+
```
|
|
133
|
+
python -m unittest discover -s tests
|
|
134
|
+
```
|
|
135
|
+
...or use the unittest integration in your IDE (e.g., PyCharm, VS Code, or Cursor).
|
|
136
|
+
|
|
137
|
+
## Core maintainers
|
|
138
|
+
|
|
139
|
+
- Arnaud Delorme, UCSD, CA, USA
|
|
140
|
+
- Christian Kothe, Intheon, CA, USA
|
|
141
|
+
- Bruno Aristimunha Pinto, Inria, France
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "eegprep"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Arnaud Delorme", email="adelorme@gmail.com" },
|
|
10
|
+
{ name="Young Truong", email="dt.young112@gmail.com" },
|
|
11
|
+
]
|
|
12
|
+
description = "EEG preprocessing pipeline on Python"
|
|
13
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
14
|
+
license = { file = "LICENSE" }
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"License :: OSI Approved :: BSD License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
]
|
|
24
|
+
keywords = ["eeg", "preprocessing", "EEGLAB"]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"colorama>=0.4.6",
|
|
27
|
+
"h5py>=3.3.0",
|
|
28
|
+
"matplotlib>=3.4.0",
|
|
29
|
+
"mne>=1.10.0",
|
|
30
|
+
"neo>=0.14.2",
|
|
31
|
+
"numpy<2",
|
|
32
|
+
"oct2py>=5.5.0",
|
|
33
|
+
"psutil>=7.0.0",
|
|
34
|
+
"pybids>=0.4",
|
|
35
|
+
"pyedflib>=0.1.42",
|
|
36
|
+
"python-picard>=0.8,<0.9",
|
|
37
|
+
"scipy>=1.7.0",
|
|
38
|
+
"sympy>=1.14.0",
|
|
39
|
+
"threadpoolctl>=3.6.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
torch = [
|
|
44
|
+
"torch>=1.10.0",
|
|
45
|
+
]
|
|
46
|
+
all = [
|
|
47
|
+
"torch>=1.10.0",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools]
|
|
51
|
+
include-package-data = false
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
where = ["src"]
|
|
55
|
+
exclude = ["eegprep.eeglab*"]
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.package-data]
|
|
58
|
+
"eegprep" = [
|
|
59
|
+
"resources/montages/*.locs",
|
|
60
|
+
"resources/montages/README.md",
|
|
61
|
+
"resources/*.m"
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.exclude-package-data]
|
|
65
|
+
"eegprep" = ["eeglab/**"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
[project.urls]
|
|
69
|
+
Homepage = "https://github.com/sccn/eegprep"
|
|
70
|
+
Issues = "https://github.com/sccn/eegprep/issues"
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
1
2
|
import numpy as np
|
|
2
|
-
from .topoplot import topoplot
|
|
3
|
-
from .eeg_rpsd import eeg_rpsd
|
|
4
|
-
from .eeg_autocorr_welch import eeg_autocorr_welch
|
|
5
|
-
from .eeg_autocorr import eeg_autocorr
|
|
6
|
-
from .eeg_autocorr_fftw import eeg_autocorr_fftw
|
|
7
|
-
from .pop_reref import pop_reref
|
|
8
3
|
|
|
9
4
|
def ICL_feature_extractor(EEG, flag_autocorr=False):
|
|
5
|
+
from eegprep import topoplot
|
|
6
|
+
from eegprep import eeg_rpsd
|
|
7
|
+
from eegprep import eeg_autocorr_welch
|
|
8
|
+
from eegprep import eeg_autocorr
|
|
9
|
+
from eegprep import eeg_autocorr_fftw
|
|
10
|
+
from eegprep import pop_reref
|
|
11
|
+
EEG = deepcopy(EEG)
|
|
10
12
|
|
|
11
13
|
# Check inputs
|
|
12
14
|
ncomp = EEG['icawinv'].shape[1]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from .utils.logs import setup_logging
|
|
4
|
+
setup_logging(logging.INFO)
|
|
5
|
+
|
|
6
|
+
from .iclabel import iclabel
|
|
7
|
+
from .pop_saveset import pop_saveset
|
|
8
|
+
from .pop_loadset import loadset, pop_loadset
|
|
9
|
+
from .pop_loadset_h5 import pop_loadset_h5
|
|
10
|
+
from .pop_resample import pop_resample
|
|
11
|
+
from .eeg_checkset import eeg_checkset
|
|
12
|
+
from .eeglabcompat import pop_eegfiltnew
|
|
13
|
+
from .eeglabcompat import clean_artifacts as eeglab_clean_artifacts
|
|
14
|
+
from .ICL_feature_extractor import ICL_feature_extractor
|
|
15
|
+
from .topoplot import topoplot
|
|
16
|
+
from .eeg_rpsd import eeg_rpsd
|
|
17
|
+
from .eeg_autocorr_welch import eeg_autocorr_welch
|
|
18
|
+
from .eeg_autocorr import eeg_autocorr
|
|
19
|
+
from .eeg_autocorr_fftw import eeg_autocorr_fftw
|
|
20
|
+
from .pop_reref import pop_reref
|
|
21
|
+
from .eeg_picard import eeg_picard
|
|
22
|
+
from .clean_flatlines import clean_flatlines
|
|
23
|
+
from .clean_drifts import clean_drifts
|
|
24
|
+
from .clean_channels_nolocs import clean_channels_nolocs
|
|
25
|
+
from .clean_channels import clean_channels
|
|
26
|
+
from .clean_asr import clean_asr
|
|
27
|
+
from .clean_windows import clean_windows
|
|
28
|
+
from .eeg_compare import eeg_compare
|
|
29
|
+
from .eeg_interp import eeg_interp
|
|
30
|
+
from .clean_artifacts import clean_artifacts
|
|
31
|
+
from .pop_load_frombids import pop_load_frombids
|
|
32
|
+
from .bids_list_eeg_files import bids_list_eeg_files
|
|
33
|
+
from .bids_preproc import bids_preproc
|
|
34
|
+
from .eeg_decodechan import eeg_decodechan
|
|
35
|
+
from .eegrej import eegrej
|
|
36
|
+
from .eeg_eegrej import eeg_eegrej
|
|
37
|
+
from .eegobj import EEGobj
|
|
38
|
+
from .redefine_functions import *
|
|
39
|
+
from .eeg_mne2eeg import eeg_mne2eeg
|
|
40
|
+
from .eeg_mne2eeg_epochs import eeg_mne2eeg_epochs
|
|
41
|
+
from .eeg_lat2point import eeg_lat2point
|
|
42
|
+
from .eeg_point2lat import eeg_point2lat
|
|
43
|
+
from .eeg_options import EEG_OPTIONS
|
|
44
|
+
from .eeg_checkset import eeg_checkset, strict_mode as eeg_checkset_strict_mode
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List, Sequence
|
|
4
|
+
from types import NoneType
|
|
5
|
+
from eegprep.utils.bids import layout_for_fpath
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# list of valid file extensions for raw EEG data files in BIDS format
|
|
11
|
+
eeg_extensions = ('.vhdr', '.edf', '.bdf', '.set')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def bids_list_eeg_files(
|
|
15
|
+
root: str,
|
|
16
|
+
subjects: Sequence[str | int] | str | int = (),
|
|
17
|
+
sessions: Sequence[str | int] | str | int = (),
|
|
18
|
+
runs: Sequence[str | int] | str | int = (),
|
|
19
|
+
tasks: Sequence[str | int] | str | int = (),
|
|
20
|
+
) -> List[str]:
|
|
21
|
+
"""
|
|
22
|
+
Return a list of all EEG raw-data files in a BIDS dataset.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
-----------
|
|
26
|
+
root : str
|
|
27
|
+
The root directory containing BIDS data.
|
|
28
|
+
subjects : Sequence[str | int], optional
|
|
29
|
+
A sequence of subject identifiers or (zero-based) indices to filter the files by.
|
|
30
|
+
If empty, all subjects are included.
|
|
31
|
+
sessions : Sequence[str | int], optional
|
|
32
|
+
A sequence of session identifiers or (zero-based) indices to filter the files by.
|
|
33
|
+
If empty, all sessions are included.
|
|
34
|
+
runs : Sequence[str | int], optional
|
|
35
|
+
A sequence of run numbers or identifiers to filter the files by. If empty, all runs
|
|
36
|
+
are included. Note that zero-based indexing does not apply to runs, unlike
|
|
37
|
+
subjects and sessions since runs are already integers.
|
|
38
|
+
tasks : Sequence[str] | str, optional
|
|
39
|
+
A sequence of task names or single task to filter the files by. If empty, all
|
|
40
|
+
tasks are included (default is an empty sequence).
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
--------
|
|
44
|
+
|
|
45
|
+
List[str]
|
|
46
|
+
A list of file paths to EEG files in the BIDS dataset.
|
|
47
|
+
"""
|
|
48
|
+
layout = layout_for_fpath(root)
|
|
49
|
+
|
|
50
|
+
# prepare filters, if any
|
|
51
|
+
filters = {
|
|
52
|
+
'subject': [subjects] if isinstance(subjects, (str, int)) else subjects,
|
|
53
|
+
'session': [sessions] if isinstance(sessions, (str, int)) else sessions,
|
|
54
|
+
'run': [runs] if isinstance(runs, (str, int)) else runs,
|
|
55
|
+
'task': [tasks] if isinstance(tasks, (str, int)) else tasks,
|
|
56
|
+
}
|
|
57
|
+
filters = {k: v for k, v in filters.items() if v} # remove empty filters
|
|
58
|
+
|
|
59
|
+
# first get all EEG files
|
|
60
|
+
eeg_files = layout.get(suffix='eeg', return_type='filename' if not filters else 'object')
|
|
61
|
+
|
|
62
|
+
# apply filters
|
|
63
|
+
if filters:
|
|
64
|
+
for key, query_values in filters.items():
|
|
65
|
+
for v in query_values:
|
|
66
|
+
if isinstance(v, str) and '-' in v and v.split('-')[0] in ('sub', 'ses', 'run', 'task'):
|
|
67
|
+
raise ValueError("Query values should not be formatted with 'sub-', 'ses-', "
|
|
68
|
+
"'run-', or 'task-' prefixes. Use the raw identifiers instead.")
|
|
69
|
+
data_values = [f.entities.get(key, None) for f in eeg_files]
|
|
70
|
+
if all(v is None for v in data_values):
|
|
71
|
+
# key is missing from entire dataset: ignore the filter
|
|
72
|
+
logger.info(f"Dataset at {root} does not contain any files with the key '{key}'; "
|
|
73
|
+
f"ignoring the filter.")
|
|
74
|
+
elif all(isinstance(v, int) for v in data_values):
|
|
75
|
+
# all items are integers; make sure our queries are also integers if they're not already
|
|
76
|
+
if any(isinstance(v, str) for v in query_values):
|
|
77
|
+
# uniformize query values to integer
|
|
78
|
+
try:
|
|
79
|
+
query_values = [int(v) for v in query_values]
|
|
80
|
+
except Exception as e:
|
|
81
|
+
raise ValueError(f"When filtering by {key}, use integers for the query ({e})")
|
|
82
|
+
eeg_files = [f for f in eeg_files if f.entities[key] in query_values]
|
|
83
|
+
else:
|
|
84
|
+
# data values might be some sort of mix of ints, strings, or None
|
|
85
|
+
# querying those strings by index (with integers)
|
|
86
|
+
if all(isinstance(v, int) for v in query_values):
|
|
87
|
+
# index the applicable values (eg subjects) with integers, alphabetically
|
|
88
|
+
# strip any missing candidates
|
|
89
|
+
data_values = [dv for dv in data_values if dv is not None and dv != '']
|
|
90
|
+
# normalize data values to strings
|
|
91
|
+
data_values = [str(dv) for dv in data_values]
|
|
92
|
+
# get unique values, sorted alphabetically
|
|
93
|
+
uq_values = sorted(set(data_values))
|
|
94
|
+
# rewrite the query values into the first k sorted unique strings
|
|
95
|
+
query_values = [uq_values[i] for i in query_values if i < len(uq_values)]
|
|
96
|
+
if all(isinstance(v, str) for v in query_values):
|
|
97
|
+
# all query values are strings now, can do a direct lookup
|
|
98
|
+
eeg_files = [f for f in eeg_files if str(f.entities.get(key)) in query_values]
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError(f"query values for {key} must either all be strings or all "
|
|
101
|
+
f"integers, but were: {query_values}")
|
|
102
|
+
# reduce to file paths
|
|
103
|
+
eeg_files = [eeg_file.path for eeg_file in eeg_files]
|
|
104
|
+
|
|
105
|
+
# have to filter by extension since the layout will also return the sidecar files
|
|
106
|
+
# (e.g., .fdt and .vmrk) and other files that are not raw EEG data files
|
|
107
|
+
filelist = [fn for fn in eeg_files if fn.endswith(eeg_extensions)]
|
|
108
|
+
return filelist
|