class ExperimentTimeline(QtGui.QWidget): def __init__(self): QtGui.QWidget.__init__(self) self.channels = None self.start_time = None # starting time according to NWB file self.layout = QtGui.QGridLayout() self.setLayout(self.layout) self.layout.setContentsMargins(0, 0, 0, 0) self.plots = PlotGrid() self.plots.set_shape(config.n_headstages, 1) self.plots.setXLink(self.plots[0, 0]) self.layout.addWidget(self.plots, 0, 0) self.ptree = pg.parametertree.ParameterTree(showHeader=False) self.layout.addWidget(self.ptree, 0, 1) self.ptree.setMaximumWidth(250) self.params = pg.parametertree.Parameter.create(name='params', type='group', addText='Add pipette') self.params.addNew = self.add_pipette_clicked # monkey! self.ptree.setParameters(self.params, showTop=False) def list_channels(self): return self.channels def get_channel_plot(self, chan): return self.plots[chan, 0] def add_pipette_clicked(self): self.add_pipette(channel=self.channels[0], start=0, stop=500) def remove_pipettes(self): for ch in self.params.children(): self.params.removeChild(ch) ch.region.scene().removeItem(ch.region) for i in range(self.plots.shape[0]): self.plots[i, 0].clear() def load_site(self, site_dh): """Generate pipette list for this site """ self.remove_pipettes() # automatically fill pipette fluorophore field expt_dh = site_dh.parent().parent() expt_info = expt_dh.info() dye = expt_info.get('internal_dye', None) internal = expt_info.get('internal', None) # automatically select electrode regions self.channels = list(range(config.n_headstages)) site_info = site_dh.info() for i in self.channels: hs_state = site_info.get('Headstage %d' % (i + 1), None) status = { 'NS': 'No seal', 'LS': 'Low seal', 'GS': 'GOhm seal', 'TF': 'Technical failure', 'NA': 'No attempt', None: 'Not recorded', }.get(hs_state, hs_state) self.add_pipette(i, status=status, internal_dye=dye, internal=internal) def load_nwb(self, nwb_handle): with pg.BusyCursor(): self._load_nwb(nwb_handle) def _load_nwb(self, nwb_handle): self.nwb_handle = nwb_handle self.nwb = MiesNwb(nwb_handle.name()) # load all recordings recs = {} for srec in self.nwb.contents: for chan in srec.devices: recs.setdefault(chan, []).append(srec[chan]) chans = sorted(recs.keys()) # find time of first recording start_time = min([rec[0].start_time for rec in recs.values()]) self.start_time = start_time end_time = max([rec[-1].start_time for rec in recs.values()]) self.plots.setXRange(0, (end_time - start_time).seconds) # plot all recordings for i, chan in enumerate(chans): n_recs = len(recs[chan]) times = np.empty(n_recs) i_hold = np.empty(n_recs) v_hold = np.empty(n_recs) v_noise = np.empty(n_recs) i_noise = np.empty(n_recs) # load QC metrics for all recordings for j, rec in enumerate(recs[chan]): dt = (rec.start_time - start_time).seconds times[j] = dt v_hold[j] = rec.baseline_potential i_hold[j] = rec.baseline_current if rec.clamp_mode == 'vc': v_noise[j] = np.nan i_noise[j] = rec.baseline_rms_noise else: v_noise[j] = rec.baseline_rms_noise i_noise[j] = np.nan # scale all qc metrics to the range 0-1 pass_brush = pg.mkBrush(100, 100, 255, 200) fail_brush = pg.mkBrush(255, 0, 0, 200) v_hold = (v_hold + 60e-3) / 20e-3 i_hold = i_hold / 400e-12 v_noise = v_noise / 5e-3 i_noise = i_noise / 100e-12 plt = self.get_channel_plot(chan) plt.setLabels(left=("Ch %d" % chan)) for data, symbol in [(np.zeros_like(times), 'o'), (v_hold, 't'), (i_hold, 'x'), (v_noise, 't1'), (i_noise, 'x')]: brushes = np.where(np.abs(data) > 1.0, fail_brush, pass_brush) plt.plot(times, data, pen=None, symbol=symbol, symbolPen=None, symbolBrush=brushes) for i in recs.keys(): start = (recs[i][0].start_time - start_time).seconds - 1 stop = (recs[i][-1].start_time - start_time).seconds + 1 pip_param = self.params.child('Pipette %d' % (i + 1)) pip_param.set_time_range(start, stop) got_data = len(recs[i]) > 2 pip_param['got data'] = got_data def add_pipette(self, channel, status=None, **kwds): elec = PipetteParameter(self, channel, status=status, **kwds) self.params.addChild(elec, autoIncrementName=True) elec.child('channel').sigValueChanged.connect( self._pipette_channel_changed) elec.region.sigRegionChangeFinished.connect( self._pipette_region_changed) self._pipette_channel_changed(elec.child('channel')) def _pipette_channel_changed(self, param): plt = self.get_channel_plot(param.value()) plt.addItem(param.parent().region) self._rename_pipettes() def _pipette_region_changed(self): self._rename_pipettes() def _rename_pipettes(self): # sort electrodes by channel elecs = {} for elec in self.params.children(): elecs.setdefault(elec['channel'], []).append(elec) for chan in elecs: # sort all electrodes on this channel by start time chan_elecs = sorted(elecs[chan], key=lambda e: e.region.getRegion()[0]) # assign names for i, elec in enumerate(chan_elecs): # rename all first to avoid name colisions elec.setName('rename%d' % i) for i, elec in enumerate(chan_elecs): # If there are multiple electrodes on this channel, then # each extra electrode increments its name by the number of # headstages (for example, on AD channel 3, the first electrode # is called "Electrode 4", and on an 8-headstage system, the # second electrode will be "Electrode 12"). e_id = (chan + 1) + (i * config.n_headstages) elec.id = e_id elec.setName('Pipette %d' % e_id) def save(self): state = {} for elec in self.params.children(): rgn = elec.region.getRegion() if self.start_time is None: start = None stop = None else: start = self.start_time + datetime.timedelta(seconds=rgn[0]) stop = self.start_time + datetime.timedelta(seconds=rgn[1]) state[elec.id] = OrderedDict([ ('pipette_status', elec['status']), ('got_data', elec['got data']), ('ad_channel', elec['channel']), ('patch_start', start), ('patch_stop', stop), ('cell_labels', { 'biocytin': '', 'red': '', 'green': '', 'blue': '' }), #('cell_qc', {'holding': None, 'access': None, 'spiking': None}), ('target_layer', elec['target layer']), ('morphology', elec['morphology']), ('internal_solution', elec['internal']), ('internal_dye', elec['internal dye']), ('synapse_to', None), ('gap_to', None), ('notes', ''), ]) return state
params = pg.parametertree.Parameter.create(name='params', type='group', children=[ dict(name='data', type='list', values=trace_names), evd.params, ]) pt.setParameters(params, showTop=False) hs.addWidget(pt) plots = PlotGrid() plots.set_shape(2, 1) plots.setXLink(plots[0, 0]) hs.addWidget(plots) evd.set_plots(plots[0, 0], plots[1, 0]) def update(auto_range=False): evd.process(traces[params['data']]) if auto_range: plots[0, 0].autoRange() evd.parameters_changed.connect(lambda: update(auto_range=False)) params.child('data').sigValueChanged.connect(lambda: update(auto_range=True)) update(auto_range=True)
plt = plots[i, j] plt.setLabels(left=('%s-%s n=%d'%(pre_type, post_type, len(rg)), 'V'), bottom=('Time', 's')) base = np.median(avg.data[:100]) plt.plot(avg.time_values, avg.data - base) # for first pulse fit = fit_psp(avg, yoffset=0, amp_ratio=(0, 'fixed'), mask_stim_artifact=True) # for later pulses #fit = fit_psp(avg, yoffset=0, mask_stim_artifact=True) amps[(pre_type, post_type)] = fit.best_values['amp'] plt.plot(avg.time_values, fit.eval()-base, pen='g') plots.setXLink(plots[0,0]) for i in range(2): for j in range(len(types)): plots[i,j].setYLink(plots[0,0]) for i in range(2,len(types)): for j in range(len(types)): plots[i,j].setYLink(plots[2,0]) plt = pg.plot() typs = amps.keys() bar = pg.BarGraphItem(x=np.arange(len(amps)), width=0.6, height=np.array(amps.values())) plt.addItem(bar) ax = plt.getAxis('bottom') ax.setTicks([[(i,"%s-%s"%amps.keys()[i]) for i in range(len(amps))]])
'bottom': ('baseline rms error', 'V') }) plt.addLegend() grid = PlotGrid() grid.set_shape(3, 1) grid.show() for r, c in ((1, 'r'), (2, 'g'), (3, 'b')): mask = rig == r rig_data = rms[mask] y, x = np.histogram(rig_data, bins=np.linspace(0, 0.002, 1000)) plt.plot(x, y / len(rig_data), stepMode=True, connect='finite', pen=c, name="Rig %d" % r) p = grid[r - 1, 0] p.plot(ts[mask], rig_data, pen=None, symbol='o', symbolPen=None, symbolBrush=(255, 255, 255, 100)) p.setLabels(left=('rig %d baseline rms noise' % r, 'V')) grid.setXLink(grid[0, 0]) grid.setYLink(grid[0, 0])
rms = np.array([row[0] for row in rows]) rig = np.array([row[1] for row in rows]).astype(int) hs = np.array([row[2] for row in rows]).astype(int) col = rig*8 + hs ts = np.array([time.mktime(row[3].timetuple()) for row in rows]) #ts -= ts[0] pg.plot(col + np.random.uniform(size=len(col))*0.7, rms, pen=None, symbol='o', symbolPen=None, symbolBrush=(255, 255, 255, 50)) plt = pg.plot(labels={'left': 'number of sweeps (normalized per rig)', 'bottom': ('baseline rms error', 'V')}) plt.addLegend() grid = PlotGrid() grid.set_shape(3, 1) grid.show() for r, c in ((1, 'r'), (2, 'g'), (3, 'b')): mask = rig==r rig_data = rms[mask] y, x = np.histogram(rig_data, bins=np.linspace(0, 0.002, 1000)) plt.plot(x, y/len(rig_data), stepMode=True, connect='finite', pen=c, name="Rig %d" % r) p = grid[r-1, 0] p.plot(ts[mask], rig_data, pen=None, symbol='o', symbolPen=None, symbolBrush=(255, 255, 255, 100)) p.setLabels(left=('rig %d baseline rms noise'%r, 'V')) grid.setXLink(grid[0, 0]) grid.setYLink(grid[0, 0])
class ExperimentTimeline(QtGui.QWidget): def __init__(self): QtGui.QWidget.__init__(self) self.channels = None self.layout = QtGui.QGridLayout() self.setLayout(self.layout) self.layout.setContentsMargins(0, 0, 0, 0) self.plots = PlotGrid() self.layout.addWidget(self.plots, 0, 0) self.ptree = pg.parametertree.ParameterTree(showHeader=False) self.layout.addWidget(self.ptree, 0, 1) self.ptree.setMaximumWidth(250) self.params = pg.parametertree.Parameter.create(name='params', type='group', addText='Add pipette') self.params.addNew = self.add_pipette_clicked # monkey! self.ptree.setParameters(self.params, showTop=False) def list_channels(self): return self.channels def get_channel_plot(self, chan): i = self.channels.index(chan) return self.plots[i, 0] def add_pipette_clicked(self): self.add_pipette(channel=self.channels[0], start=0, stop=500) def remove_pipettes(self): for ch in self.params.children(): self.params.removeChild(ch) ch.region.scene().removeItem(ch.region) def load_experiment(self, nwb_handle): self.nwb_handle = nwb_handle self.nwb = MiesNwb(nwb_handle.name()) # load all recordings recs = {} for srec in self.nwb.contents: for chan in srec.devices: recs.setdefault(chan, []).append(srec[chan]) chans = sorted(recs.keys()) self.channels = chans self.plots.set_shape(len(chans), 1) self.plots.setXLink(self.plots[0, 0]) # find time of first recording start_time = min([rec[0].start_time for rec in recs.values()]) self.start_time = start_time end_time = max([rec[-1].start_time for rec in recs.values()]) self.plots.setXRange(0, (end_time - start_time).seconds) # plot all recordings for i, chan in enumerate(chans): n_recs = len(recs[chan]) times = np.empty(n_recs) i_hold = np.empty(n_recs) v_hold = np.empty(n_recs) v_noise = np.empty(n_recs) i_noise = np.empty(n_recs) # load QC metrics for all recordings for j, rec in enumerate(recs[chan]): dt = (rec.start_time - start_time).seconds times[j] = dt v_hold[j] = rec.baseline_potential i_hold[j] = rec.baseline_current if rec.clamp_mode == 'vc': v_noise[j] = np.nan i_noise[j] = rec.baseline_rms_noise else: v_noise[j] = rec.baseline_rms_noise i_noise[j] = np.nan # scale all qc metrics to the range 0-1 pass_brush = pg.mkBrush(100, 100, 255, 200) fail_brush = pg.mkBrush(255, 0, 0, 200) v_hold = (v_hold + 60e-3) / 20e-3 i_hold = i_hold / 400e-12 v_noise = v_noise / 5e-3 i_noise = i_noise / 100e-12 plt = self.plots[i, 0] plt.setLabels(left=("Ch %d" % chan)) for data, symbol in [(np.zeros_like(times), 'o'), (v_hold, 't'), (i_hold, 'x'), (v_noise, 't1'), (i_noise, 'x')]: brushes = np.where(np.abs(data) > 1.0, fail_brush, pass_brush) plt.plot(times, data, pen=None, symbol=symbol, symbolPen=None, symbolBrush=brushes) # automatically select electrode regions self.remove_pipettes() site_info = self.nwb_handle.parent().info() for i in self.channels: hs_state = site_info.get('Headstage %d' % i, None) if hs_state is None: continue status = { 'NS': 'No seal', 'LS': 'Low seal', 'GS': 'GOhm seal', 'TF': 'Technical failure', }[hs_state] start = (recs[i][0].start_time - start_time).seconds - 1 stop = (recs[i][-1].start_time - start_time).seconds + 1 # assume if we got more than two recordings, then a cell was present. got_cell = len(recs[i]) > 2 self.add_pipette(i, start, stop, status=status, got_cell=got_cell) def add_pipette(self, channel, start, stop, status=None, got_cell=None): elec = PipetteParameter(self, channel, start, stop, status=status, got_cell=got_cell) self.params.addChild(elec, autoIncrementName=True) elec.child('channel').sigValueChanged.connect( self._pipette_channel_changed) elec.region.sigRegionChangeFinished.connect( self._pipette_region_changed) self._pipette_channel_changed(elec.child('channel')) def _pipette_channel_changed(self, param): plt = self.get_channel_plot(param.value()) plt.addItem(param.parent().region) self._rename_pipettes() def _pipette_region_changed(self): self._rename_pipettes() def _rename_pipettes(self): # sort electrodes by channel elecs = {} for elec in self.params.children(): elecs.setdefault(elec['channel'], []).append(elec) for chan in elecs: # sort all electrodes on this channel by start time chan_elecs = sorted(elecs[chan], key=lambda e: e.region.getRegion()[0]) # assign names for i, elec in enumerate(chan_elecs): # rename all first to avoid name colisions elec.setName('rename%d' % i) for i, elec in enumerate(chan_elecs): # If there are multiple electrodes on this channel, then # each extra electrode increments its name by the number of # headstages (for example, on AD channel 3, the first electrode # is called "Electrode 4", and on an 8-headstage system, the # second electrode will be "Electrode 12"). e_id = (chan + 1) + (i * n_headstages) elec.id = e_id elec.setName('Pipette %d' % e_id) def save(self): state = [] for elec in self.params.children(): rgn = elec.region.getRegion() start = self.start_time + datetime.timedelta(seconds=rgn[0]) stop = self.start_time + datetime.timedelta(seconds=rgn[1]) state.append({ 'id': elec.id, 'status': elec['status'], 'got_cell': elec['got cell'], 'channel': elec['channel'], 'start': start, 'stop': stop, }) return state
class ExperimentTimeline(QtGui.QWidget): def __init__(self): QtGui.QWidget.__init__(self) self.channels = None self.start_time = None # starting time according to NWB file self.layout = QtGui.QGridLayout() self.setLayout(self.layout) self.layout.setContentsMargins(0, 0, 0, 0) self.plots = PlotGrid() self.plots.set_shape(config.n_headstages, 1) self.plots.setXLink(self.plots[0, 0]) self.layout.addWidget(self.plots, 0, 0) self.ptree = pg.parametertree.ParameterTree(showHeader=False) self.layout.addWidget(self.ptree, 0, 1) self.ptree.setMaximumWidth(250) self.params = pg.parametertree.Parameter.create(name='params', type='group', addText='Add pipette') self.params.addNew = self.add_pipette_clicked # monkey! self.ptree.setParameters(self.params, showTop=False) def list_channels(self): return self.channels def get_channel_plot(self, chan): return self.plots[chan, 0] def add_pipette_clicked(self): self.add_pipette(channel=self.channels[0], start=0, stop=500) def remove_pipettes(self): for ch in self.params.children(): self.params.removeChild(ch) ch.region.scene().removeItem(ch.region) for i in range(self.plots.shape[0]): self.plots[i,0].clear() def load_site(self, site_dh): """Generate pipette list for this site """ self.remove_pipettes() # automatically fill pipette fluorophore field expt_dh = site_dh.parent().parent() expt_info = expt_dh.info() dye = expt_info.get('internal_dye', None) internal = expt_info.get('internal', None) # automatically select electrode regions self.channels = list(range(config.n_headstages)) site_info = site_dh.info() for i in self.channels: hs_state = site_info.get('Headstage %d'%(i+1), None) status = { 'NS': 'No seal', 'LS': 'Low seal', 'GS': 'GOhm seal', 'TF': 'Technical failure', 'NA': 'No attempt', None: 'Not recorded', }.get(hs_state, hs_state) self.add_pipette(i, status=status, internal_dye=dye, internal=internal) def load_nwb(self, nwb_handle): with pg.BusyCursor(): self._load_nwb(nwb_handle) def _load_nwb(self, nwb_handle): self.nwb_handle = nwb_handle self.nwb = MiesNwb(nwb_handle.name()) # load all recordings recs = {} for srec in self.nwb.contents: for chan in srec.devices: recs.setdefault(chan, []).append(srec[chan]) chans = sorted(recs.keys()) # find time of first recording start_time = min([rec[0].start_time for rec in recs.values()]) self.start_time = start_time end_time = max([rec[-1].start_time for rec in recs.values()]) self.plots.setXRange(0, (end_time-start_time).seconds) # plot all recordings for i,chan in enumerate(chans): n_recs = len(recs[chan]) times = np.empty(n_recs) i_hold = np.empty(n_recs) v_hold = np.empty(n_recs) v_noise = np.empty(n_recs) i_noise = np.empty(n_recs) # load QC metrics for all recordings for j,rec in enumerate(recs[chan]): dt = (rec.start_time - start_time).seconds times[j] = dt v_hold[j] = rec.baseline_potential i_hold[j] = rec.baseline_current if rec.clamp_mode == 'vc': v_noise[j] = np.nan i_noise[j] = rec.baseline_rms_noise else: v_noise[j] = rec.baseline_rms_noise i_noise[j] = np.nan # scale all qc metrics to the range 0-1 pass_brush = pg.mkBrush(100, 100, 255, 200) fail_brush = pg.mkBrush(255, 0, 0, 200) v_hold = (v_hold + 60e-3) / 20e-3 i_hold = i_hold / 400e-12 v_noise = v_noise / 5e-3 i_noise = i_noise / 100e-12 plt = self.get_channel_plot(chan) plt.setLabels(left=("Ch %d" % chan)) for data,symbol in [(np.zeros_like(times), 'o'), (v_hold, 't'), (i_hold, 'x'), (v_noise, 't1'), (i_noise, 'x')]: brushes = np.where(np.abs(data) > 1.0, fail_brush, pass_brush) plt.plot(times, data, pen=None, symbol=symbol, symbolPen=None, symbolBrush=brushes) for i in recs.keys(): start = (recs[i][0].start_time - start_time).seconds - 1 stop = (recs[i][-1].start_time - start_time).seconds + 1 pip_param = self.params.child('Pipette %d' % (i+1)) pip_param.set_time_range(start, stop) got_data = len(recs[i]) > 2 pip_param['got data'] = got_data def add_pipette(self, channel, status=None, **kwds): elec = PipetteParameter(self, channel, status=status, **kwds) self.params.addChild(elec, autoIncrementName=True) elec.child('channel').sigValueChanged.connect(self._pipette_channel_changed) elec.region.sigRegionChangeFinished.connect(self._pipette_region_changed) self._pipette_channel_changed(elec.child('channel')) def _pipette_channel_changed(self, param): plt = self.get_channel_plot(param.value()) plt.addItem(param.parent().region) self._rename_pipettes() def _pipette_region_changed(self): self._rename_pipettes() def _rename_pipettes(self): # sort electrodes by channel elecs = {} for elec in self.params.children(): elecs.setdefault(elec['channel'], []).append(elec) for chan in elecs: # sort all electrodes on this channel by start time chan_elecs = sorted(elecs[chan], key=lambda e: e.region.getRegion()[0]) # assign names for i,elec in enumerate(chan_elecs): # rename all first to avoid name colisions elec.setName('rename%d' % i) for i,elec in enumerate(chan_elecs): # If there are multiple electrodes on this channel, then # each extra electrode increments its name by the number of # headstages (for example, on AD channel 3, the first electrode # is called "Electrode 4", and on an 8-headstage system, the # second electrode will be "Electrode 12"). e_id = (chan+1) + (i*config.n_headstages) elec.id = e_id elec.setName('Pipette %d' % e_id) def save(self): state = {} for elec in self.params.children(): rgn = elec.region.getRegion() if self.start_time is None: start = None stop = None else: start = self.start_time + datetime.timedelta(seconds=rgn[0]) stop = self.start_time + datetime.timedelta(seconds=rgn[1]) state[elec.id] = OrderedDict([ ('pipette_status', elec['status']), ('got_data', elec['got data']), ('ad_channel', elec['channel']), ('patch_start', start), ('patch_stop', stop), ('cell_labels', {'biocytin': '', 'red': '', 'green': '', 'blue': ''}), #('cell_qc', {'holding': None, 'access': None, 'spiking': None}), ('target_layer', elec['target layer']), ('morphology', elec['morphology']), ('internal_solution', elec['internal']), ('internal_dye', elec['internal dye']), ('synapse_to', None), ('gap_to', None), ('notes', ''), ]) return state