def browse(self, row = 3, col = 3): ''' Plot the raw traces grouped close to each other based on the groups of cells parameters: row (int) - number of rows of subplots col (int) - number of columns of subplots return: f (list) - list of image windows ''' f = [] for t in np.unique(self.data['group']): cell_ind = self.data.index[np.nonzero(self.data['group'] == t)] ft = [] counter = 0 for c in cell_ind: for trial in self.trial_data[c, :, :]: trace, sr, stim = util.load_wave(self.folder + \ util.gen_name(self.data['No'][c], trial[0])) if not len(trace): continue sub_num = counter - row * col * (len(ft) - 1) if row * col <= sub_num: ft.append(pg.GraphicsWindow(title = \ 'Image {:d}'.format(len(ft) + 1) + ' in ' + t)) sub_num -= row * col axis = ft[-1].addPlot(int(sub_num / col), np.mod(sub_num, col)) axis.setTitle('Cell {0:d}, rate = {1:.0f} Hz, I = {2:.0f} pA'.format(\ self.data['No'][c], trial[1], trial[2] * 1e12)) plot.plot_trace_v(trace, sr, ax = axis) counter += 1 f.extend(ft) return f
def aveplot(intensity_dir, group_size, folder, cells, intensities, trial, start, freq, num, \ win = None, medfilt = True, smooth = False, base_win = 0.3, col = 3, row = 3): ''' f = aveplot(intensity_dir, group_size, folder, cells, intensities, trial, start, freq, \ num, medfilt = True, smooth = False, base_win = 0.3): Plot the averaged responses across several trials with stimulus of certain intensity for different cells at different intensity levels. parameters: intensity_dir (string) - intensity data file folder (string) - dirctory to the data folder cells (array_like) - indices of cells intensities (array_like) - intensities of the trials to be plotted for each cell trial (array_like) - number of trials in each intensity group to be plotted start (float) - time of stimulation start freq (float) - frequency of stimulation num (int) - number of stimuli medfilt (float) - median filter threshold smooth (boolean) - whether to smooth the traces with default parameters base_win (float) - size of baseline window, right before the first stimulation used for baseline alignment col (int) - number of columns of subplots row (int) - number of rows of subplots return: f (GraphicsWindow) - window object of pyqtgraph package ''' f = [] count = 0 pg.setConfigOption('background', 'w') pg.setConfigOption('foreground', 'k') for i, cell in enumerate(cells): for intensity in intensities[i]: file_dirs = util.intensity2file(folder, [cell], [intensity], trial, \ intensity_dir, group_size) traces = [] sr = 0 if len(file_dirs) == 0: continue for fd in file_dirs: t, sr, stim = util.load_wave(fd) if base_win: baseline = np.mean(t[int(start - base_win):int(start)]) t = t - baseline if smooth: t = process.smooth(t, sr, 600) if medfilt: t = process.thmedfilt(t, 5, 30e-12) traces.append(t) ave_trace = np.mean(traces, 0) #print(np.mean(ave_trace)) sub_num = count - row * col * (len(f) - 1) if row * col <= sub_num: f.append(pg.GraphicsWindow(title = \ 'Group {0:d}'.format(len(f) + 1))) sub_num -= row * col axis = f[-1].addPlot(int(sub_num / col), np.mod(sub_num, col)) axis.setTitle('Cell {0:d}, Int {1:.2f}'.format(cell, intensity)) plot.plot_trace_v(ave_trace, sr, ax = axis, win = win, \ stim = [start + d / freq for d in range(num)]) count += 1 return f
def align_ahp(self, spikes = [0], max_trace = 30, param_file = 'param_spike_detect'): ''' Align and plot specified action potentials from chosen trials in the same graphs. parameters: spikes (list) - indices of spikes to plot, 0 is the first max_trace (int) - maximum number of traces in each graph, not to be too crowded param_file (String) - directory of spike detection parameter file return: f (list) - list of image windows ''' # Traverse all the trials, find the spikes and store the trace, time range # of the spikes and the type of the cell which the trace belong to in # separated lists sequentially traces = [[] for d in range(len(spikes))] limits = [[] for d in range(len(spikes))] cell_types = [[] for d in range(len(spikes))] spike_params = ap.get_params(param_file) for c in self.data.index: for trial in self.trial_data[c, :, :]: trace, sr, stim = util.load_wave(self.folder + \ util.gen_name(self.data['No'][c], trial[0])) if sr > 0: starts = ap.spike_detect(trace, sr, spike_params) # start of all spikes for i, spike in enumerate(spikes): if spike < len(starts) - 1: traces[i].append(trace) cell_types[i].append(self.data['group'][c]) limits[i].append([starts[spike], starts[spike + 1]]) f = [] for i, spike in enumerate(spikes): types = np.sort(np.unique(cell_types[i])) image_num = int(np.ceil(len(cell_types[i]) / max_trace)) # number of images fs = [pg.GraphicsWindow(title = 'Image {0:d} in spike {1:d}'.format(d, spike)) \ for d in range(image_num)] ax = [d.addPlot(0, 0) for d in fs] cl = [pg.intColor(i, hues = len(types)) for i in range(len(types))] # colors # Add legend to the plotItems lgit = [pg.PlotDataItem(pen = cl[d]) for d in range(len(cl))] for ax_ in ax: lg = ax_.addLegend() for t, l in zip(types, lgit): lg.addItem(l, t) group_max = [] # maximum number of traces in one image for each groups for t in types: group_max.append(np.ceil(np.count_nonzero(np.array(cell_types[i]) == t) \ / image_num)) group_trial = [0 for d in range(len(types))] # keep track of trials # plotted in each groups for j in range(len(cell_types[i])): _group = np.nonzero(types == cell_types[i][j])[0][0] plot.plot_trace_v(traces[i][j], 1, win = limits[i][j], ax = \ ax[int(np.floor(group_trial[_group] / group_max[_group]))], \ shift = [-limits[i][j][0], -traces[i][j][limits[i][j][0]]], \ cl = cl[_group]) group_trial[_group] += 1 f.extend(fs) return f
def spike_detect(trace, sr, params, begin=0, end=-1, plotting=False): ''' start_ind = spike_detect(trace, sr, params, begin = 0, end = -1, plotting = False) Detect action potential spikes and return the spike rising time points. Find start time of spikes by finding point with slope over slope_th followed by a peak of relative amplitude above peak_th. The peak is defined as the first point reversing slope after the start point. parameters: trace (array_like) - voltage trace sr (float) - sampling rate params (dictionary) - parameters begin (float) - begin of the time window to be analyzed end (float) - end of the time window to be analyzed, it represents the end of the trace when it's -1 plotting (boolean) - whether to plot the trace with starting point marked for inspection return: start_ind (array_like) - indices of spike starting points ''' slope_th = params['spike_slope_threshold'] peak_th = params['spike_peak_threshold'] width_th = params['half_width_threshold'] sign = params['sign'] if sign < 0: trace = trace * sign trace_diff = np.diff(trace) * sr pstart = np.nonzero(trace_diff > slope_th)[0] # possible start points reverse = np.nonzero(trace_diff < 0)[0] # possible peak points start_ind = [] #print('start len = {:f}'.format(len(pstart))) #print('reverse len = {:f}'.format(len(reverse))) i = 0 # index in pstart j = 0 # index in reverse if end == -1: end = len(trace) / sr while i < len(pstart) and j < len(reverse) and pstart[i] < sr * end: if pstart[i] < sr * begin: i += 1 elif pstart[i] < reverse[j]: #print('b: {0:f}, p: {1:f}'.format(trace[pstart[i]], trace[reverse[j]])) if peak_th < trace[reverse[j]] - trace[pstart[i]] and \ reverse[j] - pstart[i] < width_th * sr: start_ind.append(pstart[i]) while i < len(pstart) and pstart[i] < reverse[j]: i += 1 else: i += 1 else: j += 1 # plot trace with spike start points marked if needed #print(start_ind) start_ind = np.array(start_ind) # transform to numpy array if plotting: w = plot.plot_trace_v(trace, sr, points=start_ind / sr) return start_ind, w else: return start_ind
def FIcompare(folder, cells, currents = [], freqs = [],\ firing_rate_data = 'firing_rate_data.txt'): ''' f = FIcompare(folder, cells, currents = [], freqs = [],\ firing_rate_data = 'firing_rate_data.txt'): Plot current clamp firing traces with certain currents input and with firing frequencies in a certain range. parameters: folder (string) - directory to the folder with raw data cells (array_like) - indices of neurons to plot currents (array_like) - list of input currents freqs (list) - of two scalars, range of the firing rates to be included firing_rate_data (string) - firing rate data file directory return: f (list) - list of figure windows ''' data = util.read_dict(firing_rate_data, 'int') f = [] for cell in cells: for trial, stim, fr in zip(*data[cell][1]): if (len(currents) == 0 or stim in currents) and \ (len(freqs) == 0 or (freqs[0] <= fr and fr < freqs[1])): trace, sr, st = util.load_wave(folder + util.gen_name(cell, trial)) f.append(plot.plot_trace_v(trace, sr)) f[-1].setWindowTitle('Cell {0:d}, Trial {1:d}, I = {2:.2e}'.\ format(cell, trial, st[2])) return f
def align(self, baseline_win = 0.2, max_trace = 30): ''' Plot the chosen trials in the same graphs, aligned to baseline and colored based on groups parameters: baseline_win (float) - baseline window size before the start of the stimulation max_trace (int) - maximum number of traces in each graph retrun: f (list) - list of image windows ''' types = np.sort(np.unique(self.data['group'])) trial_num = np.count_nonzero(self.trial_data[:, :, 0]) # trial_num = self.trial_data.shape[0] * self.trial_data.shape[1] image_num = int(np.ceil(trial_num / max_trace)) # number of images f = [pg.GraphicsWindow(title = 'Image {0:d}'.format(d)) for d in range(image_num)] ax = [d.addPlot(0, 0) for d in f] cl = [pg.intColor(i, hues = len(types)) for i in range(len(types))] # colors # Add legend to the plotItems lgit = [pg.PlotDataItem(pen = cl[d]) for d in range(len(cl))] for ax_ in ax: lg = ax_.addLegend() for t, l in zip(types, lgit): lg.addItem(l, t) counter = 0 for c in self.data.index: for t in self.trial_data[c]: if t[0] == 0: break trace, sr, stim = util.load_wave(self.folder + \ util.gen_name(self.data['No'][c], t[0])) if sr > 0: _group = np.nonzero(types == self.data['group'][c])[0][0] plot.plot_trace_v(trace, sr, ax = \ ax[int(np.floor(counter / max_trace))], shift = \ [0, -np.mean(trace[int(stim[0] - baseline_win):int(stim[0])])], \ cl = cl[_group]) counter += 1 return f
def analyze(self, verbose=0): ''' Detect the spikes of the minis and analyze them Criterions: 1. Rise time short enough 2. Amplitude large enough 3. Decay fit exponential curve with low enough residual 4. Decay time constant big enough ''' # miniEPSC parameters after analysis self.miniRises = [] # valid minis' rise times self.miniPeaks = [] # valid mini's peak index self.miniAmps = [] # valid mini's peak amplitude self.miniDecayTaus = [] # valid mini's decay time constant x = self.x[int(self.sr * self.params['start']): \ int(self.sr * self.params['end'])] * self.params['sign'] # rig defect related single point noise x = self.thmedfilt(x, self.params['medianFilterWinSize'], \ self.params['medianFilterThresh']) # scale x = x * self.scale # remove linear shifting baseline p = np.polyfit(np.arange(len(x)), x, 1) x = (x - np.polyval(p, np.arange(len(x)))) # low pass filter fx = self.smooth(x, self.sr, self.params['lowBandWidth']) dfx = np.diff(fx) * self.sr peaks = (0 < dfx[0:-1]) & (dfx[1:] < 0) troughs = (dfx[0:-1] < 0) & (0 < dfx[1:]) # points with local maximum slope, which is also larger than threshold rises = (dfx[0:-1] < self.params["riseSlope"]) & \ (self.params["riseSlope"] < dfx[1:]) ''' rises = np.zeros(peaks.shape) rises = (dfx[0:-2] < dfx[1:-1]) & (dfx[2:] < dfx[1:-1]) & \ (self.params['riseSlope'] < dfx[1:-1]) ''' # indices of either rises or peaks ptrInds = np.concatenate((np.nonzero(peaks | rises | troughs)[0], \ [int(self.params['end'] * self.sr)]), axis = None) lastRise = -self.params["riseTime"] * self.sr # last rise point index last2Rise = 0 # the rise point index before last rise point baseline = 0 # current baseline level peakStack = [] # peaks stacked to close to each other for i in range(len(ptrInds) - 1): if peaks[ptrInds[i]]: if ptrInds[i] - lastRise < self.params['riseTime'] * self.sr or \ len(peakStack): if (len(peakStack) and ptrInds[i + 1] - peakStack[0] \ < self.params["stackWin"] * self.sr): peakStack.append(ptrInds[i]) else: if last2Rise < lastRise - \ int(self.params['baseLineWin'] * self.sr): baseline = np.mean(x[lastRise - \ int(self.params['baseLineWin'] * self.sr):\ lastRise]) amp = fx[ptrInds[i]] - baseline if self.params['minAmp'] < amp or len(peakStack): if not len(peakStack) and ptrInds[i + 1] - ptrInds[i] < \ self.params["stackWin"] * self.sr and \ i + 3 < len(ptrInds) and not rises[ptrInds[i + 2]]: peakStack.append(ptrInds[i]) else: if len(peakStack): amp = np.max(fx[peakStack] - baseline) peakStack = [] sample = x[lastRise:ptrInds[i + 1]] # exponential function to fit the decay fun = lambda x, t1, t2, a, b, c: \ a * np.exp(-x / t1) - b * np.exp(-x / t2) + c # initial parameter values p0 = [self.params["offTauIni"], self.params["onTauIni"], \ fx[lastRise] + amp - baseline, amp, baseline] # boundaries bounds = ([-np.inf, -np.inf, 0, 0, -np.inf], \ [np.inf, np.inf, np.inf, np.inf, np.inf]) try: popt, pcov = curve_fit(fun, np.arange(len(sample)), \ sample, p0, bounds = bounds, \ loss = "linear", max_nfev = 1e3 * len(sample)) tau_rise = popt[1] / self.sr tau_decay = popt[0] / self.sr res = np.sqrt(np.sum((fun(np.arange(len(sample)), \ *popt) - sample) ** 2)) if verbose > 1: print("popt: ", popt) print("tau rise: ", tau_rise, "tau decay: ", \ tau_decay, "res: ", res, "time:", \ lastRise / self.sr) fm = pg.GraphicsWindow() ax = fm.addPlot(0, 0) plot.plot_trace_v(x[lastRise:ptrInds[i + 1]], \ self.sr, ax = ax) plot.plot_trace_v(fx[lastRise:ptrInds[i + 1]], \ self.sr, ax = ax, cl = 'g') plot.plot_trace_v(\ fun(np.arange(len(sample)), *popt), \ self.sr, ax = ax, cl = 'r') print("Continue (c) or step ([s])") if input() == 'c': verbose = 1 if self.params['minTau'] < tau_decay \ and res < self.params['residual']: ''' self.miniPeaks.append(self.params['start'] + \ ptrInds[i] / self.sr) self.miniRises.append(self.params["start"] + \ lastRise / self.sr) ''' self.miniPeaks.append(ptrInds[i] / self.sr) self.miniRises.append(lastRise / self.sr) self.miniAmps.append(amp / self.scale) self.miniDecayTaus.append(tau_decay) except RuntimeError as e: print("Fit Error") print(e) except ValueError as e: print("Initialization Error") print(e) elif rises[ptrInds[i]]: last2Rise = lastRise lastRise = ptrInds[i] if verbose > 0: fs = pg.GraphicsWindow() ax = [fs.addPlot(i, 0) for i in range(3)] plot.plot_trace_v(x, self.sr, ax=ax[0]) plot.plot_trace_v(fx, self.sr, ax=ax[0], cl='g') plot.plot_trace_v(fx, self.sr, ax = ax[1], pcl = 'r', \ points = np.nonzero(rises)[0] / self.sr) plot.plot_trace_v(fx, self.sr, ax = ax[1], pcl = None, \ points = np.nonzero(peaks)[0] / self.sr) plot.plot_trace_v(fx, self.sr, points = self.miniRises, \ ax = ax[1], pcl = 'b') ax[0].setXLink(ax[1]) ax[0].setYLink(ax[1]) plot.plot_trace_v(dfx, self.sr, ax=ax[2]) return fs
def browse(folder, cell_num, trial_num = None, row = 3, col = 3, medfilt = True): ''' f = browse(folder, cell_num, trial_num = None, row = 3, col = 3, medfilt = True): Plot data traces of a cell in figures with multiple subplots to quicky review the traces parameters: folder (string) - directory to the folder with the data files cell_num (int) - cell index trial_num (int or list) - trial index or indices, include all the trials if it's None row (int) - number of rows of subplots col (int) - number of cols of subplots medfilt (boolean) - whether to apply median filter with a default threshold return: f (list) - list of window objects of pyqtgraph package ''' if(folder[-1] is not os.sep): folder = folder + os.sep if type(trial_num) is int: file_dir = folder + 'Cell_{0:04d}_{1:04d}.ibw'.format(cell_num, trial_num) print(file_dir) trace, sr, stim = util.load_wave(file_dir) if not len(trace): return if medfilt: trace = process.thmedfilt(trace, 5, 40e-12) f = plot.plot_trace_v(trace, sr) if stim[2] != 0: f.getItem(0, 0).setTitle('Trial {0:d}, I = {1:.2e}'.format(trial_num, stim[2])) #f.save_fig('tmp.png', dpi = 96) elif type(trial_num) is list: f = [] for i in range(len(trial_num)): file_dir = folder + 'Cell_{0:04d}_{1:04d}.ibw'.format(cell_num, trial_num[i]) print(file_dir) trace, sr, stim = util.load_wave(file_dir) if not len(trace): continue if medfilt: trace = process.thmedfilt(trace, 5, 40e-12) sub_num = i - row * col * (len(f) - 1) if row * col <= sub_num: f.append(pg.GraphicsWindow(title = \ 'Cell {0:d}, Group {1:d}'.format(cell_num, len(f) + 1))) sub_num -= row * col #axis = f[-1].addPlot(int(sub_num / row), np.mod(sub_num, col), \ axis = f[-1].addPlot(int(sub_num / col), np.mod(sub_num, col)) if stim[2] != 0: axis.setTitle('Trial {0:d}, I = {1:.2e}'.format(trial_num[i], stim[2])) else: axis.setTitle('Trial {0:d}'.format(trial_num[i])) plot.plot_trace_v(trace, sr, ax = axis) else: f = [] file_pre = 'Cell_{0:04d}_'.format(cell_num) print(file_pre) data_files = os.listdir(folder) count = 0 for data_file in data_files: matched = re.match(file_pre + '0*([1-9][0-9]*).ibw', data_file) if matched: trial_num = int(matched.group(1)) sub_num = count - row * col * (len(f) - 1) if row * col <= sub_num: f.append(pg.GraphicsWindow(title = 'Cell {0:d}, Group {1:d}'.format(cell_num, len(f) + 1))) sub_num -= row * col axis = f[-1].addPlot(int(sub_num / col), np.mod(sub_num, col)) file_dir = folder + data_file trace, sr, stim = util.load_wave(file_dir) print(file_dir + ' {:f}'.format(len(trace) / sr)) if stim[2] != 0: axis.setTitle('Trial {0:d}, I = {1:.2e}'.format(trial_num, stim[2])) else: axis.setTitle('Trial {0:d}'.format(trial_num)) if medfilt: trace = process.thmedfilt(trace, 5, 40e-12) plot.plot_trace_v(trace, sr, ax = axis) count += 1 return f