Coverage for /builds/tms-localization/papers/tmsloc_proto/scripts/05_merge_exp_data.py: 82%
89 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-20 07:31 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-20 07:31 +0000
1#!/usr/bin/env python
3"""
4Merges experimental data and saves results in fn_exp specified in subject object file.
5Also corrects coil <-> head distance in separate file '*_corrected.csv'.
7merge_exp_data.py -s <subjectobjfile> -e <exp_id> -m <mesh_idx> -o -d -r
9-s <str> ... filename (incl. path) of subject object file
10-e <int> ... experiment ID
11-m <int> ... mesh idx
12-o ... coil position outlier correction (for conditions with multiple same stimulation locations)
13-d ... coil <-> head distance correction
14-r ... remove coil position with distance > +-5 mm from skin surface
15-p ... plot MEPs
16--start_mep ... Start of time frame after TMS pulse where p2p value is evaluated (in ms)
17--end_mep ... End of time frame after TMS pulse where p2p value is evaluated (in ms)
18"""
20import os
21import h5py
22import pynibs
23import argparse
24import numpy as np
25import pandas as pd
27# Read input
28parser = argparse.ArgumentParser(description='Run SimNIBS to determine electric fields\n')
29parser.add_argument('-s', '--fn_subject',
30 help='Filename (incl. path) of subject object file', required=True, type=str)
31parser.add_argument('-e', '--exp_idx',
32 help='Experiment ID', required=True, type=str)
33parser.add_argument('-m', '--mesh_idx',
34 help='Mesh idx', required=True)
35parser.add_argument('-o', '--outlier_correction',
36 help='Remove outliers of coil position.', required=False,
37 action='store_true')
38parser.add_argument('-d', '--distance_correction',
39 help='Perform coil <-> head distance correction', required=False,
40 action='store_true')
41parser.add_argument('-r', '--remove_coil_skin_distance_outlier',
42 help='Remove coil position with distance > -5/+2 mm from skin surface', required=False,
43 action='store_true')
44parser.add_argument('-p', '--plot',
45 help='Plot MEPs', required=False,
46 action='store_true')
47parser.add_argument('--start_mep',
48 help='Start of time frame after TMS pulse where p2p value is evaluated (in ms)', required=False,
49 default=18)
50parser.add_argument('--end_mep',
51 help='End of time frame after TMS pulse where p2p value is evaluated (in ms)', required=False,
52 default=35)
54args = parser.parse_args()
56fn_subject = os.path.abspath(args.fn_subject)
57exp_id = args.exp_idx
58mesh_idx = args.mesh_idx
59start_mep = float(args.start_mep)
60end_mep = float(args.end_mep)
62if args.outlier_correction:
63 coil_outlier_corr = True
64else:
65 coil_outlier_corr = False
67if args.distance_correction:
68 coil_distance_corr = True
69else:
70 coil_distance_corr = False
72if args.remove_coil_skin_distance_outlier:
73 remove_coil_skin_distance_outlier = True
74else:
75 remove_coil_skin_distance_outlier = False
77if args.plot:
78 plot = True
79else:
80 plot = False
82# load subject object
83subject = pynibs.load_subject(fn_subject)
84drop_mep_idx = None
85mep_onsets = None
86cfs_data_column = 0
87channels = ["channel_0"]
89if 'mep_onsets' in subject.exp[exp_id].keys():
90 mep_onsets = subject.exp[exp_id]['mep_onsets']
92if 'drop_mep_idx' in subject.exp[exp_id].keys():
93 drop_mep_idx = subject.exp[exp_id]['drop_mep_idx']
95if "cfs_data_column" in subject.exp[exp_id].keys():
96 cfs_data_column = subject.exp[exp_id]["cfs_data_column"]
97 channels = ["channel_" + str(i) for i in range(len(cfs_data_column))]
99if "channels" in subject.exp[exp_id].keys():
100 channels = subject.exp[exp_id]["channels"]
101 cfs_data_column = range(len(channels))
103if "fn_exp_hdf5" in subject.exp[exp_id].keys():
104 if os.path.exists(subject.exp[exp_id]["fn_exp_hdf5"][0]):
105 os.remove(subject.exp[exp_id]["fn_exp_hdf5"][0])
107if "fn_exp_csv" in subject.exp[exp_id].keys():
108 if os.path.exists(subject.exp[exp_id]["fn_exp_csv"][0]):
109 os.remove(subject.exp[exp_id]["fn_exp_csv"][0])
111# merged data will be saved in mesh specific subfolder
112subject.exp[exp_id]["fn_exp_hdf5"] = [os.path.join(subject.subject_folder, "exp", exp_id, f"mesh_{mesh_idx}", "experiment.hdf5")]
114if not os.path.exists(os.path.split(subject.exp[exp_id]["fn_exp_hdf5"][0])[0]):
115 os.makedirs(os.path.split(subject.exp[exp_id]["fn_exp_hdf5"][0])[0])
117# merge experimental data to experiment.hdf5
118verbose = True
119if subject.exp[exp_id]['nnav_system'].lower() == "localite":
121 # this is the csv version
122 # pynibs.merge_exp_data_localite(subject=subject, exp_id=exp_id, mesh_idx=mesh_idx,
123 # coil_outlier_corr=coil_outlier_corr,
124 # verbose=verbose, version='csv')
126 # this is the hdf5 version
127 pynibs.expio.localite.merge_exp_data_localite(subject=subject,
128 exp_idx=exp_id,
129 mesh_idx=mesh_idx,
130 coil_outlier_corr_cond=coil_outlier_corr,
131 remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier,
132 coil_distance_corr=coil_distance_corr,
133 verbose=verbose,
134 drop_mep_idx=drop_mep_idx,
135 mep_onsets=mep_onsets,
136 channels=channels,
137 cfs_data_column=cfs_data_column,
138 plot=plot,
139 start_mep=start_mep,
140 end_mep=end_mep)
142elif subject.exp[exp_id]['nnav_system'].lower() == "visor":
143 pynibs.expio.visor.merge_exp_data_visor(subject=subject,
144 exp_id=exp_id,
145 mesh_idx=mesh_idx,
146 verbose=verbose)
149elif subject.exp[exp_id]['nnav_system'].lower() == "brainsight":
150 pynibs.expio.brainsight.merge_exp_data_brainsight(subject=subject,
151 exp_idx=exp_id,
152 mesh_idx=mesh_idx,
153 coil_outlier_corr_cond=coil_outlier_corr,
154 remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier,
155 coil_distance_corr=coil_distance_corr,
156 verbose=verbose,
157 plot=plot,
158 start_mep=start_mep,
159 end_mep=end_mep)
160else:
161 raise NotImplementedError(f"Neuronavigation system {subject.exp[exp_id]['nnav_system']} not supported!"
162 f"Use either 'Localite' or 'Visor'...")
164# Plot coil positions with MEP data
165#######################################################################################################################
166if 'plot_coil_ps_fn' in subject.exp[exp_id].keys():
167 fn_coil_pos = subject.exp[exp_id]['plot_coil_ps_fn']
168else:
169 fn_coil_pos = "plot_coil_pos.hdf5"
170fn_coil_pos = os.path.join(os.path.split(subject.exp[exp_id]["fn_exp_hdf5"][0])[0], fn_coil_pos)
171df_exp = pd.read_hdf(subject.exp[exp_id]["fn_exp_hdf5"][0], "phys_data/postproc/EMG")
173mep = dict()
174for channel in subject.exp[exp_id]["channels"]:
175 mep[channel] = np.array(df_exp[f"p2p_{channel}"])
177# plot coil positions
178pynibs.create_stimsite_from_exp_hdf5(fn_exp=subject.exp[exp_id]["fn_exp_hdf5"][0],
179 fn_hdf=fn_coil_pos,
180 datanames=subject.exp[exp_id]["channels"],
181 data=np.vstack([mep[channel] for channel in mep.keys()]).T,
182 overwrite=True)
184# Create matsimnibs.hdf5
185#######################################################################################################################
186if 'fn_matsimnibs' in subject.exp[exp_id].keys():
187 fn_matsimnibs = subject.exp[exp_id]['fn_matsimnibs']
188else:
189 fn_matsimnibs = "matsimnibs.hdf5"
190fn_matsimnibs = os.path.join(os.path.split(subject.exp[exp_id]["fn_exp_hdf5"][0])[0], fn_matsimnibs)
191fn_exp_hdf5 = subject.exp[exp_id]["fn_exp_hdf5"][0]
193print(f"Reading: {fn_exp_hdf5}")
194df_stim_data = pd.read_hdf(fn_exp_hdf5, "stim_data")
196matsimnibs = np.zeros((4, 4, df_stim_data.shape[0]))
198for i in range(df_stim_data.shape[0]):
199 matsimnibs[:, :, i] = df_stim_data["coil_mean"].iloc[i]
201print(f"Writing: {fn_matsimnibs}")
202with h5py.File(fn_matsimnibs, "w") as f:
203 f.create_dataset("matsimnibs", data=matsimnibs)