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.
@@ -0,0 +1,137 @@
1
+ import scipy.io
2
+ import torch
3
+ import scipy
4
+ import numpy as np
5
+
6
+ class Reshape(torch.nn.Module):
7
+ def __init__(self, shape):
8
+ super().__init__()
9
+ self.shape = shape
10
+
11
+ def forward(self, x):
12
+ return x.view(x.shape[0], *self.shape)
13
+
14
+ class Concatenate(torch.nn.Module):
15
+ def __init__(self, dim):
16
+ super().__init__()
17
+ self.dim = dim
18
+
19
+ def forward(self, x: list):
20
+ return torch.cat(x, dim=self.dim)
21
+
22
+
23
+ class ICLabelNet(torch.nn.Module):
24
+ def __init__(self, mat_path):
25
+ super().__init__()
26
+ iclabel_matlab = scipy.io.loadmat(mat_path)
27
+ params = iclabel_matlab['params'][0]
28
+ i = 11
29
+ print('shape of param', i, torch.tensor(params[i][1]).shape)
30
+ self.discriminator_image_layer1_conv = torch.nn.Conv2d(in_channels=1, out_channels=128, kernel_size=4, stride=2, padding=1, dilation=1)
31
+ print(self.discriminator_image_layer1_conv.weight.shape)
32
+ self.discriminator_image_layer1_conv.weight = torch.nn.Parameter(torch.tensor(params[0][1]).permute(3, 2, 0, 1))
33
+ self.discriminator_image_layer1_conv.bias = torch.nn.Parameter(torch.tensor(params[1][1]).squeeze())
34
+ self.discriminator_image_layer1_relu = torch.nn.LeakyReLU(0.2)
35
+ self.discriminator_image_layer2_conv = torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=4, stride=2, padding=1, dilation=1)
36
+ self.discriminator_image_layer2_conv.weight = torch.nn.Parameter(torch.tensor(params[2][1]).permute(3, 2, 0, 1))
37
+ self.discriminator_image_layer2_conv.bias = torch.nn.Parameter(torch.tensor(params[3][1]).squeeze())
38
+ self.discriminator_image_layer2_relu = torch.nn.LeakyReLU(0.2)
39
+ self.discriminator_image_layer3_conv = torch.nn.Conv2d(in_channels=256, out_channels=512, kernel_size=4, stride=2, padding=1, dilation=1)
40
+ self.discriminator_image_layer3_conv.weight = torch.nn.Parameter(torch.tensor(params[4][1]).permute(3, 2, 0, 1))
41
+ self.discriminator_image_layer3_conv.bias = torch.nn.Parameter(torch.tensor(params[5][1]).squeeze())
42
+ self.discriminator_image_layer3_relu = torch.nn.LeakyReLU(0.2)
43
+ self.discriminator_psdmed_layer1_conv_conv = torch.nn.Conv2d(in_channels=1, out_channels=128, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
44
+ self.discriminator_psdmed_layer1_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[6][1]).permute(3, 2, 0, 1))
45
+ self.discriminator_psdmed_layer1_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[7][1]).squeeze())
46
+ self.discriminator_psdmed_layer1_conv_relu = torch.nn.LeakyReLU(0.2)
47
+ self.discriminator_psdmed_layer2_conv_conv = torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
48
+ self.discriminator_psdmed_layer2_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[8][1]).permute(3, 2, 0, 1))
49
+ self.discriminator_psdmed_layer2_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[9][1]).squeeze())
50
+ self.discriminator_psdmed_layer2_conv_relu = torch.nn.LeakyReLU(0.2)
51
+ self.discriminator_psdmed_layer3_conv_conv = torch.nn.Conv2d(in_channels=256, out_channels=1, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
52
+ self.discriminator_psdmed_layer3_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[10][1]).unsqueeze(3).permute(3, 2, 0, 1))
53
+ self.discriminator_psdmed_layer3_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[11][1]).squeeze(1))
54
+ self.discriminator_psdmed_layer3_conv_relu = torch.nn.LeakyReLU(0.2)
55
+ self.discriminator_autocorr_layer1_conv_conv = torch.nn.Conv2d(in_channels=1, out_channels=128, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
56
+ self.discriminator_autocorr_layer1_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[12][1]).permute(3, 2, 0, 1))
57
+ self.discriminator_autocorr_layer1_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[13][1]).squeeze())
58
+ self.discriminator_autocorr_layer1_conv_relu = torch.nn.LeakyReLU(0.2)
59
+ self.discriminator_autocorr_layer2_conv_conv = torch.nn.Conv2d(in_channels=128, out_channels=256, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
60
+ self.discriminator_autocorr_layer2_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[14][1]).permute(3, 2, 0, 1))
61
+ self.discriminator_autocorr_layer2_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[15][1]).squeeze())
62
+ self.discriminator_autocorr_layer2_conv_relu = torch.nn.LeakyReLU(0.2)
63
+ self.discriminator_autocorr_layer3_conv_conv = torch.nn.Conv2d(in_channels=256, out_channels=1, kernel_size=(1,3), stride=1, padding=(0,1), dilation=1)
64
+ self.discriminator_autocorr_layer3_conv_conv.weight = torch.nn.Parameter(torch.tensor(params[16][1]).unsqueeze(3).permute(3, 2, 0, 1))
65
+ self.discriminator_autocorr_layer3_conv_conv.bias = torch.nn.Parameter(torch.tensor(params[17][1]).squeeze(1))
66
+ self.discriminator_autocorr_layer3_conv_relu = torch.nn.LeakyReLU(0.2)
67
+ self.discriminator_psdmed_reshape = Reshape((100, 1, 1))
68
+ self.discriminator_psdmed_concat1 = Concatenate(dim=2)
69
+ self.discriminator_psdmed_concat2 = Concatenate(dim=3)
70
+ self.discriminator_autocorr_reshape = Reshape((100, 1, 1))
71
+ self.discriminator_autocorr_concat1 = Concatenate(dim=2)
72
+ self.discriminator_autocorr_concat2 = Concatenate(dim=3)
73
+ self.discriminator_concat = Concatenate(dim=1)
74
+ self.discriminator_conv = torch.nn.Conv2d(in_channels=712, out_channels=7, kernel_size=4, stride=1, padding=0, dilation=1)
75
+ self.discriminator_conv.weight = torch.nn.Parameter(torch.tensor(params[18][1]).permute(3, 2, 0, 1))
76
+ self.discriminator_conv.bias = torch.nn.Parameter(torch.tensor(params[19][1]).squeeze())
77
+ self.discriminator_softmax = torch.nn.Softmax(dim=1)
78
+
79
+ def forward(self, image, psdmed, autocorr):
80
+ x_image = self.discriminator_image_layer1_conv(image)
81
+ x_image = self.discriminator_image_layer1_relu(x_image)
82
+ x_image = self.discriminator_image_layer2_conv(x_image)
83
+ x_image = self.discriminator_image_layer2_relu(x_image)
84
+ x_image = self.discriminator_image_layer3_conv(x_image)
85
+ x_image = self.discriminator_image_layer3_relu(x_image)
86
+ print('x_image', x_image.shape)
87
+
88
+ x_psdmed = self.discriminator_psdmed_layer1_conv_conv(psdmed)
89
+ x_psdmed = self.discriminator_psdmed_layer1_conv_relu(x_psdmed)
90
+ x_psdmed = self.discriminator_psdmed_layer2_conv_conv(x_psdmed)
91
+ x_psdmed = self.discriminator_psdmed_layer2_conv_relu(x_psdmed)
92
+ x_psdmed = self.discriminator_psdmed_layer3_conv_conv(x_psdmed)
93
+ x_psdmed = self.discriminator_psdmed_layer3_conv_relu(x_psdmed)
94
+ x_psdmed = self.discriminator_psdmed_reshape(x_psdmed)
95
+ x_psdmed = self.discriminator_psdmed_concat1([x_psdmed]*4)
96
+ x_psdmed = self.discriminator_psdmed_concat2([x_psdmed]*4)
97
+ print('x_psdmed', x_psdmed.shape)
98
+
99
+ x_autocorr = self.discriminator_autocorr_layer1_conv_conv(autocorr)
100
+ x_autocorr = self.discriminator_autocorr_layer1_conv_relu(x_autocorr)
101
+ x_autocorr = self.discriminator_autocorr_layer2_conv_conv(x_autocorr)
102
+ x_autocorr = self.discriminator_autocorr_layer2_conv_relu(x_autocorr)
103
+ x_autocorr = self.discriminator_autocorr_layer3_conv_conv(x_autocorr)
104
+ x_autocorr = self.discriminator_autocorr_layer3_conv_relu(x_autocorr)
105
+ x_autocorr = self.discriminator_autocorr_reshape(x_autocorr)
106
+ x_autocorr = self.discriminator_autocorr_concat1([x_autocorr]*4)
107
+ x_autocorr = self.discriminator_autocorr_concat2([x_autocorr]*4)
108
+ print('x_autocorr', x_autocorr.shape)
109
+
110
+ x = self.discriminator_concat([x_image, x_psdmed, x_autocorr])
111
+ x = self.discriminator_conv(x)
112
+ print('x', x.shape)
113
+ # subtract max value to avoid overflow
114
+ x = x - torch.max(x, dim=1, keepdim=True).values
115
+ x = self.discriminator_softmax(x)
116
+
117
+ return x
118
+
119
+ if __name__ == "__main__":
120
+ model = ICLabelNet('netICL.mat')
121
+ data = scipy.io.loadmat('python_temp_reformated.mat')
122
+ image_mat = data['grid'][0][0]
123
+ psdmed_mat = data['grid'][0][1]
124
+ autocorr_mat = data['grid'][0][2]
125
+ # assuming third dimension is trivial and last dimension is channel. First two dimensions (32 x 32) are size of topoplot
126
+ image = torch.tensor(image_mat).permute(-1, 2, 0, 1)
127
+ print('image shape', image.shape)
128
+ psdmed = torch.tensor(psdmed_mat).permute(-1, 2, 0, 1)
129
+ print('psd shape', psdmed.shape)
130
+ autocorr = torch.tensor(autocorr_mat).permute(-1, 2, 0, 1)
131
+ print('autocorr shape', autocorr.shape)
132
+ output = model(image, psdmed, autocorr)
133
+ print(output.shape)
134
+
135
+ # save the output to a mat file
136
+ scipy.io.savemat('output4_py.mat', {'output': output.detach().numpy()})
137
+
eegprep/pop_loadset.py ADDED
@@ -0,0 +1,97 @@
1
+ import scipy.io
2
+ import numpy as np
3
+ import os
4
+
5
+ # Allows access using . notation
6
+ # class EEG:
7
+ # def __init__(self, **kwargs):
8
+ # self.__dict__.update(kwargs)
9
+ # def __getitem__(self, key):
10
+ # return self.__dict__[key]
11
+ # def __setitem__(self, key, value):
12
+ # self.__dict__[key] = value
13
+
14
+ default_empty = np.array([])
15
+ #default_empty = None
16
+
17
+ def loadset(file_path):
18
+ return pop_loadset(file_path)
19
+
20
+ def pop_loadset(file_path):
21
+ # Load MATLAB file
22
+ EEG = scipy.io.loadmat(file_path, struct_as_record=False, squeeze_me=True)
23
+
24
+ def new_check(obj):
25
+ # check if obj is a dictionary and apply recursively the function to each object not changing the struture of the dictionary
26
+ if isinstance(obj, dict):
27
+ return {key: new_check(obj[key]) for key in obj}
28
+ # check if obj is a numpy array and apply recursively the function to each object not changing the struture of the array
29
+ elif isinstance(obj, list):
30
+ if len(obj) == 0:
31
+ return default_empty
32
+ else:
33
+ return [new_check(item) for item in obj]
34
+ elif isinstance(obj, np.ndarray):
35
+ # check if empty and return none
36
+ if obj.size == 0:
37
+ return default_empty
38
+ # check if it is a numeric array
39
+ elif obj.dtype.kind in ['i', 'u', 'f', 'c']:
40
+ return obj
41
+ else:
42
+ return np.array([new_check(item) for item in obj])
43
+ # check if it is a scalar or a string and return it
44
+ elif np.isscalar(obj) or isinstance(obj, str):
45
+ return obj
46
+ # check if obj is a mat_struct object and convert it to a dictionary
47
+ elif isinstance(obj, scipy.io.matlab.mat_struct) or isinstance(obj, scipy.io.matlab.mio5_params.mat_struct):
48
+ dict_obj = {}
49
+ for field_name in obj._fieldnames:
50
+ field_value = getattr(obj, field_name)
51
+ dict_obj[field_name] = new_check(field_value)
52
+ return dict_obj
53
+
54
+ # check if EEG['data'] is a string, and if it the case, read the binary float32 file
55
+ #EEG = check_keys(EEG)
56
+ EEG = new_check(EEG)
57
+ if 'EEG' in EEG:
58
+ EEG = EEG['EEG']
59
+
60
+ if 'data' in EEG and isinstance(EEG['data'], str):
61
+ # get path from file_path
62
+ EEG['filepath'] = os.path.dirname(file_path)
63
+ file_name = EEG['filepath'] + os.sep + EEG['data']
64
+ EEG['data'] = np.fromfile(file_name, dtype='float32').reshape( EEG['pnts']*EEG['trials'], EEG['nbchan'])
65
+ EEG['data'] = EEG['data'].T.reshape(EEG['nbchan'], EEG['trials'], EEG['pnts']).transpose(0, 2, 1)
66
+
67
+ # compute ICA activations
68
+ if 'icaweights' in EEG and 'icasphere' in EEG and EEG['icaweights'].size > 0 and EEG['icasphere'].size > 0:
69
+ EEG['icaact'] = np.dot(np.dot(EEG['icaweights'], EEG['icasphere']), EEG['data'].reshape(int(EEG['nbchan']), -1))
70
+ EEG['icaact'] = EEG['icaact'].astype(np.float32)
71
+ EEG['icaact'] = EEG['icaact'].reshape(EEG['icaweights'].shape[0], -1, int(EEG['trials']))
72
+
73
+ # subtract 1 to EEG['icachansind'] to make it 0-based
74
+ if 'icachansind' in EEG and EEG['icachansind'].size > 0:
75
+ EEG['icachansind'] = EEG['icachansind'] - 1
76
+
77
+ # type conversion
78
+ EEG['xmin'] = float(EEG['xmin'])
79
+ EEG['xmax'] = float(EEG['xmax'])
80
+ EEG['srate'] = float(EEG['srate'])
81
+
82
+ return EEG
83
+
84
+ def test_pop_loadset():
85
+ file_path = './tmp2.set'
86
+ file_path = '/System/Volumes/Data/data/data/STUDIES/STERN/S04/Memorize.set' #'./eeglab_data_with_ica_tmp.set'
87
+ EEG = pop_loadset(file_path)
88
+
89
+ # print the keys of the EEG dictionary
90
+ print(EEG.keys())
91
+
92
+ # test_pop_loadset()
93
+
94
+ # STILL OPEN QUESTION: Better to have empty MATLAB arrays as None for empty numpy arrays (current default).
95
+ # The current default is to make it more MALTAB compatible. A lot of MATLAB function start indexing MATLAB
96
+ # empty arrays to add values to them. This is not possible with None and would create more conversion and
97
+ # bugs. However, None is more pythonic.
eegprep/pop_reref.py ADDED
@@ -0,0 +1,44 @@
1
+ import numpy as np
2
+
3
+ def pop_reref(EEG, ref):
4
+
5
+ # check if ref is not empty and not none
6
+ if ref is not None and ref != []:
7
+ raise ValueError('Feature not implemented: The ref parameter must be empty or None')
8
+
9
+ # Check if the number of channels in EEG['icachansind'] is the same as the number of channels in EEG['nbchan']
10
+ if len(EEG['icachansind']) == EEG['nbchan']:
11
+ # Subtract mean from EEG data using broadcasting in NumPy
12
+ EEG['data'] = EEG['data'] - np.mean(EEG['data'], axis=0)
13
+
14
+ #print(np.array2string(EEG['icaweights'][:10, :10], precision=3, suppress_small=True))
15
+
16
+ # Subtract mean from EEG icawinv using broadcasting in NumPy
17
+ EEG['icawinv'] = EEG['icawinv'] - np.mean(EEG['icawinv'], axis=0)
18
+
19
+ # show the first 10 rows and columns of EEG['icawinv']
20
+ # print it pretty line by line with 4 digit after the decimal point
21
+
22
+ # Compute the pseudoinverse of EEG['icawinv']
23
+ EEG['icaweights'] = np.linalg.pinv(EEG['icawinv'])
24
+ #print(' ')
25
+ #print(np.array2string(EEG['icaweights'][:10, :10], precision=3, suppress_small=True))
26
+
27
+ EEG['icasphere'] = np.eye(EEG['nbchan'])
28
+
29
+ # Compute the ICA activations
30
+ # data = EEG['data'].reshape(EEG['data'].shape[0], -1)
31
+ # EEG['icaact'] = np.dot(EEG['icaweights'], data)
32
+ # EEG['icaact'] = EEG['icaact'].reshape(EEG['icaweights'].shape[0], EEG['pnts'], EEG['trials'])
33
+
34
+ # Set EEG['ref'] to 'average'
35
+ EEG['ref'] = 'average'
36
+
37
+ # Update the reference for each channel in EEG['chanlocs']
38
+ for iChan in range(len(EEG['chanlocs'])):
39
+ EEG['chanlocs'][iChan]['ref'] = 'average'
40
+
41
+ return EEG
42
+
43
+ else:
44
+ raise ValueError('Feature not implemented: The number of channels in EEG[''icachansind''] must be the same as the number of channels in EEG[''nbchan'']')
@@ -0,0 +1,31 @@
1
+ from pop_reref import pop_reref
2
+ from pop_loadset import pop_loadset
3
+ import sys
4
+ import os
5
+ import scipy.io
6
+
7
+ # Check if a parameter was provided
8
+ if len(sys.argv) > 1:
9
+ # The first element in sys.argv is the script name, so we access the second one for the argument
10
+ file_name = sys.argv[1]
11
+ print(f"The EEG file is: {file_name}")
12
+ print(f"Output will be in python_temp.mat (result field)")
13
+ else:
14
+ file_name = './eeglab_data_with_ica_tmp.set'
15
+ # raise("Provide the name of a file containing EEG (full path) as the first parameter.")
16
+
17
+ # check if file name exists on disk
18
+ if not os.path.isfile(file_name):
19
+ raise(f"File {file_name} does not exist.")
20
+
21
+ EEG = pop_loadset(file_name)
22
+ EEG = pop_reref(EEG, None)
23
+ EEG['chanlocs'] = []
24
+ EEG['urchanlocs'] = []
25
+ EEG['event'] = []
26
+ EEG['urevent'] = []
27
+ EEG['epoch'] = []
28
+
29
+ # save in a matlab file
30
+ scipy.io.savemat('python_temp.mat', EEG)
31
+ print('Saved python_temp.mat')
@@ -0,0 +1,22 @@
1
+ from ICL_feature_extractor import ICL_feature_extractor
2
+ from pop_loadset import pop_loadset
3
+ from pop_saveset import pop_saveset
4
+ from pop_reref import pop_reref
5
+ import sys
6
+
7
+ # check if a parameter is present and if it is assign eeglab_file_path to it
8
+ if len(sys.argv) > 2:
9
+ eeglab_file_path_in = sys.argv[1]
10
+ eeglab_file_path_out = sys.argv[2]
11
+ else:
12
+ eeglab_file_path_in = './eeglab_data_with_ica_tmp.set'
13
+ eeglab_file_path_out = './eeglab_data_with_ica_tmp_averef.set'
14
+
15
+ EEG = pop_loadset(eeglab_file_path_in)
16
+
17
+ # Print the loaded data
18
+ EEG = pop_reref(EEG, [])
19
+
20
+ # save dataset
21
+ pop_saveset(EEG, eeglab_file_path_out)
22
+
@@ -0,0 +1,42 @@
1
+
2
+ import os
3
+ from scipy.io import savemat
4
+ import tempfile # need to do something with tempfile
5
+ from pop_loadset import pop_loadset
6
+ from pop_saveset import pop_saveset
7
+ from oct2py import octave as eeglab
8
+
9
+ def pop_resample( EEG, freq): # 2 additional parameters in MATLAB (never used)
10
+
11
+ pop_saveset(EEG, 'tmp.set') # 0.8 seconds
12
+
13
+ path2eeglab = '/System/Volumes/Data/data/matlab/eeglab/' # init >10 seconds
14
+ eeglab.addpath(path2eeglab + '/functions/guifunc')
15
+ eeglab.addpath(path2eeglab + '/functions/popfunc')
16
+ eeglab.addpath(path2eeglab + '/functions/adminfunc')
17
+ eeglab.addpath(path2eeglab + '/plugins/firfilt')
18
+ eeglab.addpath(path2eeglab + '/functions/sigprocfunc')
19
+ eeglab.addpath(path2eeglab + '/functions/miscfunc')
20
+ eeglab.addpath(path2eeglab + '/plugins/dipfit')
21
+
22
+ EEG2 = eeglab.pop_loadset('tmp.set') # 2 seconds
23
+ EEG2 = eeglab.pop_resample(EEG2, freq) # 2.4 seconds
24
+ eeglab.pop_saveset(EEG2, 'tmp2.set') # 2.4 seconds
25
+
26
+ EEG3 = pop_loadset('tmp2.set') # 0.2 seconds
27
+
28
+ # delete temporary files
29
+ os.remove('tmp.set')
30
+ os.remove('tmp2.set')
31
+ return EEG3
32
+
33
+ def test_pop_resample():
34
+ eeglab_file_path = './eeglab_data_with_ica_tmp.set'
35
+ EEG = pop_loadset(eeglab_file_path)
36
+ EEG2 = pop_resample(EEG, 100)
37
+ print(EEG2.keys())
38
+ print(EEG2['srate'])
39
+
40
+ # test_pop_resample()
41
+
42
+
eegprep/pop_saveset.py ADDED
@@ -0,0 +1,201 @@
1
+ import scipy.io
2
+ import numpy as np
3
+ import os
4
+
5
+ # Allows access using . notation
6
+ # class EEG:
7
+ # def __init__(self, **kwargs):
8
+ # self.__dict__.update(kwargs)
9
+ # def __getitem__(self, key):
10
+ # return self.__dict__[key]
11
+ # def __setitem__(self, key, value):
12
+ # self.__dict__[key] = value
13
+
14
+ default_empty = np.array([])
15
+
16
+ def flatten_dict_sub(d, parent_key='', sep='_'):
17
+ items = []
18
+ for k, v in d.items():
19
+ new_key = f"{parent_key}{sep}{k}" if parent_key else k
20
+ if isinstance(v, dict):
21
+ items.extend(flatten_dict_sub(v, new_key, sep=sep).items())
22
+ else:
23
+ items.append((new_key, v))
24
+ return dict(items)
25
+
26
+ def flatten_dict(data):
27
+ # Flatten each dictionary and collect the fields and types
28
+ flat_data = [flatten_dict_sub(item) for item in data]
29
+ fields = list(flat_data[0].keys())
30
+ dtypes = []
31
+
32
+ # Determine data types
33
+ for field in fields:
34
+ sample_value = flat_data[0][field]
35
+ if isinstance(sample_value, int):
36
+ dtypes.append((field, np.int32))
37
+ elif isinstance(sample_value, float):
38
+ dtypes.append((field, np.float64))
39
+ else:
40
+ dtypes.append((field, 'U{}'.format(max(len(str(item[field])) for item in flat_data))))
41
+
42
+ # Convert the flattened data to a list of tuples
43
+ data_tuples = [tuple(item[field] for field in fields) for item in flat_data]
44
+
45
+ # Create the rec.array
46
+ dtype = np.dtype([(key, 'O') for key in data[0].keys()])
47
+ rec_array = np.array(data_tuples, dtype=dtype).view(np.recarray)
48
+ return rec_array
49
+
50
+ def saveset(EEG, file_name):
51
+ return pop_saveset(EEG, file_name)
52
+
53
+ def pop_saveset(EEG, file_path):
54
+ # convert Events to structured array
55
+ # if 'event' in EEG:
56
+ # EEG['event'] = flatten_dict(EEG['event'])
57
+
58
+ # add 1 to EEG['icachansind'] to make it 1-based
59
+ if 'icachansind' in EEG and EEG['icachansind'].size > 0:
60
+ EEG['icachansind'] = EEG['icachansind'] + 1
61
+
62
+ # search for array of dictionaries and convert them to flatten_dicts
63
+ for key in EEG:
64
+ if isinstance(EEG[key], np.ndarray) and len(EEG[key]) > 0 and isinstance(EEG[key][0], dict):
65
+ EEG[key] = flatten_dict(EEG[key])
66
+
67
+ EEG['icaact'] = default_empty
68
+ scipy.io.savemat(file_path, EEG)
69
+
70
+ return EEG
71
+
72
+
73
+ # Example to export MNE epochs to EEGLAB dataset
74
+ # Events are not handled correctly in this example but it works
75
+
76
+ import mne
77
+ from mne.datasets import sample
78
+ import numpy as np
79
+ from scipy.io import savemat
80
+
81
+ def pop_saveset2(EEG, file_name):
82
+
83
+ # add 1 to EEG['icachansind'] to make it 1-based
84
+ if 'icachansind' in EEG and EEG['icachansind'].size > 0:
85
+ EEG['icachansind'] = EEG['icachansind'] + 1
86
+
87
+ eeglab_dict = {
88
+ 'setname' : '',
89
+ 'filename' : '',
90
+ 'filepath' : '',
91
+ 'subject' : '',
92
+ 'group' : '',
93
+ 'condition' : '',
94
+ 'session' : np.array([]),
95
+ 'comments' : '',
96
+ 'nbchan' : float(EEG['nbchan']),
97
+ 'trials' : float(EEG['trials']),
98
+ 'pnts' : float(EEG['pnts']),
99
+ 'srate' : float(EEG['srate']),
100
+ 'xmin' : float(EEG['xmin']),
101
+ 'xmax' : float(EEG['xmax']),
102
+ 'times' : EEG['times'],
103
+ 'data' : EEG['data'],
104
+ 'icaact' : np.array([]),
105
+ 'icawinv' : np.array([]),
106
+ 'icasphere' : np.array([]),
107
+ 'icaweights' : np.array([]),
108
+ 'icachansind' : np.array([]),
109
+ 'chanlocs' : np.array([]),
110
+ 'urchanlocs' : np.array([]),
111
+ 'chaninfo' : np.array([]),
112
+ 'ref' : np.array([]),
113
+ 'event' : np.array([]),
114
+ 'urevent' : np.array([]),
115
+ 'eventdescription': np.array([]),
116
+ 'epoch' : np.array([]),
117
+ 'epochdescription': np.array([]),
118
+ 'reject' : np.array([]),
119
+ 'stats' : np.array([]),
120
+ 'specdata' : np.array([]),
121
+ 'specicaact' : np.array([]),
122
+ 'splinefile' : np.array([]),
123
+ 'icasplinefile' : np.array([]),
124
+ 'dipfit' : np.array([]),
125
+ 'history' : np.array([]),
126
+ 'saved' : np.array([]),
127
+ 'etc' : np.array([]),
128
+ 'datfile' : np.array([]),
129
+ 'run' : np.array([]),
130
+ 'roi' : np.array([]),
131
+ }
132
+
133
+ # Create the list of dictionaries with a string field
134
+ d_list = [{
135
+ 'labels': c['labels'],
136
+ 'theta': c['theta'] if not isinstance(c['theta'], np.ndarray) else None,
137
+ 'radius': c['radius'] if not isinstance(c['radius'], np.ndarray) else None,
138
+ 'X': c['X'] if not isinstance(c['X'], np.ndarray) else None,
139
+ 'Y': c['Y'] if not isinstance(c['Y'], np.ndarray) else None,
140
+ 'Z': c['Z'] if not isinstance(c['Z'], np.ndarray) else None,
141
+ 'sph_theta': c['sph_theta'] if not isinstance(c['sph_theta'], np.ndarray) else None,
142
+ 'sph_phi': c['sph_phi'] if not isinstance(c['sph_phi'], np.ndarray) else None,
143
+ 'sph_radius': c['sph_radius'] if not isinstance(c['sph_radius'], np.ndarray) else None,
144
+ 'type': c['type'] if not isinstance(c['type'], np.ndarray) else None,
145
+ 'urchan': c['urchan'] if not isinstance(c['urchan'], np.ndarray) else None,
146
+ 'ref': c['ref'] if not isinstance(c['ref'], np.ndarray) else None
147
+ } for c in EEG['chanlocs']]
148
+
149
+ dtype = np.dtype([
150
+ ('labels', 'U100'), # String up to 100 characters
151
+ ('theta', np.float64),
152
+ ('radius', np.float64),
153
+ ('X', np.float64),
154
+ ('Y', np.float64),
155
+ ('Z', np.float64),
156
+ ('sph_theta', np.float64),
157
+ ('sph_phi', np.float64),
158
+ ('sph_radius', np.float64),
159
+ ('type', 'U10'), # String up to 10 characters
160
+ ('urchan', np.int32),
161
+ ('ref', 'U100') # String up to 100 characters
162
+ ])
163
+
164
+ # Convert the list of dictionaries to a structured NumPy array
165
+ eeglab_dict['chanlocs'] = np.array([
166
+ (
167
+ item['labels'],
168
+ item['theta'],
169
+ item['radius'],
170
+ item['X'],
171
+ item['Y'],
172
+ item['Z'],
173
+ item['sph_theta'],
174
+ item['sph_phi'],
175
+ item['sph_radius'],
176
+ item['type'],
177
+ item['urchan'],
178
+ item['ref']
179
+ )
180
+ for item in d_list
181
+ ], dtype=dtype)
182
+
183
+ # # Step 4: Save the EEGLAB dataset as a .mat file
184
+ savemat(file_name, eeglab_dict)
185
+
186
+ from pop_loadset import pop_loadset
187
+
188
+ def test_pop_saveset():
189
+ file_path = './eeglab_data_with_ica_tmp.set'
190
+ EEG = pop_loadset(file_path)
191
+ pop_saveset( EEG, 'tmp.set')
192
+ pop_saveset2(EEG, 'tmp2.set') # does not do events and function above is better
193
+ # print the keys of the EEG dictionary
194
+ print(EEG.keys())
195
+
196
+ # test_pop_saveset()
197
+
198
+ # STILL OPEN QUESTION: Better to have empty MATLAB arrays as None for empty numpy arrays (current default).
199
+ # The current default is to make it more MALTAB compatible. A lot of MATLAB function start indexing MATLAB
200
+ # empty arrays to add values to them. This is not possible with None and would create more conversion and
201
+ # bugs. However, None is more pythonic.
@@ -0,0 +1,50 @@
1
+ import numpy as np
2
+ import h5py
3
+
4
+ def save_dict_to_hdf5(data, filename, dataset_name):
5
+ # Create a structured dtype from the dictionary keys and their corresponding types
6
+ dtype = []
7
+ for key, value in data.items():
8
+ if isinstance(value, str):
9
+ dtype.append((key, 'S{}'.format(len(value))))
10
+ elif isinstance(value, int):
11
+ dtype.append((key, 'i4'))
12
+ elif isinstance(value, float):
13
+ dtype.append((key, 'f8'))
14
+ elif isinstance(value, np.ndarray):
15
+ dtype.append((key, 'f8', value.shape))
16
+ elif value is None:
17
+ dtype.append((key, 'S4'))
18
+ else:
19
+ raise TypeError(f"Unsupported data type for key {key}: {type(value)}")
20
+
21
+ structured_dtype = np.dtype(dtype)
22
+
23
+ # Convert dictionary values to a structured array
24
+ structured_data = np.array([tuple(
25
+ value.encode('utf-8') if isinstance(value, str) else
26
+ str(value).encode('utf-8') if value is None else
27
+ value for value in data.values()
28
+ )], dtype=structured_dtype)
29
+
30
+ # Save to HDF5
31
+ with h5py.File(filename, 'w') as hdf:
32
+ hdf.create_dataset(dataset_name, data=structured_data)
33
+
34
+ # Example usage
35
+ data = {
36
+ 'labels': 'FPz',
37
+ 'theta': np.array([0,1,2,3]),
38
+ 'radius': 0.5066888888888889,
39
+ 'X': 84.98123361344625,
40
+ 'Y': 0,
41
+ 'Z': -1.7860385037488253,
42
+ 'sph_theta': 0,
43
+ 'sph_phi': -1.203999999999994,
44
+ 'sph_radius': 85,
45
+ 'type': 'EEG',
46
+ 'urchan': 1,
47
+ 'ref': None
48
+ }
49
+
50
+ save_dict_to_hdf5(data, 'data.h5', 'dataset_name')