Source code for graphinglib.fits

from __future__ import annotations

from copy import deepcopy
from functools import partial
from typing import Callable, Literal, Optional

import matplotlib.pyplot as plt
import numpy as np
from numpy.typing import ArrayLike
from scipy.optimize import curve_fit

from .data_plotting_1d import Curve, Scatter
from .graph_elements import Point

try:
    from typing import Self
except ImportError:
    from typing_extensions import Self


class GeneralFit(Curve):
    """
    Dummy class for curve fits. Defines the interface for all curve fits.

    .. attention:: Not to be used directly.

    Parameters
    ----------
    curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter`
        The object to be fit.
    label : str, optional
        Label to be displayed in the legend.
    color : str
        Color of the curve.
        Default depends on the ``figure_style`` configuration.
    line_width : int
        Line width of the curve.
        Default depends on the ``figure_style`` configuration.
    line_style : str
        Line style of the curve.
        Default depends on the ``figure_style`` configuration.
    """

    def __init__(
        self,
        curve_to_be_fit: Curve | Scatter,
        label: Optional[str] = None,
        color: str = "default",
        line_width: int | Literal["default"] = "default",
        line_style: str = "default",
    ) -> None:
        """
        Parameters
        ----------
        curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter`
            The object to be fit.
        label : str, optional
            Label to be displayed in the legend.
        color : str
            Color of the curve.
            Default depends on the ``figure_style`` configuration.
        line_width : int
            Line width of the curve.
            Default depends on the ``figure_style`` configuration.
        line_style : str
            Line style of the curve.
            Default depends on the ``figure_style`` configuration.
        """
        self._curve_to_be_fit = curve_to_be_fit
        self._color = color
        self._line_width = line_width
        if label:
            self._label = label + " : " + "$f(x) = $" + str(self)
        else:
            self._label = "$f(x) = $" + str(self)
        self._line_style = line_style

        self._function: Callable[[np.ndarray], np.ndarray]

        self._setup_attributes()

    def _setup_attributes(self) -> None:
        self._res_curves_to_be_plotted = False
        self._res_sigma_multiplier = None
        self._res_color = None
        self._res_line_width = None
        self._res_line_style = None

        self._show_errorbars: bool = False
        self._errorbars_color = None
        self._errorbars_line_width = None
        self._cap_thickness = None
        self._cap_width = None

        self._show_error_curves: bool = False
        self._error_curves_fill_between: bool = False
        self._error_curves_color = None
        self._error_curves_line_style = None
        self._error_curves_line_width = None

        self._fill_between_bounds: Optional[tuple[float, float]] = None
        self._fill_between_other_curve: Optional[Self] = None
        self._fill_between_color: Optional[str] = None

    @property
    def curve_to_be_fit(self) -> Curve | Scatter:
        return self._curve_to_be_fit

    @curve_to_be_fit.setter
    def curve_to_be_fit(self, curve: Curve | Scatter) -> None:
        self._curve_to_be_fit = curve

    @property
    def function(self) -> Callable[[np.ndarray], np.ndarray]:
        return self._function

    def __str__(self) -> str:
        """
        Create a string representation of the fit function.
        """
        raise NotImplementedError()

    def get_coordinates_at_x(self, x: float) -> tuple[float, float]:
        return (x, self._function(x))

    def create_point_at_x(
        self,
        x: float,
        label: str | None = None,
        color: str = "default",
        edge_color: str = "default",
        marker_size: float | Literal["default"] = "default",
        marker_style: str = "default",
        line_width: float | Literal["default"] = "default",
    ) -> Point:
        """
        Gets the point on the curve at a given x value.

        Parameters
        ----------
        x : float
            x value of the point.
        label : str, optional
            Label to be displayed in the legend.
        color : str
            Face color of the point.
            Default depends on the ``figure_style`` configuration.
        edge_color : str
            Edge color of the point.
            Default depends on the ``figure_style`` configuration.
        marker_size : float
            Size of the point.
            Default depends on the ``figure_style`` configuration.
        marker_style : str
            Style of the point.
            Default depends on the ``figure_style`` configuration.
        line_width : float
            Width of the edge of the point.
            Default depends on the ``figure_style`` configuration.

        Returns
        -------
        :class:`~graphinglib.graph_elements.Point` object on the curve at the given x value.
        """
        return Point(
            x,
            self._function(x),
            label=label,
            color=color,
            edge_color=edge_color,
            marker_size=marker_size,
            marker_style=marker_style,
            edge_width=line_width,
        )

    def get_coordinates_at_y(
        self, y: float, interpolation_method: str = "linear"
    ) -> list[tuple[float, float]]:
        return super().get_coordinates_at_y(y, interpolation_method)

    def create_points_at_y(
        self,
        y: float,
        interpolation_kind: str = "linear",
        label: str | None = None,
        color: str = "default",
        edge_color: str = "default",
        marker_size: float | Literal["default"] = "default",
        marker_style: str = "default",
        line_width: float | Literal["default"] = "default",
    ) -> list[Point]:
        """
        Creates the Points on the curve at a given y value.

        Parameters
        ----------
        y : float
            y value of the point.
        interpolation_kind : str
            Kind of interpolation to be used.
            Default is "linear".
        label : str, optional
            Label to be displayed in the legend.
        color : str
            Face color of the point.
            Default depends on the ``figure_style`` configuration.
        edge_color : str
            Edge color of the point.
            Default depends on the ``figure_style`` configuration.
        marker_size : float
            Size of the point.
            Default depends on the ``figure_style`` configuration.
        marker_style : str
            Style of the point.
            Default depends on the ``figure_style`` configuration.
        line_width : float
            Width of the edge of the point.
            Default depends on the ``figure_style`` configuration.

        Returns
        -------
        list[:class:`~graphinglib.graph_elements.Point`]
            List of :class:`~graphinglib.graph_elements.Point` objects on the curve at the given y value.
        """
        coord_pairs = self.get_coordinates_at_y(y, interpolation_kind)
        points = [
            Point(
                coord[0],
                coord[1],
                label=label,
                color=color,
                edge_color=edge_color,
                marker_size=marker_size,
                marker_style=marker_style,
                edge_width=line_width,
            )
            for coord in coord_pairs
        ]
        return points

    def _plot_element(self, axes: plt.Axes, z_order: int, **kwargs) -> None:
        """
        Plots the element in the specified
        Axes
        """
        params = {
            "color": self._color,
            "linewidth": self._line_width,
            "linestyle": self._line_style,
        }
        params = {key: value for key, value in params.items() if value != "default"}
        (self.handle,) = axes.plot(
            self._x_data,
            self._y_data,
            label=self._label,
            zorder=z_order,
            **params,
        )
        if self._res_curves_to_be_plotted:
            y_fit = self._y_data
            residuals = self.get_residuals()
            std = np.std(residuals)
            y_fit_plus_std = y_fit + (self._res_sigma_multiplier * std)
            y_fit_minus_std = y_fit - (self._res_sigma_multiplier * std)
            params = {
                "color": self._res_color,
                "linewidth": self._res_line_width,
                "linestyle": self._res_line_style,
            }
            params = {key: value for key, value in params.items() if value != "default"}
            axes.plot(
                self._x_data,
                y_fit_minus_std,
                zorder=z_order,
                **params,
            )
            axes.plot(
                self._x_data,
                y_fit_plus_std,
                zorder=z_order,
                **params,
            )
        if self._fill_between_bounds:
            kwargs = {"alpha": 0.2}
            if self._fill_between_color:
                kwargs["color"] = self._fill_between_color
            else:
                kwargs["color"] = self.handle[0].get_color()
            params = {key: value for key, value in kwargs.items() if value != "default"}
            axes.fill_between(
                self._x_data,
                self._y_data,
                where=np.logical_and(
                    self._x_data >= self._fill_between_bounds[0],
                    self._x_data <= self._fill_between_bounds[1],
                ),
                zorder=z_order - 2,
                **params,
            )

    def show_residual_curves(
        self,
        sigma_multiplier: float = 1,
        color: str = "default",
        line_width: float | Literal["default"] = "default",
        line_style: str = "default",
    ) -> None:
        """
        Displays two curves ``"sigma_multiplier"`` standard deviations above and below the fit curve.

        Parameters
        ----------
        sigma_multiplier : float
            Distance in standard deviations from the fit curve.
            Default is 1.
        color : str
            Color of the residual curves.
            Default depends on the ``figure_style`` configuration.
        line_width : float
            Line width of the residual curves.
            Default depends on the ``figure_style`` configuration.
        """
        self._res_curves_to_be_plotted = True
        self._res_sigma_multiplier = sigma_multiplier
        self._res_color = color
        self._res_line_width = line_width
        self._res_line_style = line_style

    def get_residuals(self) -> np.ndarray:
        """
        Calculates the residuals of the fit curve.

        Returns
        -------
        residuals : np.ndarray
            Array of residuals.
        """
        y_data = self._function(self._curve_to_be_fit._x_data)
        residuals = y_data - self._curve_to_be_fit._y_data
        return residuals

    def get_Rsquared(self) -> float:
        """
        Calculates the :math:`R^2` value of the fit curve.

        Returns
        -------
        Rsquared : float
            :math:`R^2` value
        """
        Rsquared = 1 - (
            np.sum(self.get_residuals() ** 2)
            / np.sum(
                (self._curve_to_be_fit._y_data - np.mean(self._curve_to_be_fit._y_data))
                ** 2
            )
        )
        return Rsquared

    def copy(self) -> Self:
        return deepcopy(self)


[docs] class FitFromPolynomial(GeneralFit): """ Creates a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing curve object using a polynomial fit. Fits a polynomial of the form :math:`f(x) = a_0 + a_1 x + a_2 x^2 + ... + a_n x^n` to the given curve. All standard Curve attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. degree : int Degree of the polynomial fit. label : str, optional Label to be displayed in the legend. color : str Color of the :class:`~graphinglib.data_plotting_1d.Curve`. Default depends on the ``figure_style`` configuration. line_width : int Line width of the :class:`~graphinglib.data_plotting_1d.Curve`. Default depends on the ``figure_style`` configuration. line_style : str Line style of the :class:`~graphinglib.data_plotting_1d.Curve`. Default depends on the ``figure_style`` configuration. Attributes ---------- coeffs : np.ndarray Coefficients of the polynomial fit. The first element is the coefficient of the lowest order term (constant term). cov_matrix : np.ndarray Covariance matrix of the polynomial fit (using the same order as the coeffs attribute). standard_deviation : np.ndarray Standard deviation of the coefficients of the polynomial fit (same order as coeffs). function : Callable Polynomial function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, degree: int, label: Optional[str] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: int | Literal["default"] = "default", ) -> None: """ Creates a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing curve object using a polynomial fit. Fits a polynomial of the form :math:`f(x) = a_0 + a_1 x + a_2 x^2 + ... + a_n x^n` to the given curve. All standard Curve attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. degree : int Degree of the polynomial fit. label : str, optional Label to be displayed in the legend. color : str Color of the :class:`~graphinglib.data_plotting_1d.Curve`. Default depends on the ``figure_style`` configuration. line_width : int Line width of the :class:`~graphinglib.data_plotting_1d.Curve`. Default depends on the ``figure_style`` configuration. line_style : str Line style of the :class:`~graphinglib.data_plotting_1d.Curve`. Default depends on the ``figure_style`` configuration. Attributes ---------- coeffs : np.ndarray Coefficients of the polynomial fit. The first element is the coefficient of the lowest order term (constant term). cov_matrix : np.ndarray Covariance matrix of the polynomial fit (using the same order as the coeffs attribute). standard_deviation : np.ndarray Standard deviation of the coefficients of the polynomial fit (same order as coeffs). function : Callable Polynomial function with the parameters of the fit. """ self._curve_to_be_fit = curve_to_be_fit inversed_coeffs, inversed_cov_matrix = np.polyfit( self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, degree, cov=True, ) self._coeffs = inversed_coeffs[::-1] self._cov_matrix = np.flip(inversed_cov_matrix) self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) self._function = self._polynomial_func_with_params() self._color = color self._line_width = line_width if label: self._label = label + " : " + "$f(x) = $" + str(self) else: self._label = "$f(x) = $" + str(self) self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def coeffs(self) -> np.ndarray: return self._coeffs @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation(self) -> np.ndarray: return self._standard_deviation def __str__(self) -> str: """ Creates a string representation of the polynomial function. """ coeff_chunks = [] power_chunks = [] ordered_rounded_coeffs = [round(coeff, 3) for coeff in self._coeffs[::-1]] for coeff, power in zip( ordered_rounded_coeffs, range(len(ordered_rounded_coeffs) - 1, -1, -1) ): if coeff == 0: continue coeff_chunks.append(self._format_coeff(coeff)) power_chunks.append(self._format_power(power)) coeff_chunks[0] = coeff_chunks[0].lstrip("+ ") return ( "$" + "".join( [coeff_chunks[i] + power_chunks[i] for i in range(len(coeff_chunks))] ) + "$" ) @staticmethod def _format_coeff(coeff: float) -> str: """ Formats a coefficient to be displayed in the string representation of the polynomial function. """ return " - {0}".format(abs(coeff)) if coeff < 0 else " + {0}".format(coeff) @staticmethod def _format_power(power: int) -> str: """ Formats a power to be displayed in the string representation of the polynomial function. """ return "x^{0}".format(power) if power != 0 else "" def _polynomial_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates a polynomial function with the parameters of the fit. Returns ------- function : Callable Polynomial function with the parameters of the fit. """ return lambda x: sum( coeff * x**exponent for exponent, coeff in enumerate(self._coeffs) )
[docs] class FitFromSine(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a sinusoidal fit. Fits a sine function of the form :math:`f(x) = a sin(bx + c) + d` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit (order: amplitude (a), frequency (b), phase (c), vertical shift (d) as written above). color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- amplitude : float Amplitude of the sine function. frequency_rad : float Frequency of the sine function in radians. phase : float Phase of the sine function. vertical_shift : float Vertical shift of the sine function. cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Sine function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: str = "default", line_style: str = "default", ) -> None: """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a sinusoidal fit. Fits a sine function of the form :math:`f(x) = a sin(bx + c) + d` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit (order: amplitude (a), frequency (b), phase (c), vertical shift (d) as written above). color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- amplitude : float Amplitude of the sine function. frequency_rad : float Frequency of the sine function in radians. frequency_deg : float Frequency of the sine function in degrees. phase_rad : float Phase of the sine function. phase_deg : float Phase of the sine function in degrees. vertical_shift : float Vertical shift of the sine function. parameters : np.ndarray Parameters of the fit (amplitude, frequency (rad), phase (rad), vertical shift) cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Sine function with the parameters of the fit. """ self._curve_to_be_fit = curve_to_be_fit self._guesses = guesses self._calculate_parameters() self._function = self._sine_func_with_params() self._color = color if label: self._label = label + " : " + "$f(x) = $" + str(self) else: self._label = "$f(x) = $" + str(self) self._line_width = line_width self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def amplitude(self) -> float: return self._amplitude @property def frequency_rad(self) -> float: return self._frequency_rad @property def frequency_deg(self) -> float: return self._frequency_deg @property def phase_rad(self) -> float: return self._phase_rad @property def phase_deg(self) -> float: return self._phase_deg @property def vertical_shift(self) -> float: return self._vertical_shift @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation(self) -> np.ndarray: return self._standard_deviation @property def parameters(self) -> np.ndarray: return self._parameters def __str__(self) -> str: """ Creates a string representation of the sine function. """ part1 = f"{self._amplitude:.3f} \sin({self._frequency_rad:.3f}x" part2 = ( f" + {self._phase_rad:.3f})" if self._phase_rad >= 0 else f" - {abs(self._phase_rad):.3f})" ) part3 = ( f" + {self._vertical_shift:.3f}" if self._vertical_shift >= 0 else f" - {abs(self._vertical_shift):.3f}" ) return f"${part1 + part2 + part3}$" def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._sine_func_template, self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._amplitude, self._frequency_rad, self._phase_rad, self._vertical_shift = ( self._parameters ) self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) # Calculate the frequency and phase in degrees self._frequency_deg = np.degrees(self._frequency_rad) self._phase_deg = np.degrees(self._phase_rad) @staticmethod def _sine_func_template( x: np.ndarray, a: float, b: float, c: float, d: float ) -> np.ndarray: """ Function to be passed to the ``curve_fit`` function. """ return a * np.sin(b * x + c) + d def _sine_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates a sine function with the parameters of the fit. Returns ------- Callable Sine function with the parameters of the fit. """ return ( lambda x: self._amplitude * np.sin(self._frequency_rad * x + self._phase_rad) + self._vertical_shift )
[docs] class FitFromExponential(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using an exponential fit. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Exponential function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: str = "default", ) -> None: """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) of the form :math:`f(x) = a \exp(bx + c)` from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using an exponential fit. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Exponential function with the parameters of the fit. """ self._curve_to_be_fit = curve_to_be_fit self._guesses = guesses self._calculate_parameters() self._function = self._exp_func_with_params() self._color = color if label: self._label = label + " : " + "$f(x) = $" + str(self) else: self._label = "$f(x) = $" + str(self) self._line_width = line_width self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def parameters(self) -> np.ndarray: return self._parameters @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation(self) -> np.ndarray: return self._standard_deviation def __str__(self) -> str: """ Creates a string representation of the exponential function. """ part1 = f"{self._parameters[0]:.3f} \exp({self._parameters[1]:.3f}x" part2 = ( f" + {self._parameters[2]:.3f})" if self._parameters[2] >= 0 else f" - {abs(self._parameters[2]):.3f})" ) return f"${part1 + part2}$" def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._exp_func_template, self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) @staticmethod def _exp_func_template(x: np.ndarray, a: float, b: float, c: float) -> np.ndarray: """ Function to be passed to the ``curve_fit`` function. """ return a * np.exp(b * x + c) def _exp_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates an exponential function with the parameters of the fit. Returns ------- function : Callable Exponential function with the parameters of the fit. """ return lambda x: self._parameters[0] * np.exp( self._parameters[1] * x + self._parameters[2] )
[docs] class FitFromGaussian(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a gaussian fit. Fits a gaussian function of the form :math:`f(x) = A e^{-\\frac{(x - \mu)^2}{2 \sigma^2}}` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is amplitude (A), mean (mu), standard deviation (sigma). color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- amplitude : float Amplitude of the gaussian function. mean : float Mean of the gaussian function. standard_deviation : float Standard deviation of the gaussian function. .. warning:: The ``standard_deviation`` attribute doesn't represent the standard deviation of the fit parameters as it does in the other fit classes. Instead, it represents the standard deviation of the gaussian function (it is one of parameters of the fit). The standard deviation of the fit parameters can be found in the ``standard_deviation_of_fit_params`` attribute. cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation_of_fit_params : np.ndarray Standard deviation of the parameters of the fit. function : Callable Gaussian function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: str = "default", ) -> None: """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a gaussian fit. Fits a gaussian function of the form :math:`f(x) = A e^{-\\frac{(x - \mu)^2}{2 \sigma^2}}` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- amplitude : float Amplitude of the gaussian function. mean : float Mean of the gaussian function. standard_deviation : float Standard deviation of the gaussian function. cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation_of_fit_params : np.ndarray Standard deviation of the parameters of the fit. function : Callable Gaussian function with the parameters of the fit. Warning ------- The ``standard_deviation`` attribute doesn't represent the standard deviation of the fit parameters as it does in the other fit classes. Instead, it represents the standard deviation of the gaussian function (it is one of parameters of the fit). The standard deviation of the fit parameters can be found in the ``standard_deviation_of_fit_params`` attribute. """ self._curve_to_be_fit = curve_to_be_fit self._guesses = guesses self._calculate_parameters() self._function = self._gaussian_func_with_params() self._color = color if label: self._label = label + " : " + str(self) else: self._label = str(self) self._line_width = line_width self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def amplitude(self) -> float: return self._amplitude @property def mean(self) -> float: return self._mean @property def standard_deviation(self) -> float: return self._standard_deviation @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation_of_fit_params(self) -> np.ndarray: return self._standard_deviation_of_fit_params @property def parameters(self) -> np.ndarray: return self._parameters def __str__(self) -> str: """ Creates a string representation of the gaussian function. """ return f"$\mu = {self._mean:.3f}, \sigma = {self._standard_deviation:.3f}, A = {self._amplitude:.3f}$" def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._gaussian_func_template, self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._amplitude = self._parameters[0] self._mean = self._parameters[1] self._standard_deviation = self._parameters[2] self._standard_deviation_of_fit_params = np.sqrt(np.diag(self._cov_matrix)) @staticmethod def _gaussian_func_template( x: np.ndarray, amplitude: float, mean: float, standard_deviation: float ) -> np.ndarray: """ Function to be passed to the ``curve_fit`` function. """ return amplitude * np.exp(-(((x - mean) / standard_deviation) ** 2) / 2) def _gaussian_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates a gaussian function with the parameters of the fit. Returns ------- function : Callable Gaussian function with the parameters of the fit. """ return lambda x: self._amplitude * np.exp( -(((x - self._mean) / self._standard_deviation) ** 2) / 2 )
[docs] class FitFromSquareRoot(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a square root fit. Fits a square root function of the form :math:`f(x) = a \sqrt{x + b} + c` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Square root function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: str = "default", ) -> None: """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a square root fit. Fits a square root function of the form :math:`f(x) = a \sqrt{x + b} + c` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Square root function with the parameters of the fit. """ self._curve_to_be_fit = curve_to_be_fit self._guesses = guesses self._calculate_parameters() self._function = self._square_root_func_with_params() self._color = color if label: self._label = label + " : " + str(self) else: self._label = str(self) self._line_width = line_width self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def parameters(self) -> np.ndarray: return self._parameters @property def cov_matrix(self) -> np.ndarray: return self.cov_matrix @property def standard_deviation(self) -> np.ndarray: return self.standard_deviation def __str__(self) -> str: """ Creates a string representation of the square root function. """ return f"${self._parameters[0]:.3f} \sqrt{{x {'+' if self._parameters[1] > 0 else '-'} {abs(self._parameters[1]):.3f}}} {'+' if self._parameters[2] > 0 else '-'} {abs(self._parameters[2]):.3f}$" def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._square_root_func_template, self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) @staticmethod def _square_root_func_template( x: np.ndarray, a: float, b: float, c: float ) -> np.ndarray: """ Function to be passed to the ``curve_fit`` function. """ return a * np.sqrt(x + b) + c def _square_root_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates a square root function with the parameters of the fit. Returns ------- function : Callable Square root function with the parameters of the fit. """ return ( lambda x: self._parameters[0] * np.sqrt(x + self._parameters[1]) + self._parameters[2] )
[docs] class FitFromLog(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a logarithmic fit. Fits a logarithmic function of the form :math:`f(x) = a \log_{base}(x + b) + c` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. log_base : float Base of the logarithm. Default is e. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Logarithmic function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, log_base: float = np.e, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: str = "default", ) -> None: """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a logarithmic fit. Fits a logarithmic function of the form :math:`f(x) = a \log_{base}(x + b) + c` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. log_base : float Base of the logarithm. Default is e. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Logarithmic function with the parameters of the fit. """ self._curve_to_be_fit = curve_to_be_fit self._log_base = log_base self._guesses = guesses self._calculate_parameters() self._function = self._log_func_with_params() self._color = color if label: self._label = label + " : " + str(self) else: self._label = str(self) self._line_width = line_width self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def parameters(self) -> np.ndarray: return self._parameters @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation(self) -> np.ndarray: return self._standard_deviation def __str__(self) -> str: """ Creates a string representation of the logarithmic function. """ return f"${self._parameters[0]:.3f} log_{self._log_base if self._log_base != np.e else 'e'}(x {'-' if self._parameters[1] < 0 else '+'} {abs(self._parameters[1]):.3f}) {'-' if self._parameters[2] < 0 else '+'} {abs(self._parameters[2]):.3f}$" def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._log_func_template(), self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) def _log_func_template( self, ) -> Callable[[float | np.ndarray, float, float, float], float | np.ndarray]: """ Function to be passed to the ``curve_fit`` function. """ return lambda x, a, b, c: a * (np.log(x + b) / np.log(self._log_base)) + c def _log_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates a logarithmic function with the parameters of the fit. Returns ------- function : Callable Logarithmic function with the parameters of the fit. """ return ( lambda x: self._parameters[0] * (np.log(x + self._parameters[1]) / np.log(self._log_base)) + self._parameters[2] )
[docs] class FitFromFunction(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from a :class:`~graphinglib.data_plotting_1d.Curve` object using an arbitrary function passed as an argument. Fits a function of the form :math:`f(x, a, b, c, ...)` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- function : Callable Function to be passed to the curve_fit function. curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c, ... color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Function with the parameters of the fit. """
[docs] def __init__( self, function: Callable, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: str = "default", ): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from a :class:`~graphinglib.data_plotting_1d.Curve` object using an arbitrary function passed as an argument. Fits a function of the form :math:`f(x, a, b, c, ...)` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- function : Callable Function to be passed to the curve_fit function. curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is a, b, c, ... as written above. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- parameters : np.ndarray Parameters of the fit (same order as guesses). cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable Function with the parameters of the fit. """ self._function_template = function self._curve_to_be_fit = curve_to_be_fit self._guesses = guesses self._color = color self._line_width = line_width self._line_style = line_style self._calculate_parameters() self._function = self._get_function_with_params() self._label = label self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def parameters(self) -> np.ndarray: return self._parameters @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation(self) -> np.ndarray: return self._standard_deviation def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._function_template, self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) def _get_function_with_params(self) -> Callable: """ Creates a function with the parameters of the fit. Returns ------- function : Callable Function with the parameters of the fit. """ argument_names = self._function_template.__code__.co_varnames[ : self._function_template.__code__.co_argcount ][1:] args_dict = { argument_names[i]: self._parameters[i] for i in range(len(argument_names)) } return partial(self._function_template, **args_dict)
[docs] class FitFromFOTF(GeneralFit): """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a first order transfer function (FOTF) fit. Fits a first order transfer function of the form :math:`f(x) = K\left(1-e^{-\\frac{t}{\\tau}}\\right)` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is K, tau. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- gain : float Gain of the first order transfer function. time_constant : float Time constant of the first order transfer function. cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable First order transfer function with the parameters of the fit. """
[docs] def __init__( self, curve_to_be_fit: Curve | Scatter, label: Optional[str] = None, guesses: Optional[ArrayLike] = None, color: str = "default", line_width: int | Literal["default"] = "default", line_style: str = "default", ) -> None: """ Create a curve fit (continuous :class:`~graphinglib.data_plotting_1d.Curve`) from an existing :class:`~graphinglib.data_plotting_1d.Curve` object using a first order transfer function (FOTF) fit. Fits a first order transfer function of the form :math:`f(x) = K \left(1 - e^{-\\frac{t}{\\tau}}\\right)` to the given curve. All standard :class:`~graphinglib.data_plotting_1d.Curve` attributes and methods are available. Parameters ---------- curve_to_be_fit : :class:`~graphinglib.data_plotting_1d.Curve` or :class:`~graphinglib.data_plotting_1d.Scatter` The object to be fit. label : str, optional Label to be displayed in the legend. guesses : ArrayLike, optional Initial guesses for the parameters of the fit. Order is K, tau. color : str Color of the curve. Default depends on the ``figure_style`` configuration. line_width : int Line width of the curve. Default depends on the ``figure_style`` configuration. line_style : str Line style of the curve. Default depends on the ``figure_style`` configuration. Attributes ---------- gain : float Gain of the first order transfer function. time_constant : float Time constant of the first order transfer function. cov_matrix : np.ndarray Covariance matrix of the parameters of the fit. standard_deviation : np.ndarray Standard deviation of the parameters of the fit. function : Callable First order transfer function with the parameters of the fit. """ self._curve_to_be_fit = curve_to_be_fit self._guesses = guesses self._calculate_parameters() self._function = self._fotf_func_with_params() self._color = color if label: self._label = label + " : " + str(self) else: self._label = str(self) self._line_width = line_width self._line_style = line_style self._res_curves_to_be_plotted = False number_of_points = ( len(self._curve_to_be_fit._x_data) if len(self._curve_to_be_fit._x_data) > 500 else 500 ) self._x_data = np.linspace( self._curve_to_be_fit._x_data[0], self._curve_to_be_fit._x_data[-1], number_of_points, ) self._y_data = self._function(self._x_data) self._setup_attributes()
@property def gain(self) -> float: return self._gain @property def time_constant(self) -> float: return self._time_constant @property def cov_matrix(self) -> np.ndarray: return self._cov_matrix @property def standard_deviation(self) -> np.ndarray: return self._standard_deviation @property def parameters(self) -> np.ndarray: return self._parameters def __str__(self) -> str: """ Creates a string representation of the first order transfer function. """ return f"$K = {self._gain:.3f}, \\tau = {self._time_constant:.3f}$" def _calculate_parameters(self) -> None: """ Calculates the parameters of the fit. """ self._parameters, self._cov_matrix = curve_fit( self._fotf_func_template, self._curve_to_be_fit._x_data, self._curve_to_be_fit._y_data, p0=self._guesses, ) self._gain = self._parameters[0] self._time_constant = self._parameters[1] self._standard_deviation = np.sqrt(np.diag(self._cov_matrix)) @staticmethod def _fotf_func_template( x: np.ndarray, gain: float, time_constant: float ) -> np.ndarray: """ Function to be passed to the ``curve_fit`` function. """ return gain * (1 - np.exp(-x / time_constant)) def _fotf_func_with_params( self, ) -> Callable[[float | np.ndarray], float | np.ndarray]: """ Creates a first order transfer function with the parameters of the fit. Returns ------- function : Callable First order transfer function with the parameters of the fit. """ return lambda x: self._gain * (1 - np.exp(-x / self._time_constant))