diff --git a/fishbook/reproclasses.py b/fishbook/reproclasses.py index 5b0f934..883da38 100644 --- a/fishbook/reproclasses.py +++ b/fishbook/reproclasses.py @@ -47,7 +47,9 @@ def gaussian_kernel(sigma, dt): class BaselineData: - + """ + Class representing the Baseline data that has been recorded within a given Dataset. + """ def __init__(self, dataset: Dataset): self.__spike_data = [] self.__eod_data = [] @@ -284,20 +286,43 @@ class BaselineData: return np.asarray(data) -class BoltzmannFit(): - def __init__(self, xvalues: np.ndarray, yvalues: np.ndarray): +class BoltzmannFit: + """ + Class representing a fit of a Boltzmann function to some data. + """ + + def __init__(self, xvalues: np.ndarray, yvalues: np.ndarray, initial_params=None): + """ + Constructor. Takes the x and the y data and tries to fit a Boltzmann to it. + + :param xvalues: numpy array of x (e.g. contrast) values + :param yvalues: numpy array of y (e.g. firing rate) values + :param initial_params: list of initial parameters, default None to autogenerate + """ assert(len(xvalues) == len(yvalues)) self.__xvals = xvalues self.__yvals = yvalues self.__fit_params = None + self.__initial_params = initial_params self.__x_sorted = np.unique(self.__xvals) self.__y_avg = None self.__y_err = None self.__do_fit() @staticmethod - def boltzmann(x, f_max, slope, inflection): - y = f_max / (1 + np.exp(-slope * (x - inflection))) + def boltzmann(x, y_max, slope, inflection): + """ + The underlying Boltzmann function. + .. math:: + f(x) = y_max / \exp{-slope*(x-inflection} + + :param x: The x values. + :param y_max: The maximum value. + :param slope: The slope parameter k + :param inflection: the position of the inflection point. + :return: the y values. + """ + y = y_max / (1 + np.exp(-slope * (x - inflection))) return y def __do_fit(self): @@ -306,14 +331,27 @@ class BoltzmannFit(): for i, c in enumerate(self.__x_sorted): self.__y_avg[i] = np.mean(self.__yvals[self.__xvals == c]) self.__y_err[i] = np.std(self.__yvals[self.__xvals == c]) - self.__fit_params, _ = curve_fit(self.boltzmann, self.__x_sorted, self.__y_avg, [np.max(self.__y_avg), 0, 0]) + if self.__initial_params: + p = self.__initial_params + else: + p = [np.max(self.__y_avg), 0, 0] + self.__fit_params, _ = curve_fit(self.boltzmann, self.__x_sorted, self.__y_avg, p) @property - def slope(self): + def slope(self) -> float: + """ + The slope of the linear part of the Boltzmann, i.e. + .. math:: + s = f_max \cdot k / 4 + :return: the slope. + """ return self.__fit_params[0] * self.__fit_params[1] / 4 @property def parameters(self): + """ fit parameters + :return: The fit parameters. + """ return self.__fit_params @property @@ -331,7 +369,18 @@ class BoltzmannFit(): class FIData: + """ + Class representing the data recorded with the relacs FI-Curve repro. The instance will load the data upon + construction which may take a while. + FI Data offers convenient access to the spike and local EOD data as well as offers conveince methods to get the + firing rate and also to fit a Boltzmann function to the the FI curve. + """ def __init__(self, dataset: Dataset): + """ + Constructor. + + :param dataset: The dataset entity for which the fi curve repro data should be loaded. + """ self.__spike_data = [] self.__contrasts = [] self.__eod_data = [] @@ -414,7 +463,7 @@ class FIData: b = f.blocks[0] mt = None - for i in tqdm(range(len(stimuli))): + for i in tqdm(range(len(stimuli)), desc="Loading data"): s = stimuli[i] if not mt or mt.id != s.multi_tag_id: mt = b.multi_tags[s.multi_tag_id] @@ -427,29 +476,62 @@ class FIData: f.close() return spikes, contrasts, eods, time + def __read_spikes_from_directory(self, repro = RePro): + + pass + @property - def size(self): + def size(self) -> int: + """ + The number of recorded trials + + :return: An integer with the number of trials. + """ return len(self.__spike_data) def spikes(self, index=-1): + """ + The spike times recorded in the specified trial(s) + + :param index: the index of the trial. Default of -1 indicates that all data should be returned. + :return: + """ if 0 <= index < self.size: return self.__spike_data[index] else: return self.__spike_data def eod(self, index=-1): + """ + The local eod (including the stimulus) measurement of the selected trial(s). + + :param index: the index of the trial. Default of -1 indicates that all data should be returned. + :return: Either two vectors representing time and the local eod or two lists of such vectors + """ if 0 <= index < self.size: return self.__eod_times[index], self.__eod_data[index] else: return self.__eod_times, self.__eod_data def contrast(self, index=-1): + """ + The stimulus contrast used in the respective trial(s). + \ + :param index: the index of the trial. Default of -1 indicates that all data should be returned. + :return: Either a single scalar representing the contrast, or a list of such scalars, one entry for each trial. + """ if 0 <= index < self.size: return self.__contrasts[index] else: return self.__contrasts def time_axis(self, index=-1): + """ + Get the time axis of a single trial or a list of time-vectors for all trials. + + :param index: the index of the trial. Default of -1 indicates that all data should be returned. + :return: Either a single vector representing time, or a list of such vectors, one for each trial. + """ if 0 <= index < self.size: return self.__eod_times[index] else: @@ -475,10 +557,13 @@ class FIData: def boltzmann_fit(self, start_time=0.01, end_time=0.05, kernel_width=0.005): """ + Extracts the average firing rate within a time window from the averaged across trial firing rate. + The analysis time window is specified by the start_time and end_time parameters. Firing rate is estimated by + convolution with a Gaussian kernel of a given width. All parameters are given in 's'. - :param start_time: - :param end_time: - :param kernel_width: + :param start_time: the start of the analysis window. + :param end_time: the end of the analysis window. + :param kernel_width: standard deviation of the Gaussian kernel used for firing rate estimation. :return: object of type BoltzmannFit """ contrasts = np.zeros(self.size)