from CellData import CellData import numpy as np import matplotlib.pyplot as plt from warnings import warn import functions as fu import helperFunctions as hF class FICurve: def __init__(self, cell_data: CellData, contrast: bool = True): self.cell_data = cell_data self.using_contrast = contrast if contrast: self.stimulus_value = cell_data.get_fi_contrasts() else: self.stimulus_value = cell_data.get_fi_intensities() self.f_zeros = [] self.f_infinities = [] self.f_baselines = [] # f_max, f_min, k, x_zero self.boltzmann_fit_vars = [] # increase, offset self.f_infinity_fit = [] self.all_calculate_frequency_points() self.f_infinity_fit = hF.fit_clipped_line(self.stimulus_value, self.f_infinities) self.boltzmann_fit_vars = hF.fit_boltzmann(self.stimulus_value, self.f_zeros) def all_calculate_frequency_points(self): mean_frequencies = self.cell_data.get_mean_isi_frequencies() time_axes = self.cell_data.get_time_axes_mean_frequencies() stimulus_start = self.cell_data.get_stimulus_start() stimulus_duration = self.cell_data.get_stimulus_duration() sampling_interval = self.cell_data.get_sampling_interval() if len(mean_frequencies) == 0: warn("FICurve:all_calculate_frequency_points(): mean_frequencies is empty.\n" "Was all_calculate_mean_isi_frequencies already called?") for i in range(len(mean_frequencies)): if time_axes[i][0] > self.cell_data.get_stimulus_start(): warn("TODO: Deal with to strongly cut frequency traces in cell data! ") self.f_zeros.append(-1) self.f_baselines.append(-1) self.f_infinities.append(-1) continue f_zero = hF.detect_f_zero_in_frequency_trace(time_axes[i], mean_frequencies[i], stimulus_start, sampling_interval) self.f_zeros.append(f_zero) f_baseline = hF.detect_f_baseline_in_freq_trace(time_axes[i], mean_frequencies[i], stimulus_start, sampling_interval) self.f_baselines.append(f_baseline) f_infinity = hF.detect_f_infinity_in_freq_trace(time_axes[i], mean_frequencies[i], stimulus_start, stimulus_duration, sampling_interval) self.f_infinities.append(f_infinity) def get_f_zero_inverse_at_frequency(self, frequency): b_vars = self.boltzmann_fit_vars return fu.inverse_full_boltzmann(frequency, b_vars[0], b_vars[1], b_vars[2], b_vars[3]) def get_f_infinity_frequency_at_stimulus_value(self, stimulus_value): infty_vars = self.f_infinity_fit return fu.clipped_line(stimulus_value, infty_vars[0], infty_vars[1]) def get_f_infinity_slope(self): return self.f_infinity_fit[0] def get_fi_curve_slope_at(self, stimulus_value): fit_vars = self.boltzmann_fit_vars return fu.derivative_full_boltzmann(stimulus_value, fit_vars[0], fit_vars[1], fit_vars[2], fit_vars[3]) def get_fi_curve_slope_of_straight(self): fit_vars = self.boltzmann_fit_vars return fu.full_boltzmann_straight_slope(fit_vars[0], fit_vars[1], fit_vars[2], fit_vars[3]) def get_f_zero_and_f_inf_intersection(self): x_values = np.arange(min(self.stimulus_value), max(self.stimulus_value), 0.0001) fit_vars = self.boltzmann_fit_vars f_zero = fu.full_boltzmann(x_values, fit_vars[0], fit_vars[1], fit_vars[2], fit_vars[3]) f_inf = fu.clipped_line(x_values, self.f_infinity_fit[0], self.f_infinity_fit[1]) intersection_indicies = np.argwhere(np.diff(np.sign(f_zero - f_inf))).flatten() # print("fi-curve calc intersection:", intersection_indicies, x_values[intersection_indicies]) if len(intersection_indicies) > 1: f_baseline = np.median(self.f_baselines) best_dist = np.inf best_idx = -1 for idx in intersection_indicies: dist = abs(fu.clipped_line(x_values[idx], self.f_infinity_fit[0], self.f_infinity_fit[1]) - f_baseline) if dist < best_dist: best_dist = dist best_idx = idx return x_values[best_idx] elif len(intersection_indicies) == 0: raise ValueError("No intersection found!") else: return x_values[intersection_indicies[0]] def get_fi_curve_slope_at_f_zero_intersection(self): x = self.get_f_zero_and_f_inf_intersection() fit_vars = self.boltzmann_fit_vars return fu.derivative_full_boltzmann(x, fit_vars[0], fit_vars[1], fit_vars[2], fit_vars[3]) def plot_fi_curve(self, savepath: str = None, comp_f_baselines=None, comp_f_zeros=None, comp_f_infs=None): min_x = min(self.stimulus_value) max_x = max(self.stimulus_value) step = (max_x - min_x) / 5000 x_values = np.arange(min_x, max_x, step) plt.plot(self.stimulus_value, self.f_baselines, color='blue', label='f_base') if comp_f_baselines is not None: plt.plot(self.stimulus_value, comp_f_baselines, 'o', color='skyblue', label='comp_values base') plt.plot(self.stimulus_value, self.f_infinities, 'o', color='green', label='f_inf') plt.plot(x_values, [fu.clipped_line(x, self.f_infinity_fit[0], self.f_infinity_fit[1]) for x in x_values], color='darkgreen', label='f_inf_fit') if comp_f_infs is not None: plt.plot(self.stimulus_value, comp_f_infs, 'o', color='lime', label='comp values f_inf') plt.plot(self.stimulus_value, self.f_zeros, 'o', color='orange', label='f_zero') popt = self.boltzmann_fit_vars plt.plot(x_values, [fu.full_boltzmann(x, popt[0], popt[1], popt[2], popt[3]) for x in x_values], color='red', label='f_0_fit') if comp_f_zeros is not None: plt.plot(self.stimulus_value, comp_f_zeros, 'o', color='wheat', label='comp_values f_zero') plt.legend() plt.ylabel("Frequency [Hz]") if self.using_contrast: plt.xlabel("Stimulus contrast") else: plt.xlabel("Stimulus intensity [mv]") if savepath is None: plt.show() else: print("save") plt.savefig(savepath + "fi_curve.png") plt.close() def plot_f_point_detections(self): mean_frequencies = np.array(self.cell_data.get_mean_isi_frequencies()) time_axes = self.cell_data.get_time_axes_mean_frequencies() for i in range(len(mean_frequencies)): fig, axes = plt.subplots(1, 1, sharex="all") axes.plot(time_axes[i], mean_frequencies[i], label="voltage") axes.plot((time_axes[i][0], time_axes[i][-1]), (self.f_zeros[i], self.f_zeros[i]), label="f_zero") axes.plot((time_axes[i][0], time_axes[i][-1]), (self.f_infinities[i], self.f_infinities[i]), '--', label="f_inf") axes.plot((time_axes[i][0], time_axes[i][-1]), (self.f_baselines[i], self.f_baselines[i]), label="f_base") axes.set_title(str(self.stimulus_value[i])) plt.legend() plt.show()