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:
- Symmetric constant error is a zero dimensional array:
error
. - Asymmetric constant error is a one dimensional array:
[lower_error, upper_error]
. - 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
- 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:
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 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 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_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):
- A
lmfit.Parameters
object. The value of each parameter must be passed appropriately tofunction
in the order determined by sorting the parameter keys alphabetically. Uselmfit_parameter_values
to get the ordered numerical parameter values. - Independent values: see
array
. - Dependent values: see
array
. - 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 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):
- Function to fit, see
function
. - Independent values: see
array
. - Dependent values: see
array
. - 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.
- Function to fit, see
-
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)
- Model
- 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 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:
- A replacement tuple as in
replacements
. - The name of a replacement in
replacements
. - The name of a replacement group in
replacement_groups
.
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 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
.