eegprep 0.0.2__py3-none-any.whl

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/topoplot.py ADDED
@@ -0,0 +1,194 @@
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from scipy.interpolate import griddata
4
+ from scipy.interpolate import Rbf
5
+ from scipy.spatial import cKDTree
6
+
7
+ def griddata_v4(x, y, v, xq, yq):
8
+ """
9
+ Python version of MATLAB's GDATAV4 interpolation based on David T. Sandwell's biharmonic spline interpolation.
10
+
11
+ Parameters:
12
+ x, y : 1D arrays of coordinates for known points
13
+ v : 1D array of values at known points
14
+ xq, yq : 2D arrays of query points coordinates
15
+
16
+ Returns:
17
+ vq : 2D array of interpolated values at query points
18
+ """
19
+ # Combine x and y into complex numbers for convenience
20
+ xy = x + 1j * y
21
+
22
+ # Determine distances between points
23
+ d = np.abs(xy[:, None] - xy[None, :])
24
+
25
+ # Determine weights for interpolation
26
+ with np.errstate(divide='ignore', invalid='ignore'):
27
+ g = (d**2) * (np.log(d) - 1) # Green's function
28
+ np.fill_diagonal(g, 0) # Fix value along diagonal
29
+ weights = np.linalg.solve(g, v)
30
+
31
+ # Initialize output array
32
+ m, n = xq.shape
33
+ vq = np.zeros_like(xq)
34
+
35
+ # Evaluate at requested points
36
+ xy = xy[:, None] # Make it column vector for broadcasting
37
+ for i in range(m):
38
+ for j in range(n):
39
+ d = np.abs(xq[i, j] + 1j * yq[i, j] - xy.ravel())
40
+ with np.errstate(divide='ignore', invalid='ignore'):
41
+ g = (d**2) * (np.log(d) - 1) # Green's function
42
+ g[d == 0] = 0 # Handle Green's function at zero
43
+ vq[i, j] = np.dot(g, weights)
44
+
45
+ return vq
46
+
47
+ def topoplot(datavector, chan_locs, **kwargs):
48
+ # Set default values
49
+ noplot = kwargs.get('noplot', 'off')
50
+ plotgrid = kwargs.get('plotgrid', 'off')
51
+ plotchans = kwargs.get('plotchans', [])
52
+ handle = None
53
+ Zi = None
54
+ chanval = np.nan
55
+ rmax = 0.5 # actual head radius
56
+ INTERPLIMITS = 'head'
57
+ INTSQUARE = 'on'
58
+ default_intrad = 1
59
+ ELECTRODES = kwargs.get('ELECTRODES', 'on')
60
+ MAXDEFAULTSHOWLOCS = 64
61
+ intrad = kwargs.get('intrad', np.nan)
62
+ plotrad = kwargs.get('plotrad', np.nan)
63
+ headrad = kwargs.get('headrad', 0.5)
64
+ squeezefac = 1.0
65
+ ContourVals = datavector
66
+ method = kwargs.get('method', 'rbf')
67
+
68
+ # print method
69
+ # print(f'method = {method}')
70
+
71
+ # Handle additional arguments
72
+ if 'noplot' in kwargs:
73
+ noplot = kwargs['noplot']
74
+ if not isinstance(noplot, str):
75
+ if len(noplot) != 2:
76
+ raise ValueError("'noplot' location should be [radius, angle]")
77
+ else:
78
+ chanrad = noplot[0]
79
+ chantheta = noplot[1]
80
+ noplot = 'on'
81
+
82
+ # Set colormap
83
+ cmap = plt.get_cmap('jet')
84
+ cmaplen = cmap.N
85
+ GRID_SCALE = 32
86
+ # GRID_SCALE = 67 # 67 is the default value in EEGLAB
87
+
88
+ if len(datavector) > MAXDEFAULTSHOWLOCS:
89
+ ELECTRODES = 'off'
90
+ else:
91
+ ELECTRODES = 'on'
92
+
93
+ datavector = np.array(datavector).flatten()
94
+ ContourVals = np.array(ContourVals).flatten()
95
+
96
+ # Read the channel location information
97
+ tmpeloc = chan_locs
98
+ labels = [loc['labels'] for loc in tmpeloc]
99
+ indices = [i for i, loc in enumerate(tmpeloc) if 'theta' in loc]
100
+ Th = np.array([tmpeloc[i]['theta'] if i in indices and not isinstance(tmpeloc[i]['theta'], np.ndarray) else np.nan for i in range(len(tmpeloc))])
101
+ Rd = np.array([tmpeloc[i]['radius'] if i in indices and not isinstance(tmpeloc[i]['radius'], np.ndarray) else np.nan for i in range(len(tmpeloc))])
102
+ Th = np.deg2rad(Th)
103
+ allchansind = list(range(len(Th)))
104
+ plotchans = indices
105
+
106
+ if len(tmpeloc) == len(datavector) + 1:
107
+ if plotchans and plotchans[-1] == len(tmpeloc):
108
+ plotchans.pop()
109
+
110
+ if len(datavector) > 1:
111
+ inds = np.union1d(np.where(np.isnan(datavector))[0], np.where(np.isinf(datavector))[0])
112
+ plotchans = list(set(plotchans) - set(inds))
113
+
114
+ x, y = np.cos(Th) * Rd, np.sin(Th) * Rd
115
+ plotchans = np.abs(plotchans)
116
+ allchansind = np.array(allchansind)[plotchans]
117
+ Th, Rd, x, y = Th[plotchans], Rd[plotchans], x[plotchans], y[plotchans]
118
+ labels = np.array(labels)[plotchans]
119
+
120
+ if np.isnan(plotrad):
121
+ plotrad = min(1.0, max(Rd) * 1.02)
122
+ plotrad = max(plotrad, 0.5)
123
+
124
+ if np.isnan(intrad):
125
+ default_intrad = 1
126
+ intrad = min(1.0, max(Rd) * 1.02)
127
+ else:
128
+ default_intrad = 0
129
+ if plotrad > intrad:
130
+ plotrad = intrad
131
+
132
+ pltchans = np.where(Rd <= plotrad)[0]
133
+ intchans = np.where((x <= intrad) & (y <= intrad) if INTSQUARE == 'on' else (Rd <= intrad))[0]
134
+
135
+ labels = labels[pltchans]
136
+
137
+ if datavector is not None and len(datavector) > 1:
138
+ intdatavector = datavector[intchans]
139
+ datavector = datavector[pltchans]
140
+ ContourVals = ContourVals[pltchans]
141
+
142
+ squeezefac = rmax / plotrad
143
+ Rd, intRd = Rd * squeezefac, Rd[intchans] * squeezefac
144
+ x, y, intx, inty = x[pltchans] * squeezefac, y[pltchans] * squeezefac, x[intchans] * squeezefac, y[intchans] * squeezefac
145
+
146
+ if default_intrad:
147
+ xmin, xmax = min(-rmax, min(intx)), max(rmax, max(intx))
148
+ ymin, ymax = min(-rmax, min(inty)), max(rmax, max(inty))
149
+ else:
150
+ xmin, xmax, ymin, ymax = -intrad * squeezefac, intrad * squeezefac, -intrad * squeezefac, intrad * squeezefac
151
+
152
+ xi, yi = np.linspace(xmin, xmax, GRID_SCALE), np.linspace(ymin, ymax, GRID_SCALE)
153
+ yi, xi = np.meshgrid(yi, xi)
154
+
155
+ if method == 'griddata':
156
+ Zi = griddata((inty, intx), intdatavector, (yi, xi), method='cubic')
157
+ else:
158
+ coords = np.array([intx.ravel(), inty.ravel()])
159
+ values = intdatavector.ravel()
160
+
161
+ # find nan in values and remove values
162
+ nanidx = np.where(np.isnan(values))[0]
163
+ coords = np.delete(coords, nanidx, axis=1)
164
+ values = np.delete(values, nanidx)
165
+
166
+ # use griddata_v4 to interpolate
167
+ Zi = griddata_v4(coords[0], coords[1], values, xi, yi)
168
+
169
+ # Create the RBF interpolator
170
+
171
+ # rbf = Rbf(coords[0], coords[1], values, function='inverse') #, function='linear')
172
+ # Zi1 = rbf(xi, yi)
173
+
174
+ # rbf = Rbf(coords[0], coords[1], values, function='thin_plate') #, function='linear')
175
+ # Zi2 = rbf(xi, yi)
176
+
177
+ # # average the two results
178
+ # Zi = (Zi1 + Zi2) / 2
179
+
180
+ mask = (np.sqrt(xi**2 + yi**2) <= rmax)
181
+ Zi[~mask] = np.nan
182
+
183
+ if noplot == 'off':
184
+ plt.imshow(Zi, extent=(xmin, xmax, ymin, ymax), origin='lower', cmap=cmap)
185
+ plt.colorbar()
186
+ plt.scatter(x, y, c='k')
187
+ plt.plot(np.cos(np.linspace(0, 2 * np.pi, 100)) * rmax, np.sin(np.linspace(0, 2 * np.pi, 100)) * rmax, 'k')
188
+ for i, txt in enumerate(labels):
189
+ plt.annotate(txt, (x[i], y[i]), fontsize=8, ha='right')
190
+ plt.title('Topoplot')
191
+ plt.axis('off')
192
+ plt.show()
193
+
194
+ return handle, Zi, plotrad, xi, yi
@@ -0,0 +1,29 @@
1
+ from topoplot import topoplot
2
+ from pop_loadset import pop_loadset
3
+ import sys
4
+
5
+ print('Topoplot compare helper')
6
+
7
+ # need 2 arguments or error
8
+ if len(sys.argv) != 3:
9
+ print(f'len(sys.argv) = {len(sys.argv)}')
10
+ print('Usage: need 2 arguments python topoplot_compare_helper.py <eeglab_file_path> <comp_num>')
11
+ sys.exit(1)
12
+
13
+ # Load the data
14
+ eeglab_file_path = sys.argv[1]
15
+ comp_num = int(sys.argv[2])
16
+
17
+ # '/System/Volumes/Data/data/matlab/eeglab/sample_data/eeglab_data_epochs_ica.set'
18
+ EEG = pop_loadset(eeglab_file_path)
19
+
20
+ # Print the loaded data
21
+ res = topoplot(EEG['icawinv'].transpose()[comp_num], EEG['chanlocs'], noplot='on')
22
+
23
+ grid = res[1]
24
+
25
+ # save in a matlab file
26
+ import scipy.io
27
+ scipy.io.savemat('topoplot_data.mat', {'grid': grid})
28
+ print('Saved topoplot_data.mat')
29
+
@@ -0,0 +1,20 @@
1
+ GNU General Public License
2
+
3
+ Copyright (C) 2024-2025
4
+
5
+ Young Truong, UCSD, dt.young112@gmail.com
6
+ Arnaud Delorme, UCSD, adelorme@ucsd.edu
7
+
8
+ This program is free software; you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation; either version 2 of the License, or
11
+ (at your option) any later version.
12
+
13
+ This program is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with this program; if not, write to the Free Software
20
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1.07 USA
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.1
2
+ Name: eegprep
3
+ Version: 0.0.2
4
+ Summary: EEG preprocessing pipeline on Python
5
+ Author-email: Arnaud Delorme <adelorme@gmail.com>, Young Truong <dt.young112@gmail.com>
6
+ License: GNU General Public License
7
+
8
+ Copyright (C) 2024-2025
9
+
10
+ Young Truong, UCSD, dt.young112@gmail.com
11
+ Arnaud Delorme, UCSD, adelorme@ucsd.edu
12
+
13
+ This program is free software; you can redistribute it and/or modify
14
+ it under the terms of the GNU General Public License as published by
15
+ the Free Software Foundation; either version 2 of the License, or
16
+ (at your option) any later version.
17
+
18
+ This program is distributed in the hope that it will be useful,
19
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ GNU General Public License for more details.
22
+
23
+ You should have received a copy of the GNU General Public License
24
+ along with this program; if not, write to the Free Software
25
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1.07 USA
26
+
27
+ Project-URL: Homepage, https://github.com/sccn/eegprep
28
+ Project-URL: Issues, https://github.com/sccn/eegprep/issues
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Operating System :: OS Independent
32
+ Requires-Python: >=3.8
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: numpy>=1.21.0
36
+ Requires-Dist: scipy>=1.7.0
37
+ Requires-Dist: matplotlib>=3.4.0
38
+ Requires-Dist: mne>=1.0.0
39
+ Requires-Dist: oct2py>=5.5.0
40
+ Requires-Dist: torch>=1.10.0
41
+ Requires-Dist: h5py>=3.3.0
42
+
43
+ ## how to duplicate a function
44
+
45
+ 1. Get a MATLAB file to load and process an EEG file
46
+
47
+ 2. Convert the code using GPT4 or when short Copilot and test in a Notebook. Once the code runs without erroring, move to 3.
48
+
49
+ 3. Use the Jupyter code to create a Python file (not notebook) to load the same file as MATLAB and process it as well (in plain Python, not in a subfunction)
50
+
51
+ 4. Start the debugger in both and compare. Note that it is better to use the debugger on Python file than Jupyter Notebook (could not get it to stop)
52
+
53
+ 5. Once the result is the same, package the Python code in a function with the same name as MATLAB
54
+
55
+ 6. Write the function to compare (see example) and the helper Python function to load the file (note that there could be a general Python helper function)
56
+
@@ -0,0 +1,34 @@
1
+ eegprep/ICL_feature_extractor.py,sha256=UBhCG6CH3OS37lGyQay9vntu2v5pn9nFVqooOjR5QmA,3260
2
+ eegprep/ICL_feature_extractor_compare_helper.py,sha256=AQPinTWHDqfCzSYQ058b2XOiNitCfusKNhzIReU1yfE,597
3
+ eegprep/__init__.py,sha256=l544MjhdEudUAeQEVYQ2aNgee4owaeNgcIfqdakj52g,28
4
+ eegprep/eeg_autocorr.py,sha256=czg6UzGMMdrBfFd6W0mo_0xGSfXk5EJvYDDGl91-h5A,1696
5
+ eegprep/eeg_autocorr_compare_helper.py,sha256=yp-SjCMJQtKzlC9b-OnOXfHMxe8cgV6jawNIzkI0IKA,351
6
+ eegprep/eeg_autocorr_fftw.py,sha256=Zjbsi7-gImj9p7GJePP8FiZT-k4ET14tyknQTf_C-m4,2063
7
+ eegprep/eeg_autocorr_fftw_compare_helper.py,sha256=YKf9-ULNHJ1SdsukGjQUd22QY_1A6N_JnYOwxEoHPgg,578
8
+ eegprep/eeg_autocorr_welch.py,sha256=9UVEy0owqxA3AqeAPi-bNw6wvA6zrAJx3qZdrGOvl1c,2303
9
+ eegprep/eeg_autocorr_welch_compare_helper.py,sha256=wn1zDUOg3HbAdY2DbdSvXWZjuj74TCqduN3kCsHR56E,381
10
+ eegprep/eeg_checkset.py,sha256=DhU4tt4gnSkMPqtnnaKEN-Vvj7AUEtNHIKgFQOt6V0I,4681
11
+ eegprep/eeg_eeglab2mne.py,sha256=NrdsaVNVuBuq1wVddGTGp0ud1J1GRsb_NghukHVlV_Q,1226
12
+ eegprep/eeg_mne2eeglab.py,sha256=4xPcAsP8lUBIr50hhByIKMaRf7ff-7FZ3XrlMt9k31w,1573
13
+ eegprep/eeg_mne2eeglab_epochs.py,sha256=8pSX0Kawd05SRfgqpwQ2Z0sVx33o4H57-D3TL-HnrbE,6350
14
+ eegprep/eeg_rpsd.py,sha256=EAAwUcEooAbfGNisDE5oZns1cU7PgT6Wt6GHqKyNhtI,2077
15
+ eegprep/eeg_rpsd_compare_helper.py,sha256=Po2R3QAjnugaNt4dcS9Y7kPfJy3Fkz3adU88tSI3h4s,457
16
+ eegprep/eeglabcompat.py,sha256=zgv2sI73AEyVYDi1IvlaZgmVsyLtb4g_w1ABzgMloDE,4274
17
+ eegprep/iclabel.py,sha256=8ScIt1-XMgNsrlBAEufzsDfN2K9lZh1z3GNvHPbLDzk,2240
18
+ eegprep/iclabel_helper.py,sha256=8rHz8kWG20ZLE0VKxbOIwTq-jfZs8vmweHnOiztth7A,815
19
+ eegprep/iclabel_net.py,sha256=iQzLkD2K46Mj-JkHmVC2vHO80PfrigyuqMDjRiHlWec,8999
20
+ eegprep/iclabel_net_load_py_measures.py,sha256=GrAvWKh0fdhKwHja3hQtQB-roP0D7vE5zVoVZqx8TQM,8951
21
+ eegprep/pop_loadset.py,sha256=tX8q2TrAow_oJMrTsAZU1sAtnrL4fDpKSHyXw38ODVs,4079
22
+ eegprep/pop_reref.py,sha256=LEfzvZ9VK2f2ck2-3_hORDb-9zm60CyGUlMTOTTfIM0,1967
23
+ eegprep/pop_reref_compare_helper.py,sha256=3xs76U-j4F9wf_DfGyG9lgYPZDYfUA0E_fSqPsf_DkE,922
24
+ eegprep/pop_reref_helper.py,sha256=qNYaQLfd3LGxPDzyaQn730DO9UWxMxQKFroCvqjjuGk,632
25
+ eegprep/pop_resample.py,sha256=3kS1TLG2rQKkCpSlqFtpUVLvqRUZcQXOVhTkBOLE4Dc,1365
26
+ eegprep/pop_saveset.py,sha256=ahN23cv6aq5MoEKnK6EmRopcZy6KeFu0YkFIpCIN2l4,7488
27
+ eegprep/save_struct_as_hdf5.py,sha256=ovsyv5iKtQN9c57c244ha4xha0uNFFfsDs4V-fNt608,1557
28
+ eegprep/topoplot.py,sha256=cZT9evJ7j5DReoshnzR3Ns5UA6D9ZTEzCIiZwAVrMJY,6863
29
+ eegprep/topoplot_compare_helper.py,sha256=H4a-K6Q9tmZqaoENCzRXbKJGhGvmXyv1KpXe4MunsG4,781
30
+ eegprep-0.0.2.dist-info/LICENSE,sha256=Xafu48R-h_kyaNj2tuhfgdEv9_ovciktjUEgRRwMZ6w,812
31
+ eegprep-0.0.2.dist-info/METADATA,sha256=dyTQhcmhCHrNf7cQB_96ddR9v4-5d_A3UpTBGaYHq1w,2493
32
+ eegprep-0.0.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
33
+ eegprep-0.0.2.dist-info/top_level.txt,sha256=PvYxowtqrnpb1Nz5a3sJMCDqi-PX8CqYys4wQRhko-s,8
34
+ eegprep-0.0.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.6.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ eegprep