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.
Files changed (107) hide show
  1. pyNIBS-0.2024.8.dist-info/LICENSE +623 -0
  2. pyNIBS-0.2024.8.dist-info/METADATA +723 -0
  3. pyNIBS-0.2024.8.dist-info/RECORD +107 -0
  4. pyNIBS-0.2024.8.dist-info/WHEEL +5 -0
  5. pyNIBS-0.2024.8.dist-info/top_level.txt +1 -0
  6. pynibs/__init__.py +34 -0
  7. pynibs/coil.py +1367 -0
  8. pynibs/congruence/__init__.py +15 -0
  9. pynibs/congruence/congruence.py +1108 -0
  10. pynibs/congruence/ext_metrics.py +257 -0
  11. pynibs/congruence/stimulation_threshold.py +318 -0
  12. pynibs/data/configuration_exp0.yaml +59 -0
  13. pynibs/data/configuration_linear_MEP.yaml +61 -0
  14. pynibs/data/configuration_linear_RT.yaml +61 -0
  15. pynibs/data/configuration_sigmoid4.yaml +68 -0
  16. pynibs/data/network mapping configuration/configuration guide.md +238 -0
  17. pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +42 -0
  18. pynibs/data/network mapping configuration/configuration_for_testing.yaml +43 -0
  19. pynibs/data/network mapping configuration/configuration_modelTMS.yaml +43 -0
  20. pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +43 -0
  21. pynibs/data/network mapping configuration/output_documentation.md +185 -0
  22. pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +77 -0
  23. pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +1281 -0
  24. pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +1281 -0
  25. pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +1281 -0
  26. pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +1281 -0
  27. pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +1281 -0
  28. pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +1281 -0
  29. pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +1281 -0
  30. pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +1281 -0
  31. pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +1281 -0
  32. pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +1281 -0
  33. pynibs/expio/Mep.py +1518 -0
  34. pynibs/expio/__init__.py +8 -0
  35. pynibs/expio/brainsight.py +979 -0
  36. pynibs/expio/brainvis.py +71 -0
  37. pynibs/expio/cobot.py +239 -0
  38. pynibs/expio/exp.py +1876 -0
  39. pynibs/expio/fit_funs.py +287 -0
  40. pynibs/expio/localite.py +1987 -0
  41. pynibs/expio/signal_ced.py +51 -0
  42. pynibs/expio/visor.py +624 -0
  43. pynibs/freesurfer.py +502 -0
  44. pynibs/hdf5_io/__init__.py +10 -0
  45. pynibs/hdf5_io/hdf5_io.py +1857 -0
  46. pynibs/hdf5_io/xdmf.py +1542 -0
  47. pynibs/mesh/__init__.py +3 -0
  48. pynibs/mesh/mesh_struct.py +1394 -0
  49. pynibs/mesh/transformations.py +866 -0
  50. pynibs/mesh/utils.py +1103 -0
  51. pynibs/models/_TMS.py +211 -0
  52. pynibs/models/__init__.py +0 -0
  53. pynibs/muap.py +392 -0
  54. pynibs/neuron/__init__.py +2 -0
  55. pynibs/neuron/neuron_regression.py +284 -0
  56. pynibs/neuron/util.py +58 -0
  57. pynibs/optimization/__init__.py +5 -0
  58. pynibs/optimization/multichannel.py +278 -0
  59. pynibs/optimization/opt_mep.py +152 -0
  60. pynibs/optimization/optimization.py +1445 -0
  61. pynibs/optimization/workhorses.py +698 -0
  62. pynibs/pckg/__init__.py +0 -0
  63. pynibs/pckg/biosig/biosig4c++-1.9.5.src_fixed.tar.gz +0 -0
  64. pynibs/pckg/libeep/__init__.py +0 -0
  65. pynibs/pckg/libeep/pyeep.so +0 -0
  66. pynibs/regression/__init__.py +11 -0
  67. pynibs/regression/dual_node_detection.py +2375 -0
  68. pynibs/regression/regression.py +2984 -0
  69. pynibs/regression/score_types.py +0 -0
  70. pynibs/roi/__init__.py +2 -0
  71. pynibs/roi/roi.py +895 -0
  72. pynibs/roi/roi_structs.py +1233 -0
  73. pynibs/subject.py +1009 -0
  74. pynibs/tensor_scaling.py +144 -0
  75. pynibs/tests/data/InstrumentMarker20200225163611937.xml +19 -0
  76. pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +14 -0
  77. pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +6373 -0
  78. pynibs/tests/data/Xdmf.dtd +89 -0
  79. pynibs/tests/data/brainsight_niiImage_nifticoord.txt +145 -0
  80. pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +1434 -0
  81. pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +47 -0
  82. pynibs/tests/data/create_subject_testsub.py +332 -0
  83. pynibs/tests/data/data.hdf5 +0 -0
  84. pynibs/tests/data/geo.hdf5 +0 -0
  85. pynibs/tests/test_coil.py +474 -0
  86. pynibs/tests/test_elements2nodes.py +100 -0
  87. pynibs/tests/test_hdf5_io/test_xdmf.py +61 -0
  88. pynibs/tests/test_mesh_transformations.py +123 -0
  89. pynibs/tests/test_mesh_utils.py +143 -0
  90. pynibs/tests/test_nnav_imports.py +101 -0
  91. pynibs/tests/test_quality_measures.py +117 -0
  92. pynibs/tests/test_regressdata.py +289 -0
  93. pynibs/tests/test_roi.py +17 -0
  94. pynibs/tests/test_rotations.py +86 -0
  95. pynibs/tests/test_subject.py +71 -0
  96. pynibs/tests/test_util.py +24 -0
  97. pynibs/tms_pulse.py +34 -0
  98. pynibs/util/__init__.py +4 -0
  99. pynibs/util/dosing.py +233 -0
  100. pynibs/util/quality_measures.py +562 -0
  101. pynibs/util/rotations.py +340 -0
  102. pynibs/util/simnibs.py +763 -0
  103. pynibs/util/util.py +727 -0
  104. pynibs/visualization/__init__.py +2 -0
  105. pynibs/visualization/para.py +4372 -0
  106. pynibs/visualization/plot_2D.py +137 -0
  107. pynibs/visualization/render_3D.py +347 -0
@@ -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.")