<commit_before># -*- mode: python; coding: utf-8 -*-
# Copyright 2012-2014 <NAME> <<EMAIL>> and collaborators.
# Licensed under the MIT License.

"""pwkit.lsqmdl - model data with least-squares fitting

Classes:

Model                  - Modeling with any function using Levenberg-Marquardt.
PolynomialModel        - Modeling with polynomials.
ScaleModel             - Modeling with a single scale factor.
ComposedModel          - Modeling with combinations of pluggable components.

ModelComponent         - Base class for ComposedModel components.
AddConstantComponent   - Adds a single value to all data points.
AddValuesComponent     - Adds a parameter for every data point.
AddPolynomialComponent - Adds a polynomial.
SeriesComponent        - Apply a set of subcomponents in series.
MatMultComponent       - Combine subcomponents in a matrix multiplication.
ScaleComponent         - Multiplies the data by a single value.

Usage:

  m = Model (func, data, [invsigma], [args]).solve (guess).print_soln ()
      # func takes (p1, p2, p3[, *args]) and returns model data
  m = PolynomialModel (maxexponent, x, data, [invsigma]).solve ().plot ()
  m = ScaleModel (x, data, [invsigma]).solve ().show_cov ()
      # data = m*x

The invsigma are *inverse sigmas*, NOT inverse *variances* (the usual
statistical weights). Since most applications deal in sigmas, take care to
write

  m = Model (func, data, 1./uncerts) # right!

not

  m = Model (func, data, uncerts) # WRONG

If you have zero uncertainty on a measurement, too bad.

"""
from __future__ import absolute_import, division, print_function, unicode_literals

__all__ = (b'Model PolynomialModel ScaleModel').split ()

import numpy as np
try:
    # numpy 1.7
    import numpy.polynomial.polynomial as npoly
except ImportError:
    import numpy.polynomial as npoly


class _ModelBase (object):
    """Data and a model for least-squares fitting. Attributes:

    data     - The data to be modeled.
    invsigma - Weights: 1/σ for each data point.
    params   - ndarray of solved model parameters.
    puncerts - ndarray of 1-sigma uncerts on params.
    pnames   - list of string names for parameters.
    covar    - Covariance matrix of parameters.
    mfunc    - A function f(...) evaluating model fixed at best params.
    mdata    - The modeled data at the best params.
    rchisq   - Reduced χ² of the fit.
    resids   - resids = data - mdata.

    Methods:

    plot       - Plot the data and model (requires `omega`; assumes 1D data).
    print_soln - Print information about the model solution.
    set_data   - Set the data to be modeled.
    show_cov   - Show the parameter covariance matrix with `pwkit.ndshow_gtk2`.
    show_corr  - Show the parameter correlation matrix with `pwkit.ndshow_gtk2`.
    solve      - Fit the model to the data.

    """
    data = None
    invsigma = None
    params = None
    puncerts = None
    pnames = None
    covar = None
    mfunc = None
    mdata = None
    rchisq = None
    resids = None

    def __init__ (self, data, invsigma=None):
        self.set_data (data, invsigma)


    def set_data (self, data, invsigma=None):
        self.data = np.array (data, dtype=np.float, ndmin=1)

        if invsigma is None:
            self.invsigma = np.ones (self.data.shape)
        else:
            i = np.array (invsigma, dtype=np.float)
            self.invsigma = np.broadcast_arrays (self.data, i)[1] # allow scalar invsigma

        if self.invsigma.shape != self.data.shape:
            raise ValueError ('data values and inverse-sigma values must have same shape')


    def print_soln (self):
        lmax = reduce (max, (len (x) for x in self.pnames), len ('r chi sq'))

        if self.puncerts is None:
            for pn, val in zip (self.pnames, self.params):
                print ('%s: %14g' % (pn.rjust (lmax), val))
        else:
            for pn, val, err in zip (self.pnames, self.params, self.puncerts):
                frac = abs (100. * err / val)
                print ('%s: %14g +/- %14g (%.2f%%)' % (pn.rjust (lmax), val, err, frac))

        if self.rchisq is None:
            print ('%s: unknown/undefined' % ('r chi sq'.rjust (lmax)))
        else:
            print ('%s: %14g' % ('r chi sq'.rjust (lmax), self.rchisq))
        return self


    def plot (self, modelx, dlines=False, xmin=None, xmax=None,
              ymin=None, ymax=None, **kwargs):
        """This assumes that `data` is 1D and that `mfunc` takes one argument that
        should be treated as the X variable.
<commit_msg>pwkit/lsqmdl.py: add make_param_dict() convenience function<commit_after># -*- mode: python; coding: utf-8 -*-
# Copyright 2012-2014 <NAME> <<EMAIL>> and collaborators.
# Licensed under the MIT License.

"""pwkit.lsqmdl - model data with least-squares fitting

Classes:

Model                  - Modeling with any function using Levenberg-Marquardt.
PolynomialModel        - Modeling with polynomials.
ScaleModel             - Modeling with a single scale factor.
ComposedModel          - Modeling with combinations of pluggable components.

ModelComponent         - Base class for ComposedModel components.
AddConstantComponent   - Adds a single value to all data points.
AddValuesComponent     - Adds a parameter for every data point.
AddPolynomialComponent - Adds a polynomial.
SeriesComponent        - Apply a set of subcomponents in series.
MatMultComponent       - Combine subcomponents in a matrix multiplication.
ScaleComponent         - Multiplies the data by a single value.

Usage:

  m = Model (func, data, [invsigma], [args]).solve (guess).print_soln ()
      # func takes (p1, p2, p3[, *args]) and returns model data
  m = PolynomialModel (maxexponent, x, data, [invsigma]).solve ().plot ()
  m = ScaleModel (x, data, [invsigma]).solve ().show_cov ()
      # data = m*x

The invsigma are *inverse sigmas*, NOT inverse *variances* (the usual
statistical weights). Since most applications deal in sigmas, take care to
write

  m = Model (func, data, 1./uncerts) # right!

not

  m = Model (func, data, uncerts) # WRONG

If you have zero uncertainty on a measurement, too bad.

"""
from __future__ import absolute_import, division, print_function, unicode_literals

__all__ = (b'Model PolynomialModel ScaleModel').split ()

import numpy as np
try:
    # numpy 1.7
    import numpy.polynomial.polynomial as npoly
except ImportError:
    import numpy.polynomial as npoly


class _ModelBase (object):
    """Data and a model for least-squares fitting. Attributes:

    data     - The data to be modeled.
    invsigma - Weights: 1/σ for each data point.
    params   - ndarray of solved model parameters.
    puncerts - ndarray of 1-sigma uncerts on params.
    pnames   - list of string names for parameters.
    covar    - Covariance matrix of parameters.
    mfunc    - A function f(...) evaluating model fixed at best params.
    mdata    - The modeled data at the best params.
    rchisq   - Reduced χ² of the fit.
    resids   - resids = data - mdata.

    Methods:

    plot       - Plot the data and model (requires `omega`; assumes 1D data).
    print_soln - Print information about the model solution.
    set_data   - Set the data to be modeled.
    show_cov   - Show the parameter covariance matrix with `pwkit.ndshow_gtk2`.
    show_corr  - Show the parameter correlation matrix with `pwkit.ndshow_gtk2`.
    solve      - Fit the model to the data.

    """
    data = None
    invsigma = None
    params = None
    puncerts = None
    pnames = None
    covar = None
    mfunc = None
    mdata = None
    rchisq = None
    resids = None

    def __init__ (self, data, invsigma=None):
        self.set_data (data, invsigma)


    def set_data (self, data, invsigma=None):
        self.data = np.array (data, dtype=np.float, ndmin=1)

        if invsigma is None:
            self.invsigma = np.ones (self.data.shape)
        else:
            i = np.array (invsigma, dtype=np.float)
            self.invsigma = np.broadcast_arrays (self.data, i)[1] # allow scalar invsigma

        if self.invsigma.shape != self.data.shape:
            raise ValueError ('data values and inverse-sigma values must have same shape')


    def print_soln (self):
        lmax = reduce (max, (len (x) for x in self.pnames), len ('r chi sq'))

        if self.puncerts is None:
            for pn, val in zip (self.pnames, self.params):
                print ('%s: %14g' % (pn.rjust (lmax), val))
        else:
            for pn, val, err in zip (self.pnames, self.params, self.puncerts):
                frac = abs (100. * err / val)
                print ('%s: %14g +/- %14g (%.2f%%)' % (pn.rjust (lmax), val, err, frac))

        if self.rchisq is None:
            print ('%s: unknown/undefined' % ('r chi sq'.rjust (lmax)))
        else:
            print ('%s: %14g' % ('r chi sq'.rjust (lmax), self.rchisq))
        return self


    def make_param_dict (self):
        """Return the parameter values and uncertainties in a dictionary.

        The keys are the parameter names as stored in `pnames`. The values
        are `pwkit.Holder` objects with the following fields:

        index  - The parameter's index number in `pnames`, `params`, etc.
        value  - Its value: `params[index]`.
        uncert - Its uncertainty: `puncerts[index]`.

        This is obviously a less efficient representation, but it can be
        convenient when there are many parameters and their indices are
        variable or annoying to work out.

        """
        from . import Holder
        result = {}
        for i, n in enumerate (self.pnames):
            result[n] = Holder (index=i,
                                value=self.params[i],
                                uncert=self.puncerts[i])
        return result


    def plot (self, modelx, dlines=False, xmin=None, xmax=None,
              ymin=None, ymax=None, **kwargs):
        """This assumes that `data` is 1D and that `mfunc` takes one argument that
        should be treated as the X variable.

{'fraction_non_alphanumeric': 0.09545727436461877, 'fraction_numerical': 0.007004202521512908, 'mean_word_length': 2.4382524939800483, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1823583', 'n_tokens_mistral': 3107, 'n_tokens_neox': 2953, 'n_words': 1118}