pyNIBS 0.2024.8__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.
- pyNIBS-0.2024.8.dist-info/LICENSE +623 -0
- pyNIBS-0.2024.8.dist-info/METADATA +723 -0
- pyNIBS-0.2024.8.dist-info/RECORD +107 -0
- pyNIBS-0.2024.8.dist-info/WHEEL +5 -0
- pyNIBS-0.2024.8.dist-info/top_level.txt +1 -0
- pynibs/__init__.py +34 -0
- pynibs/coil.py +1367 -0
- pynibs/congruence/__init__.py +15 -0
- pynibs/congruence/congruence.py +1108 -0
- pynibs/congruence/ext_metrics.py +257 -0
- pynibs/congruence/stimulation_threshold.py +318 -0
- pynibs/data/configuration_exp0.yaml +59 -0
- pynibs/data/configuration_linear_MEP.yaml +61 -0
- pynibs/data/configuration_linear_RT.yaml +61 -0
- pynibs/data/configuration_sigmoid4.yaml +68 -0
- pynibs/data/network mapping configuration/configuration guide.md +238 -0
- pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +42 -0
- pynibs/data/network mapping configuration/configuration_for_testing.yaml +43 -0
- pynibs/data/network mapping configuration/configuration_modelTMS.yaml +43 -0
- pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +43 -0
- pynibs/data/network mapping configuration/output_documentation.md +185 -0
- pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +77 -0
- pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +1281 -0
- pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +1281 -0
- pynibs/expio/Mep.py +1518 -0
- pynibs/expio/__init__.py +8 -0
- pynibs/expio/brainsight.py +979 -0
- pynibs/expio/brainvis.py +71 -0
- pynibs/expio/cobot.py +239 -0
- pynibs/expio/exp.py +1876 -0
- pynibs/expio/fit_funs.py +287 -0
- pynibs/expio/localite.py +1987 -0
- pynibs/expio/signal_ced.py +51 -0
- pynibs/expio/visor.py +624 -0
- pynibs/freesurfer.py +502 -0
- pynibs/hdf5_io/__init__.py +10 -0
- pynibs/hdf5_io/hdf5_io.py +1857 -0
- pynibs/hdf5_io/xdmf.py +1542 -0
- pynibs/mesh/__init__.py +3 -0
- pynibs/mesh/mesh_struct.py +1394 -0
- pynibs/mesh/transformations.py +866 -0
- pynibs/mesh/utils.py +1103 -0
- pynibs/models/_TMS.py +211 -0
- pynibs/models/__init__.py +0 -0
- pynibs/muap.py +392 -0
- pynibs/neuron/__init__.py +2 -0
- pynibs/neuron/neuron_regression.py +284 -0
- pynibs/neuron/util.py +58 -0
- pynibs/optimization/__init__.py +5 -0
- pynibs/optimization/multichannel.py +278 -0
- pynibs/optimization/opt_mep.py +152 -0
- pynibs/optimization/optimization.py +1445 -0
- pynibs/optimization/workhorses.py +698 -0
- pynibs/pckg/__init__.py +0 -0
- pynibs/pckg/biosig/biosig4c++-1.9.5.src_fixed.tar.gz +0 -0
- pynibs/pckg/libeep/__init__.py +0 -0
- pynibs/pckg/libeep/pyeep.so +0 -0
- pynibs/regression/__init__.py +11 -0
- pynibs/regression/dual_node_detection.py +2375 -0
- pynibs/regression/regression.py +2984 -0
- pynibs/regression/score_types.py +0 -0
- pynibs/roi/__init__.py +2 -0
- pynibs/roi/roi.py +895 -0
- pynibs/roi/roi_structs.py +1233 -0
- pynibs/subject.py +1009 -0
- pynibs/tensor_scaling.py +144 -0
- pynibs/tests/data/InstrumentMarker20200225163611937.xml +19 -0
- pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +14 -0
- pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +6373 -0
- pynibs/tests/data/Xdmf.dtd +89 -0
- pynibs/tests/data/brainsight_niiImage_nifticoord.txt +145 -0
- pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +1434 -0
- pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +47 -0
- pynibs/tests/data/create_subject_testsub.py +332 -0
- pynibs/tests/data/data.hdf5 +0 -0
- pynibs/tests/data/geo.hdf5 +0 -0
- pynibs/tests/test_coil.py +474 -0
- pynibs/tests/test_elements2nodes.py +100 -0
- pynibs/tests/test_hdf5_io/test_xdmf.py +61 -0
- pynibs/tests/test_mesh_transformations.py +123 -0
- pynibs/tests/test_mesh_utils.py +143 -0
- pynibs/tests/test_nnav_imports.py +101 -0
- pynibs/tests/test_quality_measures.py +117 -0
- pynibs/tests/test_regressdata.py +289 -0
- pynibs/tests/test_roi.py +17 -0
- pynibs/tests/test_rotations.py +86 -0
- pynibs/tests/test_subject.py +71 -0
- pynibs/tests/test_util.py +24 -0
- pynibs/tms_pulse.py +34 -0
- pynibs/util/__init__.py +4 -0
- pynibs/util/dosing.py +233 -0
- pynibs/util/quality_measures.py +562 -0
- pynibs/util/rotations.py +340 -0
- pynibs/util/simnibs.py +763 -0
- pynibs/util/util.py +727 -0
- pynibs/visualization/__init__.py +2 -0
- pynibs/visualization/para.py +4372 -0
- pynibs/visualization/plot_2D.py +137 -0
- pynibs/visualization/render_3D.py +347 -0
pynibs/expio/brainvis.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def read_channel_names(fname):
|
|
6
|
+
"""
|
|
7
|
+
Reads the channel names from .vhdr (info) file, which is recorded during EEG.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
fname : str
|
|
12
|
+
Filename of .vhdr info file.
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
-------
|
|
16
|
+
channel_names : list of str
|
|
17
|
+
List containing the channel names.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
f = open(fname, "r")
|
|
21
|
+
eof = False
|
|
22
|
+
i_line = 0
|
|
23
|
+
channel_info_start = np.inf
|
|
24
|
+
channel_names = []
|
|
25
|
+
|
|
26
|
+
while not eof:
|
|
27
|
+
lines = f.readline()
|
|
28
|
+
|
|
29
|
+
if lines == "[Channel Infos]\n":
|
|
30
|
+
channel_info_start = 5 + i_line
|
|
31
|
+
|
|
32
|
+
if i_line >= channel_info_start:
|
|
33
|
+
|
|
34
|
+
if lines == "\n":
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
channel_names.append(re.search(r"(?<=\=)(.*?)(?=\,)", lines).group(0))
|
|
38
|
+
|
|
39
|
+
i_line += 1
|
|
40
|
+
|
|
41
|
+
return channel_names
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_sampling_frequency(fname):
|
|
45
|
+
"""
|
|
46
|
+
Reads the sampling frequency from .vhdr (info) file, which is recorded during EEG.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
fname : str
|
|
51
|
+
Filename of .vhdr info file.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
sampling_frequency : float
|
|
56
|
+
Sampling frequency.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
f = open(fname, "r")
|
|
60
|
+
eof = False
|
|
61
|
+
sampling_frequency = True
|
|
62
|
+
|
|
63
|
+
while not eof:
|
|
64
|
+
line = f.readline()
|
|
65
|
+
|
|
66
|
+
if "Sampling Rate [Hz]: " in line:
|
|
67
|
+
start_idx = line.find(":") + 2
|
|
68
|
+
sampling_frequency = float(line[start_idx:])
|
|
69
|
+
break
|
|
70
|
+
|
|
71
|
+
return sampling_frequency
|
pynibs/expio/cobot.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
""" Functions to interact with ANT / OMRON TMS Cobots """
|
|
2
|
+
import os
|
|
3
|
+
import h5py
|
|
4
|
+
import json
|
|
5
|
+
import datetime
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
import pynibs
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=False,
|
|
13
|
+
remove_coil_skin_distance_outlier=True, coil_distance_corr=True,
|
|
14
|
+
verbose=False, plot=False):
|
|
15
|
+
"""
|
|
16
|
+
Merge the TMS coil positions and the mep data into an experiment.hdf5 file.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
subject : pynibs.subject.Subject
|
|
21
|
+
Subject object.
|
|
22
|
+
exp_idx : str
|
|
23
|
+
Experiment ID.
|
|
24
|
+
mesh_idx : str
|
|
25
|
+
Mesh ID.
|
|
26
|
+
coil_outlier_corr_cond : bool
|
|
27
|
+
Correct outlier of coil position and orientation (+-2 mm, +-3 deg) in case of conditions.
|
|
28
|
+
remove_coil_skin_distance_outlier : bool
|
|
29
|
+
Remove outlier of coil position lying too far away from the skin surface (+- 5 mm).
|
|
30
|
+
coil_distance_corr : bool
|
|
31
|
+
Perform coil <-> head distance correction (coil is moved towards head surface until coil touches scalp).
|
|
32
|
+
verbose : bool
|
|
33
|
+
Plot output messages.
|
|
34
|
+
plot : bool, optional, default: False
|
|
35
|
+
Plot MEPs and p2p evaluation.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
import biosig
|
|
39
|
+
except ImportError:
|
|
40
|
+
ImportError("Please install biosig from pynibs/pkg/biosig folder!")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
fn_exp_hdf5 = subject.exp[exp_idx]["fn_exp_hdf5"][0]
|
|
44
|
+
|
|
45
|
+
# 1) EMG
|
|
46
|
+
cfs_fn = subject.exp[exp_idx]["fn_data"][0][0]
|
|
47
|
+
cfs_header = json.loads(biosig.header(cfs_fn))
|
|
48
|
+
cfs_emg = biosig.data(cfs_fn)
|
|
49
|
+
|
|
50
|
+
num_sweeps = cfs_header["NumberOfSweeps"]
|
|
51
|
+
|
|
52
|
+
total_num_samples = cfs_header["NumberOfSamples"]
|
|
53
|
+
samples_per_sweep = int(total_num_samples / num_sweeps)
|
|
54
|
+
sampling_rate = int(cfs_header["Samplingrate"])
|
|
55
|
+
time = np.linspace(0, samples_per_sweep, samples_per_sweep) / sampling_rate
|
|
56
|
+
num_channels = cfs_emg.shape[1]
|
|
57
|
+
|
|
58
|
+
tms_pulse_time = subject.exp[exp_idx]['tms_pulse_time']
|
|
59
|
+
# drop_mep_idx = None # Which MEPs to remove before matching
|
|
60
|
+
start_mep_ms = 18
|
|
61
|
+
end_mep_ms = 40
|
|
62
|
+
fn_plot = None
|
|
63
|
+
|
|
64
|
+
# cfs_data_column = range(num_channels)
|
|
65
|
+
d = dict()
|
|
66
|
+
|
|
67
|
+
# get timestamps
|
|
68
|
+
tms_pulse_timedelta = datetime.timedelta()
|
|
69
|
+
# get hour, minute and second
|
|
70
|
+
time_mep_list = []
|
|
71
|
+
trigger_event_idcs = []
|
|
72
|
+
# convert time string into integer
|
|
73
|
+
for event in cfs_header["EVENT"]:
|
|
74
|
+
date = datetime.datetime.strptime(event["TimeStamp"], '%Y-%b-%d %H:%M:%S')
|
|
75
|
+
|
|
76
|
+
# we are interested in the tms pulse time, so add it to ts
|
|
77
|
+
date += tms_pulse_timedelta
|
|
78
|
+
time_mep_list.append(date)
|
|
79
|
+
|
|
80
|
+
# compute indices in data block corresponding to the events
|
|
81
|
+
if event["TYP"] == "0x7ffe":
|
|
82
|
+
trigger_event_idcs.append(
|
|
83
|
+
round(event["POS"] * cfs_header["Samplingrate"])
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
num_sweeps = min(num_sweeps, len(trigger_event_idcs))
|
|
87
|
+
|
|
88
|
+
for c_idx in range(num_channels):
|
|
89
|
+
# Use emg data startinng from the index of the first trigger event
|
|
90
|
+
# assumptions:
|
|
91
|
+
# - after an initial offset all emg data were captured consecutively
|
|
92
|
+
# - the first emg data frame may be captured without an explicit TMS
|
|
93
|
+
# tigger (eg. by checking the "write to disk" option)
|
|
94
|
+
# - if we had dropouts in between the emg data block (not just at the
|
|
95
|
+
# beginning) we would need to adhere to the entire trigger_event_indices
|
|
96
|
+
# list.
|
|
97
|
+
channel_emg = np.reshape(
|
|
98
|
+
cfs_emg[trigger_event_idcs[0]:, c_idx],
|
|
99
|
+
(num_sweeps, samples_per_sweep)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
d[f"mep_raw_data_time_{c_idx}"] = []
|
|
103
|
+
d[f"mep_filt_data_time_{c_idx}"] = []
|
|
104
|
+
d[f"mep_raw_data_{c_idx}"] = []
|
|
105
|
+
d[f"mep_filt_data_{c_idx}"] = []
|
|
106
|
+
d[f"p2p_{c_idx}"] = []
|
|
107
|
+
d[f"mep_latency_{c_idx}"] = []
|
|
108
|
+
|
|
109
|
+
for s_idx in range(0, num_sweeps):
|
|
110
|
+
if plot:
|
|
111
|
+
fn_channel = os.path.join(os.path.split(subject.exp[exp_idx]["fn_data"][0][0])[0], "plots", str(c_idx))
|
|
112
|
+
fn_plot = os.path.join(fn_channel, f"mep_{s_idx:04}")
|
|
113
|
+
os.makedirs(fn_channel, exist_ok=True)
|
|
114
|
+
|
|
115
|
+
# filter data and calculate p2p values
|
|
116
|
+
p2p, mep_filt_data, latency = pynibs.calc_p2p(
|
|
117
|
+
sweep=channel_emg[s_idx],
|
|
118
|
+
tms_pulse_time=tms_pulse_time,
|
|
119
|
+
sampling_rate=sampling_rate,
|
|
120
|
+
start_mep=start_mep_ms,
|
|
121
|
+
end_mep=end_mep_ms,
|
|
122
|
+
measurement_start_time=0,
|
|
123
|
+
fn_plot=fn_plot
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
d[f"mep_raw_data_time_{c_idx}"].append(time)
|
|
127
|
+
d[f"mep_filt_data_time_{c_idx}"].append(time)
|
|
128
|
+
d[f"mep_raw_data_{c_idx}"].append(channel_emg[s_idx])
|
|
129
|
+
d[f"mep_filt_data_{c_idx}"].append(mep_filt_data)
|
|
130
|
+
d[f"p2p_{c_idx}"].append(p2p)
|
|
131
|
+
d[f"mep_latency_{c_idx}"].append(latency)
|
|
132
|
+
|
|
133
|
+
# 2) coil positions
|
|
134
|
+
csv_fn = subject.exp[exp_idx]["fn_tms_nav"][0][0]
|
|
135
|
+
out_coil_pos_fn = os.path.join(os.path.dirname(csv_fn), "coilpos")
|
|
136
|
+
|
|
137
|
+
csv_coil_pos_data = pd.read_csv(csv_fn)
|
|
138
|
+
centers = csv_coil_pos_data[['pos_x', 'pos_y', 'pos_z']].to_numpy()
|
|
139
|
+
m0 = csv_coil_pos_data[['o_x', 'o_y', 'o_z']].to_numpy()
|
|
140
|
+
m1 = csv_coil_pos_data[['n_x', 'n_y', 'n_z']].to_numpy()
|
|
141
|
+
m2 = csv_coil_pos_data[['a_x', 'a_y', 'a_z']].to_numpy()
|
|
142
|
+
|
|
143
|
+
matsimnibs = np.zeros((centers.shape[0], 4, 4))
|
|
144
|
+
matsimnibs[:, 3, :3] = centers
|
|
145
|
+
matsimnibs[:, 0, :3] = m0
|
|
146
|
+
matsimnibs[:, 1, :3] = m1
|
|
147
|
+
matsimnibs[:, 2, :3] = m2
|
|
148
|
+
matsimnibs[:, 3, 3] = 1
|
|
149
|
+
matsimnibs_t = matsimnibs.transpose()
|
|
150
|
+
|
|
151
|
+
fn_matsimnibs_out = os.path.join(os.path.dirname(out_coil_pos_fn), "matsimnibs.hdf5")
|
|
152
|
+
with h5py.File(fn_matsimnibs_out, 'w') as matsimnibs_hf5:
|
|
153
|
+
matsimnibs_hf5["matsimnibs"] = matsimnibs_t
|
|
154
|
+
|
|
155
|
+
'''
|
|
156
|
+
# remove coil position outliers (in case of conditions)
|
|
157
|
+
#######################################################
|
|
158
|
+
if coil_outlier_corr_cond:
|
|
159
|
+
if verbose:
|
|
160
|
+
print("Removing coil position outliers")
|
|
161
|
+
d = coil_outlier_correction_cond(exp=d,
|
|
162
|
+
outlier_angle=5.,
|
|
163
|
+
outlier_loc=3.,
|
|
164
|
+
fn_exp_out=fn_exp_hdf5)
|
|
165
|
+
'''
|
|
166
|
+
|
|
167
|
+
# perform coil <-> head distance correction
|
|
168
|
+
fn_mesh_hdf5 = subject.mesh[mesh_idx]['fn_mesh_hdf5']
|
|
169
|
+
if coil_distance_corr:
|
|
170
|
+
if verbose:
|
|
171
|
+
print("Performing coil <-> head distance correction")
|
|
172
|
+
matsimnibs_t = pynibs.coil_distance_correction_matsimnibs(matsimnibs=matsimnibs_t,
|
|
173
|
+
fn_mesh_hdf5=fn_mesh_hdf5,
|
|
174
|
+
distance=1,
|
|
175
|
+
remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier)
|
|
176
|
+
|
|
177
|
+
d["coil_0"] = []
|
|
178
|
+
for i in range(centers.shape[0]):
|
|
179
|
+
d["coil_0"].append(matsimnibs_t[:, :, i])
|
|
180
|
+
d["condition"] = csv_coil_pos_data['idx'].to_numpy()
|
|
181
|
+
d["coil_mean"] = d["coil_0"]
|
|
182
|
+
|
|
183
|
+
# 3) save in "experiment.hdf5"
|
|
184
|
+
coil_0_np = np.array(d['coil_0'])
|
|
185
|
+
pynibs.write_coil_pos_hdf5(
|
|
186
|
+
fn_hdf=out_coil_pos_fn,
|
|
187
|
+
centers=coil_0_np[:, :3, 3], # np.array(centers),
|
|
188
|
+
m0=coil_0_np[:, :3, 0], # np.array(m0),
|
|
189
|
+
m1=coil_0_np[:, :3, 1], # np.array(m1),
|
|
190
|
+
m2=coil_0_np[:, :3, 2], # np.array(m2),
|
|
191
|
+
overwrite=True
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# create dictionary of stimulation data
|
|
195
|
+
#######################################
|
|
196
|
+
d_stim_data = dict()
|
|
197
|
+
d_stim_data["coil_0"] = d["coil_0"]
|
|
198
|
+
d_stim_data["number"] = csv_coil_pos_data["idx"].to_numpy()
|
|
199
|
+
d_stim_data["current"] = csv_coil_pos_data["didt"].to_numpy()
|
|
200
|
+
d_stim_data["date"] = csv_coil_pos_data["date"].to_list()
|
|
201
|
+
d_stim_data["time"] = csv_coil_pos_data["time"].to_list()
|
|
202
|
+
d_stim_data["coil_type"] = csv_coil_pos_data["coil_type"].to_list()
|
|
203
|
+
|
|
204
|
+
# create dictionary of raw physiological data
|
|
205
|
+
#############################################
|
|
206
|
+
d_phys_data_raw = dict()
|
|
207
|
+
# d_phys_data_raw["EMG Start"] = []
|
|
208
|
+
# d_phys_data_raw["EMG End"] = []
|
|
209
|
+
# d_phys_data_raw["EMG Res."] = []
|
|
210
|
+
# d_phys_data_raw["EMG Channels"] = []
|
|
211
|
+
d_phys_data_raw["EMG Window Start"] = [start_mep_ms] * num_sweeps
|
|
212
|
+
d_phys_data_raw["EMG Window End"] = [end_mep_ms] * num_sweeps
|
|
213
|
+
|
|
214
|
+
for chan in range(num_channels):
|
|
215
|
+
d_phys_data_raw[f"mep_raw_data_time_{chan}"] = d[f"mep_raw_data_time_{chan}"]
|
|
216
|
+
d_phys_data_raw[f"mep_raw_data_{chan}"] = d[f"mep_raw_data_{chan}"]
|
|
217
|
+
|
|
218
|
+
# create dictionary of postprocessed physiological data
|
|
219
|
+
#######################################################
|
|
220
|
+
d_phys_data_postproc = dict()
|
|
221
|
+
|
|
222
|
+
for chan in range(num_channels):
|
|
223
|
+
d_phys_data_postproc[f"mep_filt_data_time_{chan}"] = d[f"mep_filt_data_time_{chan}"]
|
|
224
|
+
d_phys_data_postproc[f"mep_filt_data_{chan}"] = d[f"mep_filt_data_{chan}"]
|
|
225
|
+
d_phys_data_postproc[f"p2p_{chan}"] = d[f"p2p_{chan}"]
|
|
226
|
+
d_phys_data_postproc[f"mep_latency_{chan}"] = d[f"mep_latency_{chan}"]
|
|
227
|
+
|
|
228
|
+
# create pandas dataframes from dicts
|
|
229
|
+
#####################################
|
|
230
|
+
df_stim_data = pd.DataFrame.from_dict(d_stim_data)
|
|
231
|
+
df_phys_data_raw = pd.DataFrame.from_dict(d_phys_data_raw)
|
|
232
|
+
df_phys_data_postproc = pd.DataFrame.from_dict(d_phys_data_postproc)
|
|
233
|
+
|
|
234
|
+
# save in .hdf5 file
|
|
235
|
+
df_stim_data.to_hdf(fn_exp_hdf5, "stim_data")
|
|
236
|
+
df_phys_data_raw.to_hdf(fn_exp_hdf5, "phys_data/raw/EMG")
|
|
237
|
+
df_phys_data_postproc.to_hdf(fn_exp_hdf5, "phys_data/postproc/EMG")
|
|
238
|
+
|
|
239
|
+
print("Done.")
|