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.
Files changed (150) hide show
  1. eegprep-0.1.0/LICENSE +25 -0
  2. eegprep-0.1.0/PKG-INFO +204 -0
  3. eegprep-0.1.0/README.md +141 -0
  4. eegprep-0.1.0/pyproject.toml +70 -0
  5. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/ICL_feature_extractor.py +8 -6
  6. eegprep-0.1.0/src/eegprep/__init__.py +44 -0
  7. eegprep-0.1.0/src/eegprep/bids_list_eeg_files.py +108 -0
  8. eegprep-0.1.0/src/eegprep/bids_preproc.py +1093 -0
  9. eegprep-0.1.0/src/eegprep/clean_artifacts.py +310 -0
  10. eegprep-0.1.0/src/eegprep/clean_asr.py +190 -0
  11. eegprep-0.1.0/src/eegprep/clean_channels.py +179 -0
  12. eegprep-0.1.0/src/eegprep/clean_channels_nolocs.py +150 -0
  13. eegprep-0.1.0/src/eegprep/clean_drifts.py +52 -0
  14. eegprep-0.1.0/src/eegprep/clean_flatlines.py +73 -0
  15. eegprep-0.1.0/src/eegprep/clean_windows.py +233 -0
  16. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_autocorr.py +3 -3
  17. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_autocorr_fftw.py +1 -1
  18. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_autocorr_welch.py +4 -4
  19. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_checkset.py +121 -4
  20. eegprep-0.1.0/src/eegprep/eeg_compare.py +241 -0
  21. eegprep-0.1.0/src/eegprep/eeg_decodechan.py +102 -0
  22. eegprep-0.0.2/src/eegprep/eeg_eeglab2mne.py → eegprep-0.1.0/src/eegprep/eeg_eeg2mne.py +7 -7
  23. eegprep-0.1.0/src/eegprep/eeg_eegrej.py +356 -0
  24. eegprep-0.1.0/src/eegprep/eeg_findboundaries.py +57 -0
  25. eegprep-0.1.0/src/eegprep/eeg_interp.py +479 -0
  26. eegprep-0.1.0/src/eegprep/eeg_lat2point.py +75 -0
  27. eegprep-0.1.0/src/eegprep/eeg_mne2eeg.py +77 -0
  28. eegprep-0.0.2/src/eegprep/eeg_mne2eeglab_epochs.py → eegprep-0.1.0/src/eegprep/eeg_mne2eeg_epochs.py +4 -4
  29. eegprep-0.1.0/src/eegprep/eeg_options.py +48 -0
  30. eegprep-0.1.0/src/eegprep/eeg_picard.py +109 -0
  31. eegprep-0.1.0/src/eegprep/eeg_point2lat.py +61 -0
  32. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/eeg_rpsd.py +1 -1
  33. eegprep-0.1.0/src/eegprep/eeglabcompat.py +359 -0
  34. eegprep-0.1.0/src/eegprep/eegobj.py +238 -0
  35. eegprep-0.1.0/src/eegprep/eegrej.py +201 -0
  36. eegprep-0.1.0/src/eegprep/epoch.py +153 -0
  37. eegprep-0.1.0/src/eegprep/iclabel.py +104 -0
  38. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/iclabel_net.py +25 -25
  39. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/iclabel_net_load_py_measures.py +1 -1
  40. eegprep-0.1.0/src/eegprep/pinv.py +69 -0
  41. eegprep-0.1.0/src/eegprep/pop_epoch.py +279 -0
  42. eegprep-0.1.0/src/eegprep/pop_load_frombids.py +1010 -0
  43. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/pop_loadset.py +49 -31
  44. eegprep-0.1.0/src/eegprep/pop_loadset_h5.py +266 -0
  45. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/pop_reref.py +20 -10
  46. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/pop_reref_helper.py +4 -4
  47. eegprep-0.1.0/src/eegprep/pop_resample.py +322 -0
  48. eegprep-0.1.0/src/eegprep/pop_rmbase.py +241 -0
  49. eegprep-0.1.0/src/eegprep/pop_saveset.py +257 -0
  50. eegprep-0.1.0/src/eegprep/pop_select.py +508 -0
  51. eegprep-0.1.0/src/eegprep/pymat.py +412 -0
  52. eegprep-0.1.0/src/eegprep/redefine_functions.py +93 -0
  53. eegprep-0.1.0/src/eegprep/resources/eeg_options_64bit.m +33 -0
  54. eegprep-0.1.0/src/eegprep/resources/montages/README.md +10 -0
  55. eegprep-0.1.0/src/eegprep/resources/montages/biosemi-128.locs +0 -0
  56. eegprep-0.1.0/src/eegprep/resources/montages/biosemi-sccn-128v1.locs +0 -0
  57. eegprep-0.1.0/src/eegprep/resources/montages/biosemi-sccn-128v2.locs +0 -0
  58. eegprep-0.1.0/src/eegprep/resources/montages/biosemi-sccn-256v1.locs +0 -0
  59. eegprep-0.1.0/src/eegprep/resources/montages/egi-gsn-hydrocell-129.locs +0 -0
  60. eegprep-0.1.0/src/eegprep/resources/montages/egi-gsn-hydrocell-257.locs +0 -0
  61. eegprep-0.1.0/src/eegprep/resources/montages/standard-10-5-342ch.locs +0 -0
  62. eegprep-0.1.0/src/eegprep/utils/__init__.py +2 -0
  63. eegprep-0.1.0/src/eegprep/utils/asr.py +568 -0
  64. eegprep-0.1.0/src/eegprep/utils/bids.py +170 -0
  65. eegprep-0.1.0/src/eegprep/utils/coords.py +123 -0
  66. eegprep-0.1.0/src/eegprep/utils/covariance.py +205 -0
  67. eegprep-0.1.0/src/eegprep/utils/git.py +66 -0
  68. eegprep-0.1.0/src/eegprep/utils/logs.py +119 -0
  69. eegprep-0.1.0/src/eegprep/utils/misc.py +251 -0
  70. eegprep-0.1.0/src/eegprep/utils/ransac.py +76 -0
  71. eegprep-0.1.0/src/eegprep/utils/sigproc.py +394 -0
  72. eegprep-0.1.0/src/eegprep/utils/spatial.py +195 -0
  73. eegprep-0.1.0/src/eegprep/utils/stats.py +404 -0
  74. eegprep-0.1.0/src/eegprep/utils/testing.py +126 -0
  75. eegprep-0.1.0/src/eegprep.egg-info/PKG-INFO +204 -0
  76. eegprep-0.1.0/src/eegprep.egg-info/SOURCES.txt +127 -0
  77. eegprep-0.1.0/src/eegprep.egg-info/requires.txt +20 -0
  78. eegprep-0.1.0/tests/test_ICL_feature_extractor.py +530 -0
  79. eegprep-0.1.0/tests/test_bids_preproc.py +157 -0
  80. eegprep-0.1.0/tests/test_bids_preproc_old.py +40 -0
  81. eegprep-0.1.0/tests/test_clean_artifacts.py +868 -0
  82. eegprep-0.1.0/tests/test_clean_asr.py +411 -0
  83. eegprep-0.1.0/tests/test_clean_drifts.py +366 -0
  84. eegprep-0.1.0/tests/test_clean_flatlines.py +499 -0
  85. eegprep-0.1.0/tests/test_clean_rawdata.py +293 -0
  86. eegprep-0.1.0/tests/test_clean_windows.py +478 -0
  87. eegprep-0.1.0/tests/test_eeg_autocorr.py +325 -0
  88. eegprep-0.1.0/tests/test_eeg_autocorr_fftw.py +367 -0
  89. eegprep-0.1.0/tests/test_eeg_autocorr_welch.py +334 -0
  90. eegprep-0.1.0/tests/test_eeg_compare.py +593 -0
  91. eegprep-0.1.0/tests/test_eeg_decodechan.py +82 -0
  92. eegprep-0.1.0/tests/test_eeg_eeg2mne.py +312 -0
  93. eegprep-0.1.0/tests/test_eeg_eegrej.py +508 -0
  94. eegprep-0.1.0/tests/test_eeg_interp.py +1022 -0
  95. eegprep-0.1.0/tests/test_eeg_lat2point.py +115 -0
  96. eegprep-0.1.0/tests/test_eeg_mne2eeg.py +610 -0
  97. eegprep-0.1.0/tests/test_eeg_mne2eeg_epochs.py +484 -0
  98. eegprep-0.1.0/tests/test_eeg_picard.py +422 -0
  99. eegprep-0.1.0/tests/test_eeg_point2lat.py +284 -0
  100. eegprep-0.1.0/tests/test_eegfindboundaries.py +369 -0
  101. eegprep-0.1.0/tests/test_eeglabcompat.py +452 -0
  102. eegprep-0.1.0/tests/test_eegobj.py +392 -0
  103. eegprep-0.1.0/tests/test_eegrej.py +77 -0
  104. eegprep-0.1.0/tests/test_epoch.py +228 -0
  105. eegprep-0.1.0/tests/test_fixtures.py +235 -0
  106. eegprep-0.1.0/tests/test_iclabel.py +26 -0
  107. eegprep-0.1.0/tests/test_import_edf.py +18 -0
  108. eegprep-0.1.0/tests/test_logs.py +125 -0
  109. eegprep-0.1.0/tests/test_pinv.py +333 -0
  110. eegprep-0.1.0/tests/test_pipeline.py +55 -0
  111. eegprep-0.1.0/tests/test_pop_epoch.py +695 -0
  112. eegprep-0.1.0/tests/test_pop_loadset_h5.py +557 -0
  113. eegprep-0.1.0/tests/test_pop_reref.py +315 -0
  114. eegprep-0.1.0/tests/test_pop_resample.py +76 -0
  115. eegprep-0.1.0/tests/test_pop_rmbase.py +144 -0
  116. eegprep-0.1.0/tests/test_pop_saveset.py +98 -0
  117. eegprep-0.1.0/tests/test_pop_select.py +552 -0
  118. eegprep-0.1.0/tests/test_pymat.py +838 -0
  119. eegprep-0.1.0/tests/test_spatial.py +211 -0
  120. eegprep-0.1.0/tests/test_topoplot.py +548 -0
  121. eegprep-0.1.0/tests/test_utils_asr.py +672 -0
  122. eegprep-0.1.0/tests/test_utils_covariance.py +471 -0
  123. eegprep-0.1.0/tests/test_utils_ransac.py +493 -0
  124. eegprep-0.1.0/tests/test_utils_testing.py +209 -0
  125. eegprep-0.0.2/LICENSE +0 -20
  126. eegprep-0.0.2/PKG-INFO +0 -56
  127. eegprep-0.0.2/README.md +0 -14
  128. eegprep-0.0.2/pyproject.toml +0 -33
  129. eegprep-0.0.2/src/eegprep/ICL_feature_extractor_compare_helper.py +0 -19
  130. eegprep-0.0.2/src/eegprep/__init__.py +0 -1
  131. eegprep-0.0.2/src/eegprep/eeg_autocorr_compare_helper.py +0 -13
  132. eegprep-0.0.2/src/eegprep/eeg_autocorr_fftw_compare_helper.py +0 -20
  133. eegprep-0.0.2/src/eegprep/eeg_autocorr_welch_compare_helper.py +0 -13
  134. eegprep-0.0.2/src/eegprep/eeg_mne2eeglab.py +0 -46
  135. eegprep-0.0.2/src/eegprep/eeg_rpsd_compare_helper.py +0 -17
  136. eegprep-0.0.2/src/eegprep/eeglabcompat.py +0 -117
  137. eegprep-0.0.2/src/eegprep/iclabel.py +0 -51
  138. eegprep-0.0.2/src/eegprep/iclabel_helper.py +0 -26
  139. eegprep-0.0.2/src/eegprep/pop_reref_compare_helper.py +0 -31
  140. eegprep-0.0.2/src/eegprep/pop_resample.py +0 -42
  141. eegprep-0.0.2/src/eegprep/pop_saveset.py +0 -201
  142. eegprep-0.0.2/src/eegprep/topoplot_compare_helper.py +0 -29
  143. eegprep-0.0.2/src/eegprep.egg-info/PKG-INFO +0 -56
  144. eegprep-0.0.2/src/eegprep.egg-info/SOURCES.txt +0 -37
  145. eegprep-0.0.2/src/eegprep.egg-info/requires.txt +0 -7
  146. {eegprep-0.0.2 → eegprep-0.1.0}/setup.cfg +0 -0
  147. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/save_struct_as_hdf5.py +0 -0
  148. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep/topoplot.py +0 -0
  149. {eegprep-0.0.2 → eegprep-0.1.0}/src/eegprep.egg-info/dependency_links.txt +0 -0
  150. {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
@@ -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