Top

scipy_data_fitting module

Data Fitting with SciPy

Complete pipeline for easy data fitting with Python.

This package is registered on the Python Package Index (PyPI) at pypi.python.org/pypi/scipy-data_fitting.

The source is hosted on GitHub at github.com/razor-x/scipy-data_fitting.

This is the pdoc generated documentation, please see the README at either location above for more details about this package.

There is also pdoc documentation for the figure creation subpackage.

"""
**Data Fitting with SciPy**

Complete pipeline for easy data fitting with Python.

This package is registered on the Python Package Index (PyPI) at
[pypi.python.org/pypi/scipy-data_fitting](https://pypi.python.org/pypi/scipy-data_fitting).

The source is hosted on GitHub at
[github.com/razor-x/scipy-data_fitting](https://github.com/razor-x/scipy-data_fitting).

This is the pdoc generated documentation,
please see the README at either location above
for more details about this package.

There is also pdoc documentation for the [figure creation subpackage](figure).
"""

from .version import __version__
from .data import Data
from .fit import Fit
from .model import Model
from .plot import Plot

__all__ = ['Data', 'Fit', 'Model', 'Plot']

Classes

class Data

Provides an interface to load data from files into numpy.ndarray objects.

Example:

>>> data = Data()
>>> data.path = 'path/to/data.csv'
>>> data.scale = (1, 'kilo')
>>> data.array
class Data:
    """
    Provides an interface to load data from files into
    [`numpy.ndarray`][1] objects.

    Example:

        #!python
        >>> data = Data()
        >>> data.path = 'path/to/data.csv'
        >>> data.scale = (1, 'kilo')
        >>> data.array

    [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
    """

    def __init__(self, name=None):
        self.name = name
        """
        The identifier name for this object.
        """

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def path(self):
        """
        Path to the file containing data to load.
        """
        return self._path

    @path.setter
    def path(self, value):
        self._path = value

    @property
    def array(self):
        """
        Data as a [`numpy.ndarray`][1] in the form

            #!python
            [
                [ x1, x2, x3, ... ],
                [ y1, y2, y3, ...]
            ]

        By default, if unset, this will be set on first access
        by calling `scipy_data_fitting.Data.load_data`.

        When loaded from file, the x and y values will be scaled according
        to `scipy_data_fitting.Data.scale`.

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
        """
        if not hasattr(self, '_array'): self._array = self.load_data()
        return self._array

    @array.setter
    def array(self, value):
        self._array = value

    @property
    def error(self):
        """
        Error associated with the data as a two element tuple of the form `(x_error, y_error)`.

        Both `x_error` and `y_error` is a [`numpy.ndarray`][1] or `None`.

        The array dimensions depend on how the error is specified:

        1. Symmetric constant error is a zero dimensional array: `error`.
        2. Asymmetric constant error is a one dimensional array: `[lower_error, upper_error]`.
        3. Symmetric error which varies for each point is an array with length equal
          to the number of points; each element is a zero dimensional array: `error`
        4. Asymmetric error which varies for each point is an array with length equal
          to the number of points; each element is a one dimensional array: `[lower_error, upper_error]`.

        This property can be set manually. If setting constant errors (cases 1 and 2 above),
        it is not necessary to explicitly use [`numpy.ndarray`][1] as the type will be converted automatically.

        For error that varies at each point (cases 3 and 4 above),
        the errors can be loaded from the file given by `scipy_data_fitting.Data.path`
        setting `scipy_data_fitting.Data.error_columns`.

        Defaults to `(None, None)` unless `scipy_data_fitting.Data.error_columns` is set,
        in which case this will be set on first access by calling `scipy_data_fitting.Data.load_error`.

        When loaded from file, the x and y values will be scaled according
        to `scipy_data_fitting.Data.scale`.

        Examples:

            #!python
            # (x_error, y_error)
            (0.1, 0.5)

            # (x_error, no y_error)
            (0.1, None)

            # ([x_lower_error, x_upper_error], y_error)
            ([0.1, 0.5], 2)

            # ([x_lower_error, x_upper_error], [y_lower_error, y_upper_error])
            ([0.1, 0.5], [2, 0.5])

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
        """
        if not hasattr(self, '_error'):
            if any(v is not None for v in self.error_columns):
                self._error = self.load_error()
            else:
                return (None, None)
        return self._error

    @error.setter
    def error(self, value):
        self._error = tuple( numpy.array(v) if v is not None else None for v in value )

    @property
    def scale(self):
        """
        Tuple `(x_scale, y_scale)` that defines how to scale data
        imported by `scipy_data_fitting.Data.load_data`
        and `scipy_data_fitting.Data.load_error`.

        If a scale is specified as a string, it will treated as a named physical constant
        and converted to the corresponding number using [`scipy.constants`][1].

        [1]: http://docs.scipy.org/doc/scipy/reference/constants.html
        """
        if not hasattr(self, '_scale'): self._scale = (1, 1)
        return self._scale

    @scale.setter
    def scale(self, value):
        self._scale = tuple( get_constant(v) for v in value )

    @property
    def error_columns(self):
        """
        Two element tuple that defines what columns in the file given
        by `scipy_data_fitting.Data.path` are the error values for each data point.

        The first element corresponds to the x error and the second to the y error.

        Each element is either an integer which gives the column,
        a two element tuple of integers, or `None`.

        In Python, indexes are zero-based, so the first column is `0`.

        Examples:

            #!python
            # (x_error, y_error)
            (2, 3)

            # (x_error, no y_error)
            (2, None)

            # ((x_lower_error, x_upper_error), y_error)
            ((2, 3), 4)

            # ((x_lower_error, x_upper_error), (y_lower_error, y_upper_error))
            ((2, 3), (4, 5))

        Defaults to `(None, None)`.
        """
        if not hasattr(self, '_error_columns'): return (None, None)
        return self._error_columns

    @error_columns.setter
    def error_columns(self, value):
        self._error_columns = value

    @property
    def genfromtxt_args(self):
        """
        Passed as keyword arguments to [`numpy.genfromtxt`][1]
        when called by `scipy_data_fitting.Data.load_data`.

        Default:

            #!python
            {
                'unpack': True,
                'delimiter': ',',
                'usecols': (0 ,1),
            }

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html
        """
        if not hasattr(self, '_genfromtxt_args'):
            self._genfromtxt_args = {
                'unpack': True,
                'delimiter': ',',
                'usecols': (0 ,1),
            }
        return self._genfromtxt_args

    @genfromtxt_args.setter
    def genfromtxt_args(self, value):
        self._genfromtxt_args = value

    @property
    def genfromtxt_args_error(self):
        """
        Passed as keyword arguments to [`numpy.genfromtxt`][1]
        when called by `scipy_data_fitting.Data.load_error`.

        Even if defined here, the `usecols` value will always be reset based
        on `scipy_data_fitting.Data.error_columns` before being passed to [`numpy.genfromtxt`][1].

        If not set, this defaults to a copy of
        `scipy_data_fitting.Data.genfromtxt_args` on first access.

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html
        """
        if not hasattr(self, '_genfromtxt_args_error'):
            self._genfromtxt_args_error = self.genfromtxt_args.copy()
        return self._genfromtxt_args_error

    @genfromtxt_args_error.setter
    def genfromtxt_args_error(self, value):
        self._genfromtxt_args_error = value

    def load_data(self):
        """
        Loads data from `scipy_data_fitting.Data.path` using [`numpy.genfromtxt`][1]
        and returns a [`numpy.ndarray`][2].

        Data is scaled according to `scipy_data_fitting.Data.scale`.

        Arguments to [`numpy.genfromtxt`][1] are controlled
        by `scipy_data_fitting.Data.genfromtxt_args`.

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html
        [2]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
        """
        array = numpy.genfromtxt(self.path, **self.genfromtxt_args)
        for n, scale in enumerate(self.scale): array[n,:] *= self.scale[n]
        return array

    def load_error(self):
        """
        Loads error values from `scipy_data_fitting.Data.path` using [`numpy.genfromtxt`][1]
        and returns a two element tuple where each element is of a form described by
        cases 3 and 4 in `scipy_data_fitting.Data.error`.

        The columns to import are set by `scipy_data_fitting.Data.error_columns`.

        Values are scaled according to `scipy_data_fitting.Data.scale`.

        Arguments to [`numpy.genfromtxt`][1] are controlled
        by `scipy_data_fitting.Data.genfromtxt_args_error`.

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html
        """
        usecols = []
        for v in self.error_columns:
            if v is None:
                pass
            elif isinstance(v, int):
                usecols.append(v)
            elif len(v) is 2:
                for n in v: usecols.append(n)
        self.genfromtxt_args_error['usecols'] = tuple(usecols)

        array = numpy.genfromtxt(self.path, **self.genfromtxt_args_error)

        error = []
        for n, v in enumerate(self.error_columns):
            if v is None:
                error.append(None)
            elif isinstance(v, int):
                if len(usecols) is 1:
                    error.append(array * self.scale[n])
                else:
                    error.append(array[0] * self.scale[n])
                    array = numpy.delete(array, (0), axis=(0))
            elif len(v) is 2:
                error.append(array[0:2] * self.scale[n])
                array = numpy.delete(array, (0, 1), axis=(0))

        return tuple(error)

Ancestors (in MRO)

  • Data
  • builtins.object

Static methods

def __init__(

self, name=None)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, name=None):
    self.name = name
    """
    The identifier name for this object.
    """

def load_data(

self)

Loads data from path using numpy.genfromtxt and returns a numpy.ndarray.

Data is scaled according to scale.

Arguments to numpy.genfromtxt are controlled by genfromtxt_args.

def load_data(self):
    """
    Loads data from `scipy_data_fitting.Data.path` using [`numpy.genfromtxt`][1]
    and returns a [`numpy.ndarray`][2].
    Data is scaled according to `scipy_data_fitting.Data.scale`.
    Arguments to [`numpy.genfromtxt`][1] are controlled
    by `scipy_data_fitting.Data.genfromtxt_args`.
    [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html
    [2]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
    """
    array = numpy.genfromtxt(self.path, **self.genfromtxt_args)
    for n, scale in enumerate(self.scale): array[n,:] *= self.scale[n]
    return array

def load_error(

self)

Loads error values from path using numpy.genfromtxt and returns a two element tuple where each element is of a form described by cases 3 and 4 in error.

The columns to import are set by error_columns.

Values are scaled according to scale.

Arguments to numpy.genfromtxt are controlled by genfromtxt_args_error.

def load_error(self):
    """
    Loads error values from `scipy_data_fitting.Data.path` using [`numpy.genfromtxt`][1]
    and returns a two element tuple where each element is of a form described by
    cases 3 and 4 in `scipy_data_fitting.Data.error`.
    The columns to import are set by `scipy_data_fitting.Data.error_columns`.
    Values are scaled according to `scipy_data_fitting.Data.scale`.
    Arguments to [`numpy.genfromtxt`][1] are controlled
    by `scipy_data_fitting.Data.genfromtxt_args_error`.
    [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html
    """
    usecols = []
    for v in self.error_columns:
        if v is None:
            pass
        elif isinstance(v, int):
            usecols.append(v)
        elif len(v) is 2:
            for n in v: usecols.append(n)
    self.genfromtxt_args_error['usecols'] = tuple(usecols)
    array = numpy.genfromtxt(self.path, **self.genfromtxt_args_error)
    error = []
    for n, v in enumerate(self.error_columns):
        if v is None:
            error.append(None)
        elif isinstance(v, int):
            if len(usecols) is 1:
                error.append(array * self.scale[n])
            else:
                error.append(array[0] * self.scale[n])
                array = numpy.delete(array, (0), axis=(0))
        elif len(v) is 2:
            error.append(array[0:2] * self.scale[n])
            array = numpy.delete(array, (0, 1), axis=(0))
    return tuple(error)

Instance variables

var array

Data as a numpy.ndarray in the form

[
    [ x1, x2, x3, ... ],
    [ y1, y2, y3, ...]
]

By default, if unset, this will be set on first access by calling load_data.

When loaded from file, the x and y values will be scaled according to scale.

var error

Error associated with the data as a two element tuple of the form (x_error, y_error).

Both x_error and y_error is a numpy.ndarray or None.

The array dimensions depend on how the error is specified:

  1. Symmetric constant error is a zero dimensional array: error.
  2. Asymmetric constant error is a one dimensional array: [lower_error, upper_error].
  3. Symmetric error which varies for each point is an array with length equal to the number of points; each element is a zero dimensional array: error
  4. Asymmetric error which varies for each point is an array with length equal to the number of points; each element is a one dimensional array: [lower_error, upper_error].

This property can be set manually. If setting constant errors (cases 1 and 2 above), it is not necessary to explicitly use numpy.ndarray as the type will be converted automatically.

For error that varies at each point (cases 3 and 4 above), the errors can be loaded from the file given by path setting error_columns.

Defaults to (None, None) unless error_columns is set, in which case this will be set on first access by calling load_error.

When loaded from file, the x and y values will be scaled according to scale.

Examples:

# (x_error, y_error)
(0.1, 0.5)

# (x_error, no y_error)
(0.1, None)

# ([x_lower_error, x_upper_error], y_error)
([0.1, 0.5], 2)

# ([x_lower_error, x_upper_error], [y_lower_error, y_upper_error])
([0.1, 0.5], [2, 0.5])

var error_columns

Two element tuple that defines what columns in the file given by path are the error values for each data point.

The first element corresponds to the x error and the second to the y error.

Each element is either an integer which gives the column, a two element tuple of integers, or None.

In Python, indexes are zero-based, so the first column is 0.

Examples:

# (x_error, y_error)
(2, 3)

# (x_error, no y_error)
(2, None)

# ((x_lower_error, x_upper_error), y_error)
((2, 3), 4)

# ((x_lower_error, x_upper_error), (y_lower_error, y_upper_error))
((2, 3), (4, 5))

Defaults to (None, None).

var genfromtxt_args

Passed as keyword arguments to numpy.genfromtxt when called by load_data.

Default:

{
    'unpack': True,
    'delimiter': ',',
    'usecols': (0 ,1),
}

var genfromtxt_args_error

Passed as keyword arguments to numpy.genfromtxt when called by load_error.

Even if defined here, the usecols value will always be reset based on error_columns before being passed to numpy.genfromtxt.

If not set, this defaults to a copy of genfromtxt_args on first access.

var name

The identifier name for this object.

var path

Path to the file containing data to load.

var scale

Tuple (x_scale, y_scale) that defines how to scale data imported by load_data and load_error.

If a scale is specified as a string, it will treated as a named physical constant and converted to the corresponding number using scipy.constants.

class Fit

Although not required at instantiation, data and model must be set to use most of this class.

Example:

>>> data = Data()
>>> model = Model()
>>> # set up the data and model objects
>>> fit = Fit('linear', data=data, model=model)
>>> fit.expression = 'line'
>>> fit.independent = {'symbol': 't', 'name': 'Time', 'units': 's'}
>>> fit.dependent = {'name': 'Distance', 'units': 'm'}
>>> fit.parameters = [
        {'symbol': 'x_0', 'value': 1, 'units': 'm'},
        {'symbol': 'v', 'guess': 1, 'units': 'm/s'},
    ]
>>>  fit.to_json(fit.name + '.json')
class Fit:
    """
    Although not required at instantiation,
    `scipy_data_fitting.Fit.data` and `scipy_data_fitting.Fit.model`
    must be set to use most of this class.

    Example:

        #!python
        >>> data = Data()
        >>> model = Model()
        >>> # set up the data and model objects
        >>> fit = Fit('linear', data=data, model=model)
        >>> fit.expression = 'line'
        >>> fit.independent = {'symbol': 't', 'name': 'Time', 'units': 's'}
        >>> fit.dependent = {'name': 'Distance', 'units': 'm'}
        >>> fit.parameters = [
                {'symbol': 'x_0', 'value': 1, 'units': 'm'},
                {'symbol': 'v', 'guess': 1, 'units': 'm/s'},
            ]
        >>>  fit.to_json(fit.name + '.json')
    """

    def __init__(self, name=None, data=None, model=None):
        self.name = name
        """
        The identifier name for this object.
        """
        self.data = data
        """
        The `scipy_data_fitting.Data` instance to use for the fit.
        """
        self.model = model
        """
        The `scipy_data_fitting.Model` instance to use for the fit.
        """

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value

    @property
    def model(self):
        return self._model

    @model.setter
    def model(self, value):
        self._model = value

    @property
    def description(self):
        """
        A short description for the fit.

        Will default to `scipy_data_fitting.Fit.name`.
        """
        if not hasattr(self, '_description'): return self.name
        return self._description

    @description.setter
    def description(self, value):
        self._description = value

    @property
    def options(self):
        """
        Dictionary of options which affect the curve fitting algorithm.

        Must contain the key `fit_function` which must be set to
        the function that will perform the fit.

        All other options are passed as keyword arguments to the `fit_function`.

        The default options use `scipy.optimize.curve_fit`.

        If `fit_function` has the special value `lmfit`, then [lmfit][1]
        is used for the fit and all other options are passed as keyword arguments
        to [`lmfit.minimize`][2].

        When using [lmfit][1], additional control of the fit is obtained by overriding
        `scipy_data_fitting.Fit.lmfit_fcn2min`.

        Any other function may be used for `fit_function` that satisfies the following criteria:

        * Must accept the following non-keyword arguments in this order
          (even if unused in the fitting function):

            1. Function to fit, see `scipy_data_fitting.Fit.function`.
            2. Independent values: see `scipy_data_fitting.Data.array`.
            3. Dependent values: see `scipy_data_fitting.Data.array`.
            4. List of the initial fitting parameter guesses in same order
               as given by `scipy_data_fitting.Fit.fitting_parameters`.
               The initial guesses will be scaled by their prefix before being passed.

        * Can accept any keyword arguments set in `scipy_data_fitting.Fit.options`.
          For example, this is how one could pass error values to the fitting function.

        * Must return an object whose first element is a list or array of the values
          of the fitted parameters (and only those values) in same order
          as given by `scipy_data_fitting.Fit.fitting_parameters`.

        Default:

            #!python
            {
                'fit_function': scipy.optimize.curve_fit,
                'maxfev': 1000,
            }

        [1]: http://lmfit.github.io/lmfit-py/
        [2]: http://lmfit.github.io/lmfit-py/fitting.html#the-minimize-function
        """
        if not hasattr(self, '_options'):
            self._options = {
                'fit_function': scipy.optimize.curve_fit,
                'maxfev': 1000,
            }
        return self._options

    @options.setter
    def options(self, value):
        self._options = value

    @property
    def lambdify_options(self):
        """
        Dictionary of options which are passed as keyword arguments
        to `scipy_data_fitting.Model.lambdify`.

        Default:

            #!python
            {'modules': 'numpy'}
        """
        if not hasattr(self, '_lambdify_options'):
            self._lambdify_options = {
                'modules': 'numpy',
            }
        return self._lambdify_options

    @lambdify_options.setter
    def lambdify_options(self, value):
        self._lambdify_options = value

    @property
    def limits(self):
        """
        Limits to use for the independent variable whenever
        creating a linespace, plot, etc.

        Defaults to `(-x, x)` where `x` is the largest absolute value
        of the data corresponding to the independent variable.
        If no such values are negative, defaults to `(0, x)` instead.
        """
        if not hasattr(self, '_limits'):
            xmax = max(abs(self.data.array[0]))
            xmin = min(self.data.array[0])

            x_error = self.data.error[0]
            if isinstance(x_error, numpy.ndarray):
                if x_error.ndim == 0: xmax = xmax + x_error

            if xmin < 0:
                self._limits = (-xmax, xmax)
            else:
                self._limits = (0, xmax)

        return self._limits

    @limits.setter
    def limits(self, value):
        self._limits = value

    @property
    def expression(self):
        """
        The name of the expression to use for the fit
        as defined in `scipy_data_fitting.Model.expressions`.
        """
        return self._expression

    @expression.setter
    def expression(self, value):
        self._expression = value

    @property
    def replacements(self):
        """
        A single replacement or replacement group,
        or a list of replacements and replacement groups
        that will be applied to the expression
        defined by `scipy_data_fitting.Fit.expression`.

        See also `scipy_data_fitting.Model.replace`.

        This defaults to `None`.
        """
        if not hasattr(self, '_replacements'): self._replacements = None
        return self._replacements

    @replacements.setter
    def replacements(self, value):
        self._replacements = value

    @property
    def dependent(self):
        """
        A dictionary that defines the dependent variable.

        The only required key is `symbol`
        which defines the corresponding SymPy symbol.

        `symbol` may be given as a SymPy symbol or the name of a symbol
        defined in `scipy_data_fitting.Model.symbols`.

        If a `prefix` is given (as a number or string), it will affect the scale when
        creating a linespace, plot, etc.

        When `prefix` is given as a string, it will be converted to a number from [`scipy.constants`][1].

        `name` and `units` are only for display purposes.

        Other keys can be added freely and will be available
        as metadata for the various output formats.

        Defaults to `{}`.

        Example:

            #!python
            {'symbol': 'V', 'name': 'Voltage', 'prefix': 'kilo', 'units': 'kV'}

        [1]: http://docs.scipy.org/doc/scipy/reference/constants.html
        """
        if not hasattr(self, '_dependent'): self._dependent = {}
        return self._dependent

    @dependent.setter
    def dependent(self, value):
        self._dependent = value

    @property
    def independent(self):
        """
        A dictionary that defines the independent variable.

        This is not required, but the possible keys are the same as
        the optional ones explained in `scipy_data_fitting.Fit.independent`.

        This defaults to `{}`.

        Example:

            #!python
            {'name': 'Time', 'prefix': 'milli', 'units': 'ms'}
        """
        if not hasattr(self, '_independent'): self._independent = {}
        return self._independent

    @independent.setter
    def independent(self, value):
        self._independent = value

    @property
    def free_variables(self):
        """
        Free variables are useful when `scipy_data_fitting.Model.lambdify` is insufficient.

        Any free variables will correspond to the first arguments of
        `scipy_data_fitting.Fit.function`.

        Any free variables must be resolved before attempting any fitting (see example).

        This defaults to `[]`.

        Example:

            #!python
            >>> fit.independent = {'symbol': 'x'}
            >>> fit.parameters = [{'symbol': 'm', 'guess': 5}]
            >>> fit.free_variables = ['t']
            >>> f = fit.function # f(t, x, m)
            >>> fit.function = lambda *args: f(2, *args)
        """
        if not hasattr(self, '_free_variables'): self._free_variables = []
        return self._free_variables

    @free_variables.setter
    def free_variables(self, value):
        self._free_variables = value

    @property
    def quantities(self):
        """
        Quantities will be computed from an expression using the fitted parameters.

        The only required element in each dictionary is `expression`
        which can be a SymPy expression, or an expression name
        from `scipy_data_fitting.Model.expressions`.

        The expressions must not contain the symbols corresponding to
        `scipy_data_fitting.Fit.free_variables`, `scipy_data_fitting.Fit.independent`,
        or `scipy_data_fitting.Fit.dependent`.

        The other keys are the same as the optional ones explained
        in `scipy_data_fitting.Fit.independent`.

        This defaults to `[]`.

        Example:

            #!python
            [{'expression': 'tau', 'name': 'Ï„', 'prefix': 'milli', 'units': 'ms'}]
        """
        if not hasattr(self, '_quantities'): self._quantities = []
        return self._quantities

    @quantities.setter
    def quantities(self, value):
        self._quantities = value

    @property
    def constants(self):
        """
        Use constants to associate symbols in expressions with numerical values
        when not specifying them as fixed parameters.

        Each constant must contain the keys `symbol` and `value`.

        `symbol` may be given as a SymPy symbol or the name of a symbol
        defined in `scipy_data_fitting.Model.symbols`.

        If a `prefix` is specified, the value will be multiplied by it before being used.

        The value (also prefix) is either numerical,
        or a string which will be converted to a number from [`scipy.constants`][1].

        This defaults to `[]`.

        Example:

            #!python
            [
                {'symbol': 'c', 'value': 'c'},
                {'symbol': 'a', 'value': 'Bohr radius'},
                {'symbol': 'M', 'value': 2, 'prefix': 'kilo'},
            ]

        [1]: http://docs.scipy.org/doc/scipy/reference/constants.html
        """
        if not hasattr(self, '_constants'): self._constants = []
        return self._constants

    @constants.setter
    def constants(self, value):
        self._constants = value

    @property
    def parameters(self):
        """
        Each parameter is must contain the key `symbol`
        and a key which is either `value` or `guess`.

        `symbol` may be given as a SymPy symbol or the name of a symbol
        defined in `scipy_data_fitting.Model.symbols`.

        When a `guess` is given, that parameter is treated as a fitting parameter
        and the `guess` is used as a starting point.

        When `value` is given, the given value is fixed.

        If a `prefix` is specified, the `value` or `guess` (or `min` and `max` in `limft`, see below)
        will be multiplied by it before being used.

        When `prefix` is given as a string, it will be converted to a number from [`scipy.constants`][1].

        When appearing in metadata, values will be scaled back by the prefix.

        In the example below, the value for `L` used in computation will be `0.003`
        but when used for display, it will appear as `3 mm`.

        `name` and `units` are only for display purposes.

        `lmfit` is an optional key which can be used when fitting with [lmfit][2].
        This only works for fitting parameters, i.e. when `guess` is given.

        `guess` will automatically be set as the [`lmfit.Parameter`][3] value
        when using [lmfit][2] even if the `lmfit` key is absent.

        The value of `lmfit` is a dictionary that will be passed as additional keyword arguments
        to [`lmfit.Parameters.add`][4] when building the corresponding [`lmfit.Parameters`][5] object.

        The values of `min` and `max`, if specified in the `limft` key,
        will be scaled by `prefix` before being used to add the parameter.

        Other keys can be added freely and will be available
        as metadata for the various output formats.

        Example:

            #!python
            [
                {'symbol': 'L', 'value': 3, 'prefix': 'milli', 'units': 'mm'},
                {'symbol': 'b', 'guess': 3, 'prefix': 'milli', 'units': 'mm'},
                {'symbol': 'm', 'guess': 3},
            ]

        [1]: http://docs.scipy.org/doc/scipy/reference/constants.html
        [2]: https://pypi.python.org/pypi/lmfit/
        [3]: http://lmfit.github.io/lmfit-py/parameters.html#Parameter
        [4]: http://lmfit.github.io/lmfit-py/parameters.html#add
        [5]: http://lmfit.github.io/lmfit-py/parameters.html#the-parameters-class
        """
        if not hasattr(self, '_parameters'): self._parameters = []
        return self._parameters

    @parameters.setter
    def parameters(self, value):
        self._parameters = value

    @property
    def fitting_parameters(self):
        """
        A list containing only elements of `scipy_data_fitting.Fit.parameters`
        which do not specify a `value` key.
        """
        return [ v for v in self.parameters if not 'value' in v ]

    @property
    def fixed_parameters(self):
        """
        A list containing only elements of `scipy_data_fitting.Fit.parameters`
        which do specify a `value` key.
        """
        return [ v for v in self.parameters if 'value' in v ]

    @property
    def expression(self):
        """
        The SymPy expression that will be used to generate
        the function that will be used for fitting.

        Any replacements defined in `scipy_data_fitting.Fit.replacements`
        are applied to the base expression before returning.

        This always returns a SymPy expression, but it may be set using a string,
        in which case the base expression will be looked up in `scipy_data_fitting.Model.expressions`.

        Example:

            #!python
            >>> fit.expression = 'line'
            >>> fit.expression # fit.model.expressions['line'] after replacements
        """
        return self.model.replace(self._expression, self.replacements)

    @expression.setter
    def expression(self, value):
        self._expression = value

    @property
    def all_variables(self):
        """
        A flat tuple of all symbols taken in order from the following:

        1. `scipy_data_fitting.Fit.free_variables`
        2. `scipy_data_fitting.Fit.independent`
        3. `scipy_data_fitting.Fit.fitting_parameters`
        4. `scipy_data_fitting.Fit.fixed_parameters`
        5. `scipy_data_fitting.Fit.constants`
        """
        variables = []
        variables.extend(self.free_variables)
        variables.append(self.independent['symbol'])
        variables.extend([ param['symbol'] for param in self.fitting_parameters ])
        variables.extend([ param['symbol'] for param in self.fixed_parameters ])
        variables.extend([ const['symbol'] for const in self.constants ])

        symbols = []
        for variable in variables:
            if isinstance(variable, str):
                symbols.append(self.model.symbol(variable))
            else:
                symbols.append(variable)

        return tuple(symbols)

    @property
    def fixed_values(self):
        """
        A flat tuple of all values corresponding to `scipy_data_fitting.Fit.fixed_parameters`
        and `scipy_data_fitting.Fit.constants` after applying any prefixes.

        The values mimic the order of those lists.
        """
        values = []
        values.extend([ prefix_factor(param) * param['value'] for param in self.fixed_parameters ])
        values.extend([ prefix_factor(const) * get_constant(const['value']) for const in self.constants ])

        return tuple(values)

    @property
    def function(self):
        """
        The function passed to the `fit_function` specified in `scipy_data_fitting.Fit.options`,
        and used by `scipy_data_fitting.Fit.pointspace` to generate plots, etc.

        Its number of arguments and their order is determined by items 1, 2, and 3
        as listed in `scipy_data_fitting.Fit.all_variables`.

        All parameter values will be multiplied by their corresponding prefix before being passed to this function.

        By default, it is a functional form of `scipy_data_fitting.Fit.expression` converted
        using `scipy_data_fitting.Model.lambdify`.

        See also `scipy_data_fitting.Fit.lambdify_options`.
        """
        if not hasattr(self,'_function'):
            function = self.model.lambdify(self.expression, self.all_variables, **self.lambdify_options)
            self._function = lambda *x: function(*(x + self.fixed_values))
        return self._function

    @function.setter
    def function(self, value):
        self._function = value

    @property
    def lmfit_parameters(self):
        """
        A [`lmfit.Parameters`][1] object built from `scipy_data_fitting.Fit.fitting_parameters`,
        see `scipy_data_fitting.Fit.parameters`.

        Each parameters is assigned a key of the form `p_00000`, `p_00001`, `p_00002`, etc.
        Thus, `sorted(self.lmfit_parameters)` will give the keys in the same
        order defined by `scipy_data_fitting.Fit.fitting_parameters`.

        Parameter values are scaled by `prefix` before assignment.

        The values of `min` and `max`, if specified in the `limft` key,
        will be scaled by `prefix` before being used to add the parameter.

        [1]: http://lmfit.github.io/lmfit-py/parameters.html#the-parameters-class
        """
        p0 = []
        for param in self.fitting_parameters:
            opts = param['lmfit'].copy() if 'lmfit' in param else {}
            if 'min' in opts: opts['min'] = prefix_factor(param) * opts['min']
            if 'max' in opts: opts['max'] = prefix_factor(param) * opts['max']
            p0.append((prefix_factor(param) * param['guess'], opts))

        params = lmfit.Parameters()
        for p in zip(itertools.count(), p0):
            params.add('p_' + "%05d" % p[0], value=p[1][0], **p[1][1])

        return params

    @property
    def lmfit_fcn2min(self):
        """
        The function to minimize when using [lmfit][1].

        If overriding this, the replacement function must accept the following
        non-keyword arguments in this order (even if unused):

        1. A [`lmfit.Parameters`][2] object.
           The value of each parameter must be passed appropriately to `scipy_data_fitting.Fit.function`
           in the order determined by sorting the parameter keys alphabetically.
           Use `scipy_data_fitting.Fit.lmfit_parameter_values` to get the ordered numerical parameter values.
        2. Independent values: see `scipy_data_fitting.Data.array`.
        3. Dependent values: see `scipy_data_fitting.Data.array`.
        4. The error: see `scipy_data_fitting.Data.error`.


        The default function computes the difference between the evaluated function and the data.

        Default example:

            #!python
            lambda params, x, data, error: self.function(x, *self.lmfit_parameter_values(params)) - data

        [1]: https://pypi.python.org/pypi/lmfit/
        [2]: http://lmfit.github.io/lmfit-py/parameters.html#the-parameters-class
        [3]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
        """
        return lambda params, x, data, error: self.function(x, *self.lmfit_parameter_values(params)) - data

    @property
    def curve_fit(self):
        """
        Fits `scipy_data_fitting.Fit.function` to the data and returns
        the output from the specified curve fit function.

        See `scipy_data_fitting.Fit.options` for details on how to control
        or override the the curve fitting algorithm.
        """
        if not hasattr(self,'_curve_fit'):
            options = self.options.copy()
            fit_function = options.pop('fit_function')
            independent_values = self.data.array[0]
            dependent_values = self.data.array[1]

            if fit_function == 'lmfit':
                self._curve_fit = lmfit.minimize(
                    self.lmfit_fcn2min, self.lmfit_parameters,
                    args=(independent_values, dependent_values, self.data.error), **options)
            else:
                p0 = [ prefix_factor(param) * param['guess'] for param in self.fitting_parameters ]
                self._curve_fit = fit_function(
                    self.function, independent_values, dependent_values, p0, **options)

        return self._curve_fit

    @property
    def fitted_parameters(self):
        """
        A tuple of fitted values for the `scipy_data_fitting.Fit.fitting_parameters`.

        The values in this tuple are not scaled by the prefix,
        as they are passed back to `scipy_data_fitting.Fit.function`,
        e.g. in most standard use cases these would be the SI values.

        If no fitting parameters were specified, this will just return an empty tuple.
        """
        if hasattr(self,'_fitted_parameters'): return self._fitted_parameters
        if not self.fitting_parameters: return tuple()
        if self.options['fit_function'] == 'lmfit':
            return tuple( self.curve_fit.params[key] for key in sorted(self.curve_fit.params) )
        else:
            return tuple(self.curve_fit[0])

    @property
    def fitted_function(self):
        """
        A function of the single independent variable after
        partially evaluating `scipy_data_fitting.Fit.function` at
        the `scipy_data_fitting.Fit.fitted_parameters`.
        """
        function = self.function
        fitted_parameters = self.fitted_parameters
        return lambda x: function(x, *fitted_parameters)

    def clear_fit(self):
        """
        For performance, the function and results of the curve fit are saved in
        `scipy_data_fitting.Fit._function`, `scipy_data_fitting.Fit._fitted_parameters`,
        and `scipy_data_fitting.Fit._curve_fit`.

        This clears these attributes.
        """
        del self._function
        del self._curve_fit
        del self._fitted_parameters

    @property
    def computed_quantities(self):
        """
        A list of the quantities defined in `scipy_data_fitting.Fit.quantities`
        evaluated with `scipy_data_fitting.Fit.fitted_parameters`.

        The list is identical to what is set with `scipy_data_fitting.Fit.quantities`
        but in each dictionary, the key `expression` is removed,
        and the key `value` is added with the value of the quantity.

        The quantity is computed using values multiplied by their prefix as in
        `scipy_data_fitting.Fit.function`. Once computed, the reported value is
        scaled by the inverse prefix.
        """
        return [ self.compute_quantity(quantity) for quantity in self.quantities ]

    def compute_quantity(self, quantity):
        """
        Processes a single quantity as described and used
        in `scipy_data_fitting.Fit.computed_quantities`.
        """
        quantity = quantity.copy()
        expression = self.model.replace(quantity.pop('expression'), self.replacements)
        variables = self.all_variables[len(self.free_variables) + 1:]
        function = self.model.lambdify(expression, variables, **self.lambdify_options)
        quantity['value'] = function(*(self.fitted_parameters + self.fixed_values)) * prefix_factor(quantity)**(-1)
        return quantity

    @property
    def computed_fitting_parameters(self):
        """
        A list identical to what is set with `scipy_data_fitting.Fit.fitting_parameters`,
        but in each dictionary, the key `value` is added with the fitted value of the quantity.
        The reported value is scaled by the inverse prefix.
        """
        fitted_parameters = []
        for (i, v) in enumerate(self.fitting_parameters):
            param = v.copy()
            param['value'] = self.fitted_parameters[i] * prefix_factor(param)**(-1)
            fitted_parameters.append(param)

        return fitted_parameters

    @property
    def metadata(self):
        """
        A dictionary which summarizes the results of the fit:

            #!python
            {
                'name': self.name,
                'description': self.description,
                'independent': self.independent,
                'dependent': self.dependent,
                'quantities': self.computed_quantities,
                'fixed_parameters': self.fixed_parameters,
                'fitted_parameters': self.computed_fitting_parameters,
            }
        """
        return {
          'name': self.name,
          'description': self.description,
          'independent': self.independent,
          'dependent': self.dependent,
          'quantities': self.computed_quantities,
          'fixed_parameters': self.fixed_parameters,
          'fitted_parameters': self.computed_fitting_parameters,
        }

    def pointspace(self, **kwargs):
        """
        Returns a dictionary with the keys `data` and `fit`.

        `data` is just `scipy_data_fitting.Data.array`.

        `fit` is a two row [`numpy.ndarray`][1], the first row values correspond
        to the independent variable and are generated using [`numpy.linspace`][2].
        The second row are the values of `scipy_data_fitting.Fit.fitted_function`
        evaluated on the linspace.

        For both `fit` and `data`, each row will be scaled by the corresponding
        inverse prefix if given in `scipy_data_fitting.Fit.independent`
        or `scipy_data_fitting.Fit.dependent`.

        Any keyword arguments are passed to [`numpy.linspace`][2].

        [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
        [2]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html
        """
        scale_array = numpy.array([
            [prefix_factor(self.independent)**(-1)],
            [prefix_factor(self.dependent)**(-1)]
        ])

        linspace = numpy.linspace(self.limits[0], self.limits[1], **kwargs)
        return {
          'data': self.data.array * scale_array,
          'fit': numpy.array([linspace, self.fitted_function(linspace)]) * scale_array
        }

    def to_json(self, path, points=50, meta=None):
        """
        Write the results of the fit to a json file at `path`.

        `points` will define the length of the `fit` array.

        If `meta` is given, a `meta` key be added with the given value.

        The json object has the form

            #!text
            {
                'data': [ [x1, y1], [x2, y2], ... ],
                'fit': [ [x1, y1], [x2, y2], ... ],
                'meta': meta
            }
        """
        pointspace = self.pointspace(num=points)
        fit_points = numpy.dstack(pointspace['fit'])[0]
        data_points = numpy.dstack(pointspace['data'])[0]

        fit = [ [ point[0],  point[1] ] for point in fit_points ]
        data = [ [ point[0],  point[1] ] for point in data_points ]

        obj = {'data': data, 'fit': fit}
        if meta: obj['meta'] = meta

        f = open(path, 'w')
        json.dump(obj, f)
        f.close

    def lmfit_parameter_values(self, params):
        """
        `params` is a [`lmfit.Parameters`][1] object.

        Returns a tuple containing the values of the parameters in `params`.

        The order is determined by sorting the parameter keys alphabetically.

        [1]: http://lmfit.github.io/lmfit-py/parameters.html#the-parameters-class

        """
        return tuple( params[key].value for key in sorted(params) )

Ancestors (in MRO)

  • Fit
  • builtins.object

Static methods

def __init__(

self, name=None, data=None, model=None)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, name=None, data=None, model=None):
    self.name = name
    """
    The identifier name for this object.
    """
    self.data = data
    """
    The `scipy_data_fitting.Data` instance to use for the fit.
    """
    self.model = model
    """
    The `scipy_data_fitting.Model` instance to use for the fit.
    """

def clear_fit(

self)

For performance, the function and results of the curve fit are saved in scipy_data_fitting.Fit._function, scipy_data_fitting.Fit._fitted_parameters, and scipy_data_fitting.Fit._curve_fit.

This clears these attributes.

def clear_fit(self):
    """
    For performance, the function and results of the curve fit are saved in
    `scipy_data_fitting.Fit._function`, `scipy_data_fitting.Fit._fitted_parameters`,
    and `scipy_data_fitting.Fit._curve_fit`.
    This clears these attributes.
    """
    del self._function
    del self._curve_fit
    del self._fitted_parameters

def compute_quantity(

self, quantity)

Processes a single quantity as described and used in computed_quantities.

def compute_quantity(self, quantity):
    """
    Processes a single quantity as described and used
    in `scipy_data_fitting.Fit.computed_quantities`.
    """
    quantity = quantity.copy()
    expression = self.model.replace(quantity.pop('expression'), self.replacements)
    variables = self.all_variables[len(self.free_variables) + 1:]
    function = self.model.lambdify(expression, variables, **self.lambdify_options)
    quantity['value'] = function(*(self.fitted_parameters + self.fixed_values)) * prefix_factor(quantity)**(-1)
    return quantity

def lmfit_parameter_values(

self, params)

params is a lmfit.Parameters object.

Returns a tuple containing the values of the parameters in params.

The order is determined by sorting the parameter keys alphabetically.

def lmfit_parameter_values(self, params):
    """
    `params` is a [`lmfit.Parameters`][1] object.
    Returns a tuple containing the values of the parameters in `params`.
    The order is determined by sorting the parameter keys alphabetically.
    [1]: http://lmfit.github.io/lmfit-py/parameters.html#the-parameters-class
    """
    return tuple( params[key].value for key in sorted(params) )

def pointspace(

self, **kwargs)

Returns a dictionary with the keys data and fit.

data is just array.

fit is a two row numpy.ndarray, the first row values correspond to the independent variable and are generated using numpy.linspace. The second row are the values of fitted_function evaluated on the linspace.

For both fit and data, each row will be scaled by the corresponding inverse prefix if given in independent or dependent.

Any keyword arguments are passed to numpy.linspace.

def pointspace(self, **kwargs):
    """
    Returns a dictionary with the keys `data` and `fit`.
    `data` is just `scipy_data_fitting.Data.array`.
    `fit` is a two row [`numpy.ndarray`][1], the first row values correspond
    to the independent variable and are generated using [`numpy.linspace`][2].
    The second row are the values of `scipy_data_fitting.Fit.fitted_function`
    evaluated on the linspace.
    For both `fit` and `data`, each row will be scaled by the corresponding
    inverse prefix if given in `scipy_data_fitting.Fit.independent`
    or `scipy_data_fitting.Fit.dependent`.
    Any keyword arguments are passed to [`numpy.linspace`][2].
    [1]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
    [2]: http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html
    """
    scale_array = numpy.array([
        [prefix_factor(self.independent)**(-1)],
        [prefix_factor(self.dependent)**(-1)]
    ])
    linspace = numpy.linspace(self.limits[0], self.limits[1], **kwargs)
    return {
      'data': self.data.array * scale_array,
      'fit': numpy.array([linspace, self.fitted_function(linspace)]) * scale_array
    }

def to_json(

self, path, points=50, meta=None)

Write the results of the fit to a json file at path.

points will define the length of the fit array.

If meta is given, a meta key be added with the given value.

The json object has the form

{
    'data': [ [x1, y1], [x2, y2], ... ],
    'fit': [ [x1, y1], [x2, y2], ... ],
    'meta': meta
}
def to_json(self, path, points=50, meta=None):
    """
    Write the results of the fit to a json file at `path`.
    `points` will define the length of the `fit` array.
    If `meta` is given, a `meta` key be added with the given value.
    The json object has the form
        #!text
        {
            'data': [ [x1, y1], [x2, y2], ... ],
            'fit': [ [x1, y1], [x2, y2], ... ],
            'meta': meta
        }
    """
    pointspace = self.pointspace(num=points)
    fit_points = numpy.dstack(pointspace['fit'])[0]
    data_points = numpy.dstack(pointspace['data'])[0]
    fit = [ [ point[0],  point[1] ] for point in fit_points ]
    data = [ [ point[0],  point[1] ] for point in data_points ]
    obj = {'data': data, 'fit': fit}
    if meta: obj['meta'] = meta
    f = open(path, 'w')
    json.dump(obj, f)
    f.close

Instance variables

var all_variables

A flat tuple of all symbols taken in order from the following:

  1. free_variables
  2. independent
  3. fitting_parameters
  4. fixed_parameters
  5. constants

var computed_fitting_parameters

A list identical to what is set with fitting_parameters, but in each dictionary, the key value is added with the fitted value of the quantity. The reported value is scaled by the inverse prefix.

var computed_quantities

A list of the quantities defined in quantities evaluated with fitted_parameters.

The list is identical to what is set with quantities but in each dictionary, the key expression is removed, and the key value is added with the value of the quantity.

The quantity is computed using values multiplied by their prefix as in function. Once computed, the reported value is scaled by the inverse prefix.

var constants

Use constants to associate symbols in expressions with numerical values when not specifying them as fixed parameters.

Each constant must contain the keys symbol and value.

symbol may be given as a SymPy symbol or the name of a symbol defined in symbols.

If a prefix is specified, the value will be multiplied by it before being used.

The value (also prefix) is either numerical, or a string which will be converted to a number from scipy.constants.

This defaults to [].

Example:

[
    {'symbol': 'c', 'value': 'c'},
    {'symbol': 'a', 'value': 'Bohr radius'},
    {'symbol': 'M', 'value': 2, 'prefix': 'kilo'},
]

var curve_fit

Fits function to the data and returns the output from the specified curve fit function.

See options for details on how to control or override the the curve fitting algorithm.

var data

The Data instance to use for the fit.

var dependent

A dictionary that defines the dependent variable.

The only required key is symbol which defines the corresponding SymPy symbol.

symbol may be given as a SymPy symbol or the name of a symbol defined in symbols.

If a prefix is given (as a number or string), it will affect the scale when creating a linespace, plot, etc.

When prefix is given as a string, it will be converted to a number from scipy.constants.

name and units are only for display purposes.

Other keys can be added freely and will be available as metadata for the various output formats.

Defaults to {}.

Example:

{'symbol': 'V', 'name': 'Voltage', 'prefix': 'kilo', 'units': 'kV'}

var description

A short description for the fit.

Will default to name.

var expression

The SymPy expression that will be used to generate the function that will be used for fitting.

Any replacements defined in replacements are applied to the base expression before returning.

This always returns a SymPy expression, but it may be set using a string, in which case the base expression will be looked up in expressions.

Example:

>>> fit.expression = 'line'
>>> fit.expression # fit.model.expressions['line'] after replacements

var fitted_function

A function of the single independent variable after partially evaluating function at the fitted_parameters.

var fitted_parameters

A tuple of fitted values for the fitting_parameters.

The values in this tuple are not scaled by the prefix, as they are passed back to function, e.g. in most standard use cases these would be the SI values.

If no fitting parameters were specified, this will just return an empty tuple.

var fitting_parameters

A list containing only elements of parameters which do not specify a value key.

var fixed_parameters

A list containing only elements of parameters which do specify a value key.

var fixed_values

A flat tuple of all values corresponding to fixed_parameters and constants after applying any prefixes.

The values mimic the order of those lists.

var free_variables

Free variables are useful when lambdify is insufficient.

Any free variables will correspond to the first arguments of function.

Any free variables must be resolved before attempting any fitting (see example).

This defaults to [].

Example:

>>> fit.independent = {'symbol': 'x'}
>>> fit.parameters = [{'symbol': 'm', 'guess': 5}]
>>> fit.free_variables = ['t']
>>> f = fit.function # f(t, x, m)
>>> fit.function = lambda *args: f(2, *args)

var function

The function passed to the fit_function specified in options, and used by pointspace to generate plots, etc.

Its number of arguments and their order is determined by items 1, 2, and 3 as listed in all_variables.

All parameter values will be multiplied by their corresponding prefix before being passed to this function.

By default, it is a functional form of expression converted using lambdify.

See also lambdify_options.

var independent

A dictionary that defines the independent variable.

This is not required, but the possible keys are the same as the optional ones explained in independent.

This defaults to {}.

Example:

{'name': 'Time', 'prefix': 'milli', 'units': 'ms'}

var lambdify_options

Dictionary of options which are passed as keyword arguments to lambdify.

Default:

{'modules': 'numpy'}

var limits

Limits to use for the independent variable whenever creating a linespace, plot, etc.

Defaults to (-x, x) where x is the largest absolute value of the data corresponding to the independent variable. If no such values are negative, defaults to (0, x) instead.

var lmfit_fcn2min

The function to minimize when using lmfit.

If overriding this, the replacement function must accept the following non-keyword arguments in this order (even if unused):

  1. A lmfit.Parameters object. The value of each parameter must be passed appropriately to function in the order determined by sorting the parameter keys alphabetically. Use lmfit_parameter_values to get the ordered numerical parameter values.
  2. Independent values: see array.
  3. Dependent values: see array.
  4. The error: see error.

The default function computes the difference between the evaluated function and the data.

Default example:

lambda params, x, data, error: self.function(x, *self.lmfit_parameter_values(params)) - data

var lmfit_parameters

A lmfit.Parameters object built from fitting_parameters, see parameters.

Each parameters is assigned a key of the form p_00000, p_00001, p_00002, etc. Thus, sorted(self.lmfit_parameters) will give the keys in the same order defined by fitting_parameters.

Parameter values are scaled by prefix before assignment.

The values of min and max, if specified in the limft key, will be scaled by prefix before being used to add the parameter.

var metadata

A dictionary which summarizes the results of the fit:

{
    'name': self.name,
    'description': self.description,
    'independent': self.independent,
    'dependent': self.dependent,
    'quantities': self.computed_quantities,
    'fixed_parameters': self.fixed_parameters,
    'fitted_parameters': self.computed_fitting_parameters,
}

var model

The Model instance to use for the fit.

var name

The identifier name for this object.

var options

Dictionary of options which affect the curve fitting algorithm.

Must contain the key fit_function which must be set to the function that will perform the fit.

All other options are passed as keyword arguments to the fit_function.

The default options use scipy.optimize.curve_fit.

If fit_function has the special value lmfit, then lmfit is used for the fit and all other options are passed as keyword arguments to lmfit.minimize.

When using lmfit, additional control of the fit is obtained by overriding lmfit_fcn2min.

Any other function may be used for fit_function that satisfies the following criteria:

  • Must accept the following non-keyword arguments in this order (even if unused in the fitting function):

    1. Function to fit, see function.
    2. Independent values: see array.
    3. Dependent values: see array.
    4. List of the initial fitting parameter guesses in same order as given by fitting_parameters. The initial guesses will be scaled by their prefix before being passed.
  • Can accept any keyword arguments set in options. For example, this is how one could pass error values to the fitting function.

  • Must return an object whose first element is a list or array of the values of the fitted parameters (and only those values) in same order as given by fitting_parameters.

Default:

{
    'fit_function': scipy.optimize.curve_fit,
    'maxfev': 1000,
}

var parameters

Each parameter is must contain the key symbol and a key which is either value or guess.

symbol may be given as a SymPy symbol or the name of a symbol defined in symbols.

When a guess is given, that parameter is treated as a fitting parameter and the guess is used as a starting point.

When value is given, the given value is fixed.

If a prefix is specified, the value or guess (or min and max in limft, see below) will be multiplied by it before being used.

When prefix is given as a string, it will be converted to a number from scipy.constants.

When appearing in metadata, values will be scaled back by the prefix.

In the example below, the value for L used in computation will be 0.003 but when used for display, it will appear as 3 mm.

name and units are only for display purposes.

lmfit is an optional key which can be used when fitting with lmfit. This only works for fitting parameters, i.e. when guess is given.

guess will automatically be set as the lmfit.Parameter value when using lmfit even if the lmfit key is absent.

The value of lmfit is a dictionary that will be passed as additional keyword arguments to lmfit.Parameters.add when building the corresponding lmfit.Parameters object.

The values of min and max, if specified in the limft key, will be scaled by prefix before being used to add the parameter.

Other keys can be added freely and will be available as metadata for the various output formats.

Example:

[
    {'symbol': 'L', 'value': 3, 'prefix': 'milli', 'units': 'mm'},
    {'symbol': 'b', 'guess': 3, 'prefix': 'milli', 'units': 'mm'},
    {'symbol': 'm', 'guess': 3},
]

var quantities

Quantities will be computed from an expression using the fitted parameters.

The only required element in each dictionary is expression which can be a SymPy expression, or an expression name from expressions.

The expressions must not contain the symbols corresponding to free_variables, independent, or dependent.

The other keys are the same as the optional ones explained in independent.

This defaults to [].

Example:

[{'expression': 'tau', 'name': 'Ï„', 'prefix': 'milli', 'units': 'ms'}]

var replacements

A single replacement or replacement group, or a list of replacements and replacement groups that will be applied to the expression defined by expression.

See also replace.

This defaults to None.

class Model

A model organizes symbols, expressions and replacements rules by name.

Example:

>>> model = Model()
>>> model.add_symbols('y', 'x', 'm', 'b')
>>> y, m, x, b = model.get_symbols('y', 'x', 'm', 'b')
>>> model.expressions['line'] = y
>>> model.replacements['slope_intercept'] = (y, m * x + b)
>>> line = model.replace('line', 'slope_intercept')
m * x + b
>>> function = model.lambdify(line, ('m', 'x', 'b'))
>>> function(1, 2, 3)
5
class Model:
    """
    A model organizes symbols, expressions and replacements rules by name.

    Example:

        #!python
        >>> model = Model()
        >>> model.add_symbols('y', 'x', 'm', 'b')
        >>> y, m, x, b = model.get_symbols('y', 'x', 'm', 'b')
        >>> model.expressions['line'] = y
        >>> model.replacements['slope_intercept'] = (y, m * x + b)
        >>> line = model.replace('line', 'slope_intercept')
        m * x + b
        >>> function = model.lambdify(line, ('m', 'x', 'b'))
        >>> function(1, 2, 3)
        5
    """

    def __init__(self, name=None):
        self.name = name
        """
        The identifier name for this object.
        """

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def expressions(self):
        """
        Dictionary to store SymPy expressions by name.
        """
        if not hasattr(self, '_expressions'): self._expressions = {}
        return self._expressions

    @expressions.setter
    def expressions(self, value):
        self._expressions = value

    @property
    def replacements(self):
        """
        Dictionary to store replacement rules by name.
        Each value is a tuple of SymPy expressions: `(expression, replacement)`.
        """
        if not hasattr(self, '_replacements'): self._replacements = {}
        return self._replacements

    @replacements.setter
    def replacements(self, value):
        self._replacements = value

    @property
    def replacement_groups(self):
        """
        Dictionary to store a sequence of replacements by name.
        Each value is a list of names that will be looked up
        in `scipy_data_fitting.Model.replacements`.

        When used to make substitutions, replacements will be applied
        one at a time in the order given.
        """
        if not hasattr(self, '_replacement_groups'): self._replacement_groups = {}
        return self._replacement_groups

    @replacement_groups.setter
    def replacement_groups(self, value):
        self._replacement_groups = value

    @property
    def symbols(self):
        """
        Dictionary to store symbols by name.

        Add symbols directly, or with `scipy_data_fitting.Model.add_symbol`
        and `scipy_data_fitting.Model.add_symbols`.
        """
        if not hasattr(self, '_symbols'): self._symbols = {}
        return self._symbols

    @symbols.setter
    def symbols(self, value):
        self._symbols = value

    def symbol(self, name):
        """
        Function to provide a shorthand for `self.symbols[name]`.
        """
        return self.symbols[name]

    def add_symbol(self, name, string=None):
        """
        Add a symbol with key `name` to `scipy_data_fitting.Model.symbols`.
        Optionally, specify an alternative `string` to pass to [`sympy.Symbol`][1],
        otherwise `name` is used.

        [1]: http://docs.sympy.org/dev/modules/core.html#id4
        """
        if not string: string = name
        self.symbols[name] = sympy.Symbol(string)

    def add_symbols(self, *names):
        """
        Pass any number of strings to add symbols to `scipy_data_fitting.Model.symbols`
        using `scipy_data_fitting.Model.add_symbol`.

        Example:

            #!python
            >>> model.add_symbols('x', 'y', 'z')
        """
        for name in names:
            self.add_symbol(name)

    def get_symbols(self, *symbols):
        """
        A tuple of symbols by name.

        Example:

            #!python
            >>> x, y, z = model.get_symbols('x', 'y', 'z')
        """
        return ( self.symbol(s) for s in symbols )

    def replace(self, expression, replacements):
        """
        All purpose method to reduce an expression by applying
        successive replacement rules.

        `expression` is either a SymPy expression
        or a key in `scipy_data_fitting.Model.expressions`.

        `replacements` can be any of the following,
        or a list of any combination of the following:

        - A replacement tuple as in `scipy_data_fitting.Model.replacements`.
        - The name of a replacement in `scipy_data_fitting.Model.replacements`.
        - The name of a replacement group in `scipy_data_fitting.Model.replacement_groups`.

        Examples:

            #!python
            >>> model.replace(x + y, (x, z))
            z + y

            >>> model.replace('expression', (x, z))
            >>> model.replace('expression', 'replacement')
            >>> model.replace('expression', ['replacement_1', 'replacement_2'])
            >>> model.replace('expression', ['replacement', 'group'])
        """
        # When expression is a string,
        # get the expressions from self.expressions.
        if isinstance(expression, str):
            expression = self.expressions[expression]

        # Allow for replacements to be empty.
        if not replacements:
            return expression

        # Allow replacements to be a string.
        if isinstance(replacements, str):
            if replacements in self.replacements:
                return self.replace(expression, self.replacements[replacements])
            elif replacements in self.replacement_groups:
                return self.replace(expression, self.replacement_groups[replacements])

        # When replacements is a list of strings or tuples,
        # Use reduce to make all the replacements.
        if all(isinstance(item, str) for item in replacements) \
        or all(isinstance(item, tuple) for item in replacements):
            return functools.reduce(self.replace, replacements, expression)

        # Otherwise make the replacement.
        return expression.replace(*replacements)

    def lambdify(self, expression, symbols, **kwargs):
        """
        Converts a SymPy expression into a function using [`sympy.lambdify`][1].

        `expression` can be a SymPy expression or the name of an expression
        in `scipy_data_fitting.Model.expressions`.

        `symbols` can be any of the following,
        or a list of any combination of the following:

        - A SymPy symbol.
        - The name of a symbol in `scipy_data_fitting.Model.symbols`.

        Additional keyword arguments are passed to [`sympy.lambdify`][1].

        [1]: http://docs.sympy.org/latest/modules/utilities/lambdify.html#sympy.utilities.lambdify.lambdify
        """
        if isinstance(expression, str):
            expression = self.expressions[expression]

        if hasattr(symbols, '__iter__'):
            variables = []
            for s in symbols:
                if isinstance(s, str):
                    variables.append(self.symbol(s))
                else:
                    variables.append(s)
        else:
            if isinstance(symbols, str):
                variables = (self.symbol(symbols), )
            else:
                variables = (symbols, )

        return sympy.lambdify(tuple(variables), expression, **kwargs)

Ancestors (in MRO)

Static methods

def __init__(

self, name=None)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, name=None):
    self.name = name
    """
    The identifier name for this object.
    """

def add_symbol(

self, name, string=None)

Add a symbol with key name to symbols. Optionally, specify an alternative string to pass to sympy.Symbol, otherwise name is used.

def add_symbol(self, name, string=None):
    """
    Add a symbol with key `name` to `scipy_data_fitting.Model.symbols`.
    Optionally, specify an alternative `string` to pass to [`sympy.Symbol`][1],
    otherwise `name` is used.
    [1]: http://docs.sympy.org/dev/modules/core.html#id4
    """
    if not string: string = name
    self.symbols[name] = sympy.Symbol(string)

def add_symbols(

self, *names)

Pass any number of strings to add symbols to symbols using add_symbol.

Example:

>>> model.add_symbols('x', 'y', 'z')
def add_symbols(self, *names):
    """
    Pass any number of strings to add symbols to `scipy_data_fitting.Model.symbols`
    using `scipy_data_fitting.Model.add_symbol`.
    Example:
        #!python
        >>> model.add_symbols('x', 'y', 'z')
    """
    for name in names:
        self.add_symbol(name)

def get_symbols(

self, *symbols)

A tuple of symbols by name.

Example:

>>> x, y, z = model.get_symbols('x', 'y', 'z')
def get_symbols(self, *symbols):
    """
    A tuple of symbols by name.
    Example:
        #!python
        >>> x, y, z = model.get_symbols('x', 'y', 'z')
    """
    return ( self.symbol(s) for s in symbols )

def lambdify(

self, expression, symbols, **kwargs)

Converts a SymPy expression into a function using sympy.lambdify.

expression can be a SymPy expression or the name of an expression in expressions.

symbols can be any of the following, or a list of any combination of the following:

  • A SymPy symbol.
  • The name of a symbol in symbols.

Additional keyword arguments are passed to sympy.lambdify.

def lambdify(self, expression, symbols, **kwargs):
    """
    Converts a SymPy expression into a function using [`sympy.lambdify`][1].
    `expression` can be a SymPy expression or the name of an expression
    in `scipy_data_fitting.Model.expressions`.
    `symbols` can be any of the following,
    or a list of any combination of the following:
    - A SymPy symbol.
    - The name of a symbol in `scipy_data_fitting.Model.symbols`.
    Additional keyword arguments are passed to [`sympy.lambdify`][1].
    [1]: http://docs.sympy.org/latest/modules/utilities/lambdify.html#sympy.utilities.lambdify.lambdify
    """
    if isinstance(expression, str):
        expression = self.expressions[expression]
    if hasattr(symbols, '__iter__'):
        variables = []
        for s in symbols:
            if isinstance(s, str):
                variables.append(self.symbol(s))
            else:
                variables.append(s)
    else:
        if isinstance(symbols, str):
            variables = (self.symbol(symbols), )
        else:
            variables = (symbols, )
    return sympy.lambdify(tuple(variables), expression, **kwargs)

def replace(

self, expression, replacements)

All purpose method to reduce an expression by applying successive replacement rules.

expression is either a SymPy expression or a key in expressions.

replacements can be any of the following, or a list of any combination of the following:

Examples:

>>> model.replace(x + y, (x, z))
z + y

>>> model.replace('expression', (x, z))
>>> model.replace('expression', 'replacement')
>>> model.replace('expression', ['replacement_1', 'replacement_2'])
>>> model.replace('expression', ['replacement', 'group'])
def replace(self, expression, replacements):
    """
    All purpose method to reduce an expression by applying
    successive replacement rules.
    `expression` is either a SymPy expression
    or a key in `scipy_data_fitting.Model.expressions`.
    `replacements` can be any of the following,
    or a list of any combination of the following:
    - A replacement tuple as in `scipy_data_fitting.Model.replacements`.
    - The name of a replacement in `scipy_data_fitting.Model.replacements`.
    - The name of a replacement group in `scipy_data_fitting.Model.replacement_groups`.
    Examples:
        #!python
        >>> model.replace(x + y, (x, z))
        z + y
        >>> model.replace('expression', (x, z))
        >>> model.replace('expression', 'replacement')
        >>> model.replace('expression', ['replacement_1', 'replacement_2'])
        >>> model.replace('expression', ['replacement', 'group'])
    """
    # When expression is a string,
    # get the expressions from self.expressions.
    if isinstance(expression, str):
        expression = self.expressions[expression]
    # Allow for replacements to be empty.
    if not replacements:
        return expression
    # Allow replacements to be a string.
    if isinstance(replacements, str):
        if replacements in self.replacements:
            return self.replace(expression, self.replacements[replacements])
        elif replacements in self.replacement_groups:
            return self.replace(expression, self.replacement_groups[replacements])
    # When replacements is a list of strings or tuples,
    # Use reduce to make all the replacements.
    if all(isinstance(item, str) for item in replacements) \
    or all(isinstance(item, tuple) for item in replacements):
        return functools.reduce(self.replace, replacements, expression)
    # Otherwise make the replacement.
    return expression.replace(*replacements)

def symbol(

self, name)

Function to provide a shorthand for self.symbols[name].

def symbol(self, name):
    """
    Function to provide a shorthand for `self.symbols[name]`.
    """
    return self.symbols[name]

Instance variables

var expressions

Dictionary to store SymPy expressions by name.

var name

The identifier name for this object.

var replacement_groups

Dictionary to store a sequence of replacements by name. Each value is a list of names that will be looked up in replacements.

When used to make substitutions, replacements will be applied one at a time in the order given.

var replacements

Dictionary to store replacement rules by name. Each value is a tuple of SymPy expressions: (expression, replacement).

var symbols

Dictionary to store symbols by name.

Add symbols directly, or with add_symbol and add_symbols.

class Plot

Interface for creating and saving plots of Fit using matplotlib.

Many style options can be configured using matplotlibrc.

Example:

>>> fit = Fit()
>>> # do things with fit until it's ready to plot
>>> plot = Plot(fit=fit)
>>> plot.save(fit.name + '.png')
>>> plot.close()
class Plot:
    """
    Interface for creating and saving plots of `scipy_data_fitting.Fit`
    using [matplotlib][1].

    Many style options can be configured using [matplotlibrc][2].

    Example:

        #!python
        >>> fit = Fit()
        >>> # do things with fit until it's ready to plot
        >>> plot = Plot(fit=fit)
        >>> plot.save(fit.name + '.png')
        >>> plot.close()

    [1]: http://matplotlib.org/
    [2]: http://matplotlib.org/users/customizing.html
    """

    def __init__(self, fit=None):
        self.fit = fit
        """
        The `scipy_data_fitting.Fit` instance to use for the fit.
        """

    @property
    def fit(self):
        return self._fit

    @fit.setter
    def fit(self, value):
        self._fit = value

    @property
    def options(self):
        """
        Dictionary of options which affect the plot style.

        Must contain the keys `data` and `fit` whose values are dictionaries.

        Options given in `data` and `fit` are passed as keyword arguments
        to [`matplotlib.pyplot.plot`][1] for the corresponding plot.

        Other options:

        - `points` is the number of points to use when generating the fit plot.

        Default:

            #!python
            {
                'data': {'marker': '.', 'linestyle': 'None'},
                'fit': {},
                'points': 300,
            }

        [1]: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot
        """
        if not hasattr(self, '_options'):
            self._options = {
                'data': {'marker': '.', 'linestyle': 'None'},
                'fit': {},
                'points': 300,
            }
        return self._options

    @options.setter
    def options(self, value):
        self._options = value

    @property
    def figure(self):
        """
        The [`matplotlib.pyplot.figure`][1] instance.

        [1]: http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure
        """
        if not hasattr(self, '_figure'): self._figure = matplotlib.pyplot.figure()
        return self._figure

    @property
    def plot(self):
        """
        The plot object, see [Pyplot][1].

        If one does not exist, it will be created first with [`add_subplot`][2].

        [1]: http://matplotlib.org/api/pyplot_api.html
        [2]: http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure.add_subplot
        """
        if not hasattr(self, '_plot'):
            plot = self.figure.add_subplot(111)
            pointspace = self.fit.pointspace(num=self.options['points'])

            if any(v is not None for v in self.fit.data.error):
                plot_data = plot.errorbar
                self.options['data']['xerr'] = self.fit.data.error[0]
                self.options['data']['yerr'] = self.fit.data.error[1]
            else:
                plot_data = plot.plot

            plot_data(*pointspace['data'], **self.options['data'])
            plot.plot(*pointspace['fit'], **self.options['fit'])

            text = {}
            for v in ('independent', 'dependent'):
                meta = getattr(self.fit, v)
                text[v] = {
                  'name': meta['name'] if 'name' in meta else '',
                  'units': ' (' + meta['units'] + ')' if 'units' in meta else ''
                }

            plot.set_xlabel(text['independent']['name'] + text['independent']['units'])
            plot.set_ylabel(text['dependent']['name'] + text['dependent']['units'])

            self._plot = plot

        return self._plot

    def save(self, path, **kwargs):
        """
        Save the plot to the file at `path`.

        Any keyword arguments are passed to [`matplotlib.pyplot.savefig`][1].

        [1]: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.savefig
        """
        self.plot if not hasattr(self, '_plot') else None
        self.figure.savefig(path, **kwargs)

    def close(self):
        """
        Closes `scipy_data_fitting.Plot.figure` with [`matplotlib.pyplot.close`][1].

        This should always be called after the plot object is no longer needed,
        e.g. after saving it to disk with `scipy_data_fitting.Plot.save`.

        [1]: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.close
        """
        matplotlib.pyplot.close(self.figure)

Ancestors (in MRO)

  • Plot
  • builtins.object

Static methods

def __init__(

self, fit=None)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, fit=None):
    self.fit = fit
    """
    The `scipy_data_fitting.Fit` instance to use for the fit.
    """

def close(

self)

Closes figure with matplotlib.pyplot.close.

This should always be called after the plot object is no longer needed, e.g. after saving it to disk with save.

def close(self):
    """
    Closes `scipy_data_fitting.Plot.figure` with [`matplotlib.pyplot.close`][1].
    This should always be called after the plot object is no longer needed,
    e.g. after saving it to disk with `scipy_data_fitting.Plot.save`.
    [1]: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.close
    """
    matplotlib.pyplot.close(self.figure)

def save(

self, path, **kwargs)

Save the plot to the file at path.

Any keyword arguments are passed to matplotlib.pyplot.savefig.

def save(self, path, **kwargs):
    """
    Save the plot to the file at `path`.
    Any keyword arguments are passed to [`matplotlib.pyplot.savefig`][1].
    [1]: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.savefig
    """
    self.plot if not hasattr(self, '_plot') else None
    self.figure.savefig(path, **kwargs)

Instance variables

var figure

var fit

The Fit instance to use for the fit.

var options

Dictionary of options which affect the plot style.

Must contain the keys data and fit whose values are dictionaries.

Options given in data and fit are passed as keyword arguments to matplotlib.pyplot.plot for the corresponding plot.

Other options:

  • points is the number of points to use when generating the fit plot.

Default:

{
    'data': {'marker': '.', 'linestyle': 'None'},
    'fit': {},
    'points': 300,
}

var plot

The plot object, see Pyplot.

If one does not exist, it will be created first with add_subplot.