def input_resistance(ext): """Estimate input resistance in MOhms, assuming all sweeps in passed extractor are hyperpolarizing responses.""" sweeps = ext.sweeps() if not sweeps: raise ft.FeatureError("no sweeps available for input resistance calculation") v_vals = [] i_vals = [] for sweep in sweeps: if sweep.i is None: raise ft.FeatureError("cannot calculate input resistance: i not defined for a sweep") v_peak, min_index = sweep.voltage_deflection('min') v_vals.append(v_peak) i_vals.append(sweep.i[min_index]) v = np.array(v_vals) i = np.array(i_vals) if len(v) == 1: # If there's just one sweep, we'll have to use its own baseline to estimate # the input resistance v = np.append(v, sweeps[0].sweep_feature("v_baseline")) i = np.append(i, 0.) A = np.vstack([i, np.ones_like(i)]).T m, c = np.linalg.lstsq(A, v)[0] return m * 1e3
def _analyze_long_squares_subthreshold(self): ext = self._long_squares_ext subthresh_sweeps = [sweep for sweep in ext.sweeps() if sweep.sweep_feature("avg_rate") == 0] subthresh_ext = EphysSweepSetFeatureExtractor.from_sweeps(subthresh_sweeps) self._subthreshold_long_squares_ext = subthresh_ext if len(subthresh_ext.sweeps()) == 0: raise ft.FeatureError("No subthreshold long square sweeps, cannot evaluate cell features.") peaks = subthresh_ext.sweep_features("peak_deflect") sags = subthresh_ext.sweep_features("sag") sag_eval_levels = np.array([sweep.voltage_deflection()[0] for sweep in subthresh_ext.sweeps()]) target_level = self.SAG_TARGET closest_index = np.argmin(np.abs(sag_eval_levels - target_level)) self._features["long_squares"]["sag"] = sags[closest_index] self._features["long_squares"]["vm_for_sag"] = sag_eval_levels[closest_index] self._features["long_squares"]["subthreshold_sweeps"] = subthresh_ext.sweeps() for s in self._features["long_squares"]["subthreshold_sweeps"]: s.set_stimulus_amplitude_calculator(_step_stim_amp) logging.debug("subthresh_sweeps: %d", len(subthresh_sweeps)) calc_subthresh_sweeps = [sweep for sweep in subthresh_sweeps if sweep.sweep_feature("stim_amp") < self.SUBTHRESH_MAX_AMP and sweep.sweep_feature("stim_amp") > self._subthresh_min_amp] logging.debug("calc_subthresh_sweeps: %d", len(calc_subthresh_sweeps)) calc_subthresh_ext = EphysSweepSetFeatureExtractor.from_sweeps(calc_subthresh_sweeps) self._subthreshold_membrane_property_ext = calc_subthresh_ext self._features["long_squares"]["subthreshold_membrane_property_sweeps"] = calc_subthresh_ext.sweeps() self._features["long_squares"]["input_resistance"] = input_resistance(calc_subthresh_ext) self._features["long_squares"]["tau"] = membrane_time_constant(calc_subthresh_ext) self._features["long_squares"]["v_baseline"] = np.nanmean(ext.sweep_features("v_baseline"))
def _analyze_long_squares_spiking(self, force_reprocess=False): if not force_reprocess and self._spiking_long_squares_ext: return ext = self._long_squares_ext ext.process_spikes() self._features["long_squares"]["sweeps"] = ext.sweeps() for s in self._features["long_squares"]["sweeps"]: s.set_stimulus_amplitude_calculator(_step_stim_amp) spiking_indexes = np.flatnonzero(ext.sweep_features("avg_rate")) if len(spiking_indexes) == 0: raise ft.FeatureError("No spiking long square sweeps, cannot compute cell features.") amps = ext.sweep_features("stim_amp")#self.long_squares_stim_amps() min_index = np.argmin(amps[spiking_indexes]) rheobase_index = spiking_indexes[min_index] rheobase_i = _step_stim_amp(ext.sweeps()[rheobase_index]) self._features["long_squares"]["rheobase_extractor_index"] = rheobase_index self._features["long_squares"]["rheobase_i"] = rheobase_i self._features["long_squares"]["rheobase_sweep"] = ext.sweeps()[rheobase_index] spiking_sweeps = [sweep for sweep in ext.sweeps() if sweep.sweep_feature("avg_rate") > 0] self._spiking_long_squares_ext = EphysSweepSetFeatureExtractor.from_sweeps(spiking_sweeps) self._features["long_squares"]["spiking_sweeps"] = self._spiking_long_squares_ext.sweeps() self._features["long_squares"]["fi_fit_slope"] = fit_fi_slope(self._spiking_long_squares_ext)
def _analyze_short_squares(self): ext = self._short_squares_ext ext.process_spikes() # Need to count how many had spikes at each amplitude; find most; ties go to lower amplitude spiking_sweeps = [sweep for sweep in ext.sweeps() if sweep.sweep_feature("avg_rate") > 0] if len(spiking_sweeps) == 0: raise ft.FeatureError("No spiking short square sweeps, cannot compute cell features.") most_common = Counter(map(_short_step_stim_amp, spiking_sweeps)).most_common() common_amp, common_count = most_common[0] for c in most_common[1:]: if c[1] < common_count: break if c[0] < common_amp: common_amp = c[0] self._features["short_squares"]["stimulus_amplitude"] = common_amp ext = EphysSweepSetFeatureExtractor.from_sweeps([sweep for sweep in spiking_sweeps if _short_step_stim_amp(sweep) == common_amp]) self._short_squares_ext = ext self._features["short_squares"]["common_amp_sweeps"] = ext.sweeps() for s in self._features["short_squares"]["common_amp_sweeps"]: s.set_stimulus_amplitude_calculator(_short_step_stim_amp)
def estimate_time_constant_at_end(self): """Calculate the membrane time constant by fitting the voltage response with a single expontial at the end of a hyperpolarising stimulus. Returns ------- tau : membrane time constant in seconds """ # Assumes this is being done on a hyperpolarizing step v_peak, peak_index = self.voltage_deflection("min") v_baseline = self.sweep_feature("v_baseline") if self.end: start_index = ft.find_time_index(self.t, self.end) else: start_index = ft.find_time_index(self.t, 0.7) frac = 0.1 search_result = np.flatnonzero(self.v[start_index:] >= frac * (v_baseline - v_peak) + v_peak) if not search_result.size: raise ft.FeatureError("Could not find interval for time constant estimate") fit_start = self.t[search_result[0] + start_index] fit_end = self.t[-1] b, inv_tau, A = ft.fit_membrane_time_constant_at_end(self.v, self.t, fit_start, fit_end) return 1. / inv_tau
def cell_extractor_for_nwb(dataset, ramps, short_squares, long_squares): """Initialize EphysCellFeatureExtractor object from NWB data set Parameters ---------- dataset : NwbDataSet ramps : list of sweep numbers of ramp sweeps short_squares : list of sweep numbers of short square sweeps long_squares : list of sweep numbers of long square sweeps """ if len(short_squares) == 0: raise ft.FeatureError("no short square sweep numbers provided") if len(ramps) == 0: raise ft.FeatureError("no ramp sweep numbers provided") if len(long_squares) == 0: raise ft.FeatureError("no long_square sweep numbers provided") ramps_ext = extractor_for_nwb_sweeps(dataset, ramps, fixed_start=RAMPS_START) temp_short_sq_ext = extractor_for_nwb_sweeps(dataset, short_squares) t_set = [s.t for s in temp_short_sq_ext.sweeps()] v_set = [s.v for s in temp_short_sq_ext.sweeps()] cutoff, thresh_frac = ft.estimate_adjusted_detection_parameters( v_set, t_set, SHORT_SQUARES_WINDOW_START, SHORT_SQUARES_WINDOW_END) thresh_frac = max(thresh_frac, 0.1) short_squares_ext = extractor_for_nwb_sweeps(dataset, short_squares, dv_cutoff=cutoff, thresh_frac=thresh_frac) long_squares_ext = extractor_for_nwb_sweeps(dataset, long_squares, fixed_start=LONG_SQUARES_START, fixed_end=LONG_SQUARES_END) return EphysCellFeatureExtractor(ramps_ext, short_squares_ext, long_squares_ext)
def fit_fi_slope(ext): """Fit the rate and stimulus amplitude to a line and return the slope of the fit.""" if len(ext.sweeps()) < 2: raise ft.FeatureError("Cannot fit f-I curve slope with less than two suprathreshold sweeps") x = np.array(map(_step_stim_amp, ext.sweeps())) y = ext.sweep_features("avg_rate") A = np.vstack([x, np.ones_like(x)]).T m, c = np.linalg.lstsq(A, y)[0] return m
def estimate_time_constant(self): """Calculate the membrane time constant by fitting the voltage response with a single exponential. Returns ------- tau : membrane time constant in seconds """ # Assumes this is being done on a hyperpolarizing step v_peak, peak_index = self.voltage_deflection("min") v_baseline = self.sweep_feature("v_baseline") if self.start: start_index = ft.find_time_index(self.t, self.start) else: start_index = 0 frac = 0.1 search_result = np.flatnonzero(self.v[start_index:] <= frac * (v_peak - v_baseline) + v_baseline) if not search_result.size: raise ft.FeatureError("could not find interval for time constant estimate") fit_start = self.t[search_result[0] + start_index] fit_end = self.t[peak_index] # There was one cell with a noisy (?) peak downwards (to -250 mV) unfortunately. That's why we have the if-statement here. # You can delete this if-statement if you have normal traces. # If this all still didn't work as expected, then hopefully there are more hyperpolarisation traces for which tau can be estimated if (self.v[peak_index] < -200) : print("A DOWNWARD PEAK WAS OBSERVED GOING TO LESS THAN 200 MV!!!") # Look for another local minimum closer to stimulus onset # We look for a couple of milliseconds after stimulus onset to 50 ms before the downward peak end_index = (start_index + 50) + np.argmin(self.v[start_index + 50 : peak_index - 1250]) fit_end = self.t[end_index] fit_start = self.t[start_index + 50] a, inv_tau, y0 = ft.fit_membrane_time_constant(self.v, self.t, fit_start, fit_end) return 1. / inv_tau