def stf_fit(p0, lsfunc): data = stf.get_trace()[stf.get_fit_start():stf.get_fit_end()] dt = stf.get_sampling_interval() x = np.arange(0, len(data) * dt, dt) plsq = leastsq(leastsq_stf, p0, args=(data, lsfunc, x)) return plsq[0]
def crop(): si = stf.get_sampling_interval() start = stf.get_fit_start() * si end = stf.get_fit_end() * si spells.cut_sweeps(start, end - start) return
def stf_fit( p0, lsfunc ): data = stf.get_trace()[ stf.get_fit_start() : stf.get_fit_end() ] dt = stf.get_sampling_interval() x = np.arange(0, len(data)*dt, dt) plsq = leastsq(leastsq_stf, p0, args=(data, lsfunc, x)) return plsq[0]
def monoexpfit(optimization=True, Tn=20): """ Fits monoexponential function with offset to data between the fit cursors in the current trace of the active channel using a Chebyshev-Levenberg- Marquardt hybrid algorithm. Optimization requires Scipy. Setting optimization to False forces this function to use just the Chebyshev algorithm. The maximum order of the Chebyshev polynomials can be set using Tn. """ # Get data fit_start = stf.get_fit_start() fit_end = stf.get_fit_end() y = np.double(stf.get_trace()[fit_start:fit_end]) si = stf.get_sampling_interval() l = len(y) t = si * np.arange(0, l, 1, np.double) # Define monoexponential function def f(t, *p): return p[0] + p[1] * np.exp(-t / p[2]) # Get initial values from Chebyshev transform fit init = chebexp(1, Tn) p0 = (init.get('Offset'), ) p0 += (init.get('Amp_0'), ) p0 += (init.get('Tau_0'), ) # Optimize (if applicable) if optimization == True: # Optimize fit using Levenberg-Marquardt algorithm options = {"ftol": 2.22e-16, "xtol": 2.22e-16, "gtol": 2.22e-16} [p, pcov] = optimize.curve_fit(f, t, y, p0, **options) elif optimization == False: p = list(p0) fit = f(t, *p) # Calculate SSE SSE = np.sum((y - fit)**2) # Plot fit in a new window matrix = np.zeros((2, stf.get_size_trace())) * np.nan matrix[0, :] = stf.get_trace() matrix[1, fit_start:fit_end] = fit stf.new_window_matrix(matrix) # Create table of results retval = [("p0_Offset", p[0])] retval += [("p1_Amp_0", p[1])] retval += [("p2_Tau_0", p[2])] retval += [("SSE", SSE)] retval += [("dSSE", 1.0 - np.sum((y - f(t, *p0))**2) / SSE)] retval += [("Time fit begins", fit_start * si)] retval += [("Time fit ends", fit_end * si)] retval = dict(retval) stf.show_table( retval, "monoexpfit, Section #%i" % float(stf.get_trace_index() + 1)) return
def batch_integration(): """ Perform batch integration between the decay/fit cursors of all traces in the active window """ n = int(stf.get_fit_end() + 1 - stf.get_fit_start()) x = [i * stf.get_sampling_interval() for i in range(n)] dictlist = [] for i in range(stf.get_size_channel()): stf.set_trace(i) y = stf.get_trace()[int(stf.get_fit_start()):int(stf.get_fit_end() + 1)] auc = np.trapz(y - stf.get_base(), x) dictlist += [("%i" % (i + 1), auc)] retval = dict(dictlist) stf.show_table(retval, "Area Under Curve") stf.set_trace(0) return
def yvalue(origin, interval): stf.set_fit_start(origin, True) stf.set_fit_end(origin + interval, True) stf.measure() x = int(stf.get_fit_end(False)) y = [] for i in range(stf.get_size_channel()): stf.set_trace(i) y.append(stf.get_trace(i)[x]) return y
def blankstim(): """ Blank values between fit cursors in all traces in the active channel. Typically used to blank stimulus artifacts. """ fit_start = stf.get_fit_start() fit_end = stf.get_fit_end() blanked_traces = [] for i in range(stf.get_size_channel()): tmp = stf.get_trace(i) tmp[fit_start:fit_end] = np.nan blanked_traces.append(tmp) stf.new_window_list(blanked_traces) return
def interpstim(): """ Interpolate values between fit cursors in all traces in the active channel. Typically used to remove stimulus artifacts. """ x = np.array( [i * stf.get_sampling_interval() for i in range(stf.get_size_trace())]) fit_start = int(stf.get_fit_start()) fit_end = int(stf.get_fit_end()) interp_traces = [] for i in range(stf.get_size_channel()): tmp = stf.get_trace(i) tmp[fit_start:fit_end] = np.interp(x[fit_start:fit_end], [x[fit_start], x[fit_end]], [tmp[fit_start], tmp[fit_end]]) interp_traces.append(tmp) stf.new_window_list(interp_traces) return
def chebexp(n, Tn=20): """ Fits sums of exponentials with offset to the current trace in the active channel using the Chebyshev tranform algorithm. The maximum order of the Chebyshev polynomials can be set using Tn. Reference: Malachowski, Clegg and Redford (2007) J Microsc 228(3): 282-95 """ # Get data trace between fit/decay cursors y = stf.get_trace()[stf.get_fit_start():stf.get_fit_end()].astype( np.double) si = np.double(stf.get_sampling_interval()) l = len(y) N = np.double(l - 1) # Calculate time dimension with unit 1 t = np.arange(0, l, 1, np.double) # Check the maximum order Chebyshev polynomials to generate if l < Tn: raise ValueError('Tn exceeds the number of data points') # Generate the polynomials T and coefficients d T0 = np.ones((l), np.double) R0 = np.sum(T0**2) d0 = np.sum((T0 * y) / R0) T = np.zeros((l, Tn), np.double) T[:, 0] = 1 - 2 * t / N T[:, 1] = 1 - 6 * t / (N - 1) + 6 * t**2 / (N * (N - 1)) R = np.zeros((Tn), np.double) d = np.zeros((Tn), np.double) for j in range(Tn): if j > 1: A = (j + 1) * (N - j) B = 2 * (j + 1) - 1 C = j * (N + j + 1) T[:, j] = (B * (N - 2 * t) * T[:, j - 1] - C * T[:, j - 2]) / A R[j] = np.sum(T[:, j]**2) d[j] = np.sum(T[:, j] * y / R[j]) # Generate additional coefficients dn that describe the relationship # between the Chebyshev coefficients d and the constant k, which is # directly related to the exponent time constant dn = np.zeros((n, Tn), np.double) for i in range(1, n + 1): for j in range(1 + i, Tn - i + 1): if i > 1: dn[i - 1, j - 1] = (((N + j + 2) * dn[i - 2, j] / (2 * j + 3)) - dn[i - 2, j - 1] - ((N - j + 1) * dn[i - 2, j - 2] / (2 * j - 1))) / 2 else: dn[i - 1, j - 1] = (((N + j + 2) * d[j] / (2 * j + 3)) - d[j - 1] - ((N - j + 1) * d[j - 2] / (2 * j - 1))) / 2 for i in range(n): dn[i, :] = dn[i, :] * np.double(np.all(dn, 0)) # Form the regression model to find the time constants of each exponent Mn = np.zeros((n, n), np.double) b = np.zeros(n, np.double) for i in range(n): b[i] = np.sum(d * dn[i, :]) for m in range(n): Mn[i, m] = -np.sum(dn[i, :] * dn[m, :]) # Solve the linear problem try: x = np.linalg.solve(Mn, b) except: x = np.linalg.lstsq(Mn, b)[0] k = np.roots(np.hstack((1, x))) if any(k != np.real(k)): raise ValueError("Result is not a sum of %d real exponents" % n) tau = -1 / np.log(1 + k) # Generate the Chebyshev coefficients df for each exponent df0 = np.zeros(n, np.double) df = np.zeros((n, Tn), np.double) for i in range(n): for j in range(Tn): df[i, j] = np.sum(np.exp(-t / tau[i]) * T[:, j] / R[j]) df0[i] = np.sum(np.exp(-t / tau[i]) * T0 / R0) # Form the regression model to find the amplitude of each exponent Mf = np.zeros((n, n), np.double) b = np.zeros(n, np.double) for i in range(n): b[i] = np.sum(d * df[i, :]) for m in range(n): Mf[i, m] = np.sum(df[i, :] * df[m, :]) # Solve the linear problem try: a = np.linalg.solve(Mf, b) except: a = np.linalg.lstsq(Mf, b)[0] # Calculate the offset for the fit offset = d0 - np.sum(df0 * a.T) # Prepare output retval = [("Amp_%d" % i, a[i]) for i in range(n)] retval += [("Tau_%d" % i, si * tau[i]) for i in range(n)] retval += [("Offset", np.double(offset))] retval = dict(retval) return retval
def EPSPtrains(latency=200, numStim=4, intvlList=[1, 0.8, 0.6, 0.4, 0.2, 0.1, 0.08, 0.06, 0.04, 0.02]): # Initialize numTrains = len(intvlList) # Number of trains intvlArray = np.array(intvlList) * 1000 # Units in ms si = stf.get_sampling_interval() # Units in ms # Background subtraction traceBaselines = [] subtractedTraces = [] k = 1e-4 x = [i * stf.get_sampling_interval() for i in range(stf.get_size_trace())] for i in range(numTrains): stf.set_trace(i) z = x y = stf.get_trace() traceBaselines.append(y) ridx = [] if intvlArray[i] > 500: for j in range(numStim): ridx += range( int(round(((intvlArray[i] * j) + latency - 1) / si)), int(round( ((intvlArray[i] * (j + 1)) + latency - 1) / si)) - 1) else: ridx += range( int(round((latency - 1) / si)), int( round(((intvlArray[i] * (numStim - 1)) + latency + 500) / si)) - 1) ridx += range(int(round(4999 / si)), int(round(5199 / si))) z = np.delete(z, ridx, 0) y = np.delete(y, ridx, 0) yi = np.interp(x, z, y) yf = signal.symiirorder1(yi, (k**2), 1 - k) traceBaselines.append(yf) subtractedTraces.append(stf.get_trace() - yf) stf.new_window_list(traceBaselines) stf.new_window_list(subtractedTraces) # Measure depolarization # Initialize variables a = [] b = [] # Set baseline start and end cursors stf.set_base_start(np.round( (latency - 50) / si)) # Average during 50 ms period before stimulus stf.set_base_end(np.round(latency / si)) # Set fit start cursor stf.set_fit_start(np.round(latency / si)) stf.set_fit_end( np.round(((intvlArray[1] * (numStim - 1)) + latency + 1000) / si)) # Include a 1 second window after last stimulus # Start AUC calculations for i in range(numTrains): stf.set_trace(i) stf.measure() b.append(stf.get_base()) n = int(stf.get_fit_end() + 1 - stf.get_fit_start()) x = np.array([k * stf.get_sampling_interval() for k in range(n)]) y = stf.get_trace()[int(stf.get_fit_start()):int(stf.get_fit_end() + 1)] a.append(np.trapz(y - b[i], x)) # Units in V.s return a
def wcp(V_step=-5, step_start=10, step_duration=20): """ Measures whole cell properties. Specifically, this function returns the voltage clamp step estimates of series resistance, input resistance, cell membrane resistance, cell membrane capacitance, cell surface area and specific membrane resistance. The series (or access) resistance is obtained my dividing the voltage step by the peak amplitude of the current transient (Ogden, 1994): Rs = V / Ip The input resistance is obtained by dividing the voltage step by the average amplitude of the steady-state current (Barbour, 2014): Rin = V / Iss The cell membrane resistance is calculated by subtracting the series resistance from the input resistance (Barbour, 1994): Rm = Rin - Rs The cell membrane capacitance is estimated by dividing the transient charge by the size of the voltage-clamp step (Taylor et al. 2012): Cm = Q / V The cell surface area is estimated by dividing the cell capacitance by the specific cell capacitance, c (1.0 uF/cm^2; Gentet et al. 2000; Niebur, 2008): Area = Cm / c The specific membrane resistance is calculated by multiplying the cell membrane resistance with the cell surface area: rho = Rm * Area Users should be aware of the approximate nature of determining cell capacitance and derived parameters from the voltage-clamp step method (Golowasch, J. et al., 2009) References: Barbour, B. (2014) Electronics for electrophysiologists. Microelectrode Techniques workshop tutorial. www.biologie.ens.fr/~barbour/electronics_for_electrophysiologists.pdf Gentet, L.J., Stuart, G.J., and Clements, J.D. (2000) Direct measurement of specific membrane capacitance in neurons. Biophys J. 79(1):314-320 Golowasch, J. et al. (2009) Membrane Capacitance Measurements Revisited: Dependence of Capacitance Value on Measurement Method in Nonisopotential Neurons. J Neurophysiol. 2009 Oct; 102(4): 2161-2175. Niebur, E. (2008), Scholarpedia, 3(6):7166. doi:10.4249/scholarpedia.7166 www.scholarpedia.org/article/Electrical_properties_of_cell_membranes (revision #13938, last accessed 30 April 2018) Ogden, D. Chapter 16: Microelectrode electronics, in Ogden, D. (ed.) Microelectrode Techniques. 1994. 2nd Edition. Cambridge: The Company of Biologists Limited. Taylor, A.L. (2012) What we talk about when we talk about capacitance measured with the voltage-clamp step method J Comput Neurosci. 32(1):167-175 """ # Error checking if stf.get_yunits() != "pA": raise ValueError('The recording is not voltage clamp') # Prepare variables from input arguments si = stf.get_sampling_interval() t0 = step_start / si l = step_duration / si # Set cursors and update measurements stf.set_base_start((step_start - 1) / si) stf.set_base_end(t0 - 1) stf.set_peak_start(t0) stf.set_peak_end((step_start + 1) / si) stf.set_fit_start(t0) stf.set_fit_end(t0 + l - 1) stf.set_peak_direction("both") stf.measure() # Calculate series resistance (Rs) from initial transient b = stf.get_base() Rs = 1000 * V_step / (stf.get_peak() - b) # in Mohm # Calculate charge delivered during the voltage clamp step n = int(stf.get_fit_end() + 1 - stf.get_fit_start()) x = [i * stf.get_sampling_interval() for i in range(n)] y = stf.get_trace()[int(stf.get_fit_start()):int(stf.get_fit_end() + 1)] Q = np.trapz(y - b, x) # Set cursors and update measurements stf.set_base_start(t0 + l - 1 - (step_duration / 4) / si) stf.set_base_end(t0 + l - 1) stf.measure() # Measure steady state current and calculate input resistance I = stf.get_base() - b Rin = 1000 * V_step / I # in Mohm # Calculate cell membrane resistance Rm = Rin - Rs # in Mohm # Calculate voltage-clamp step estimate of the cell capacitance t = x[-1] - x[0] Cm = (Q - I * t) / V_step # in pF # Estimate membrane surface area, where the capacitance per unit area is 1.0 uF/cm^2 A = Cm * 1e-06 / 1.0 # in cm^2 # Calculate specific membrane resistance rho = 1e+03 * Rm * A # in kohm.cm^2; usually 10 at rest # Create table of results retval = [] retval += [("Holding current (pA)", b)] retval += [("Series resistance (Mohm)", Rs)] retval += [("Input resistance (Mohm)", Rin)] retval += [("Cell resistance (Mohm)", Rm)] retval += [("Cell capacitance (pF)", Cm)] retval += [("Surface area (um^2)", A * 1e+04**2)] retval += [("Membrane resistivity (kohm.cm^2)", rho)] retval = dict(retval) stf.show_table(retval, "Whole-cell properties") return retval