diff options
Diffstat (limited to 'silx/math/fit/fittheories.py')
-rw-r--r-- | silx/math/fit/fittheories.py | 1374 |
1 files changed, 0 insertions, 1374 deletions
diff --git a/silx/math/fit/fittheories.py b/silx/math/fit/fittheories.py deleted file mode 100644 index 6b19a38..0000000 --- a/silx/math/fit/fittheories.py +++ /dev/null @@ -1,1374 +0,0 @@ -# coding: utf-8 -#/*########################################################################## -# -# Copyright (c) 2004-2020 European Synchrotron Radiation Facility -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -########################################################################### */ -"""This modules provides a set of fit functions and associated -estimation functions in a format that can be imported into a -:class:`silx.math.fit.FitManager` instance. - -These functions are well suited for fitting multiple gaussian shaped peaks -typically found in spectroscopy data. The estimation functions are designed -to detect how many peaks are present in the data, and provide an initial -estimate for their height, their center location and their full-width -at half maximum (fwhm). - -The limitation of these estimation algorithms is that only gaussians having a -similar fwhm can be detected by the peak search algorithm. -This *search fwhm* can be defined by the user, if -he knows the characteristics of his data, or can be automatically estimated -based on the fwhm of the largest peak in the data. - -The source code of this module can serve as template for defining your own -fit functions. - -The functions to be imported by :meth:`FitManager.loadtheories` are defined by -a dictionary :const:`THEORY`: with the following structure:: - - from silx.math.fit.fittheory import FitTheory - - THEORY = { - 'theory_name_1': FitTheory( - description='Description of theory 1', - function=fitfunction1, - parameters=('param name 1', 'param name 2', …), - estimate=estimation_function1, - configure=configuration_function1, - derivative=derivative_function1), - - 'theory_name_2': FitTheory(…), - } - -.. note:: - - Consider using an OrderedDict instead of a regular dictionary, when - defining your own theory dictionary, if the order matters to you. - This will likely be the case if you intend to load a selection of - functions in a GUI such as :class:`silx.gui.fit.FitManager`. - -Theory names can be customized (e.g. ``gauss, lorentz, splitgauss``…). - -The mandatory parameters for :class:`FitTheory` are ``function`` and -``parameters``. - -You can also define an ``INIT`` function that will be executed by -:meth:`FitManager.loadtheories`. - -See the documentation of :class:`silx.math.fit.fittheory.FitTheory` -for more information. - -Module members: ---------------- -""" -import numpy -from collections import OrderedDict -import logging - -from silx.math.fit import functions -from silx.math.fit.peaks import peak_search, guess_fwhm -from silx.math.fit.filters import strip, savitsky_golay -from silx.math.fit.leastsq import leastsq -from silx.math.fit.fittheory import FitTheory - -_logger = logging.getLogger(__name__) - -__authors__ = ["V.A. Sole", "P. Knobel"] -__license__ = "MIT" -__date__ = "15/05/2017" - - -DEFAULT_CONFIG = { - 'NoConstraintsFlag': False, - 'PositiveFwhmFlag': True, - 'PositiveHeightAreaFlag': True, - 'SameFwhmFlag': False, - 'QuotedPositionFlag': False, # peak not outside data range - 'QuotedEtaFlag': False, # force 0 < eta < 1 - # Peak detection - 'AutoScaling': False, - 'Yscaling': 1.0, - 'FwhmPoints': 8, - 'AutoFwhm': True, - 'Sensitivity': 2.5, - 'ForcePeakPresence': True, - # Hypermet - 'HypermetTails': 15, - 'QuotedFwhmFlag': 0, - 'MaxFwhm2InputRatio': 1.5, - 'MinFwhm2InputRatio': 0.4, - # short tail parameters - 'MinGaussArea4ShortTail': 50000., - 'InitialShortTailAreaRatio': 0.050, - 'MaxShortTailAreaRatio': 0.100, - 'MinShortTailAreaRatio': 0.0010, - 'InitialShortTailSlopeRatio': 0.70, - 'MaxShortTailSlopeRatio': 2.00, - 'MinShortTailSlopeRatio': 0.50, - # long tail parameters - 'MinGaussArea4LongTail': 1000.0, - 'InitialLongTailAreaRatio': 0.050, - 'MaxLongTailAreaRatio': 0.300, - 'MinLongTailAreaRatio': 0.010, - 'InitialLongTailSlopeRatio': 20.0, - 'MaxLongTailSlopeRatio': 50.0, - 'MinLongTailSlopeRatio': 5.0, - # step tail - 'MinGaussHeight4StepTail': 5000., - 'InitialStepTailHeightRatio': 0.002, - 'MaxStepTailHeightRatio': 0.0100, - 'MinStepTailHeightRatio': 0.0001, - # Hypermet constraints - # position in range [estimated position +- estimated fwhm/2] - 'HypermetQuotedPositionFlag': True, - 'DeltaPositionFwhmUnits': 0.5, - 'SameSlopeRatioFlag': 1, - 'SameAreaRatioFlag': 1, - # Strip bg removal - 'StripBackgroundFlag': True, - 'SmoothingFlag': True, - 'SmoothingWidth': 5, - 'StripWidth': 2, - 'StripIterations': 5000, - 'StripThresholdFactor': 1.0} -"""This dictionary defines default configuration parameters that have effects -on fit functions and estimation functions, mainly on fit constraints. -This dictionary is accessible as attribute :attr:`FitTheories.config`, -which can be modified by configuration functions defined in -:const:`CONFIGURE`. -""" - -CFREE = 0 -CPOSITIVE = 1 -CQUOTED = 2 -CFIXED = 3 -CFACTOR = 4 -CDELTA = 5 -CSUM = 6 -CIGNORED = 7 - - -class FitTheories(object): - """Class wrapping functions from :class:`silx.math.fit.functions` - and providing estimate functions for all of these fit functions.""" - def __init__(self, config=None): - if config is None: - self.config = DEFAULT_CONFIG - else: - self.config = config - - def ahypermet(self, x, *pars): - """ - Wrapping of :func:`silx.math.fit.functions.sum_ahypermet` without - the tail flags in the function signature. - - Depending on the value of `self.config['HypermetTails']`, one can - activate or deactivate the various terms of the hypermet function. - - `self.config['HypermetTails']` must be an integer between 0 and 15. - It is a set of 4 binary flags, one for activating each one of the - hypermet terms: *gaussian function, short tail, long tail, step*. - - For example, 15 can be expressed as ``1111`` in base 2, so a flag of - 15 means all terms are active. - """ - g_term = self.config['HypermetTails'] & 1 - st_term = (self.config['HypermetTails'] >> 1) & 1 - lt_term = (self.config['HypermetTails'] >> 2) & 1 - step_term = (self.config['HypermetTails'] >> 3) & 1 - return functions.sum_ahypermet(x, *pars, - gaussian_term=g_term, st_term=st_term, - lt_term=lt_term, step_term=step_term) - - def poly(self, x, *pars): - """Order n polynomial. - The order of the polynomial is defined by the number of - coefficients (``*pars``). - - """ - p = numpy.poly1d(pars) - return p(x) - - @staticmethod - def estimate_poly(x, y, n=2): - """Estimate polynomial coefficients for a degree n polynomial. - - """ - pcoeffs = numpy.polyfit(x, y, n) - constraints = numpy.zeros((n + 1, 3), numpy.float64) - return pcoeffs, constraints - - def estimate_quadratic(self, x, y): - """Estimate quadratic coefficients - - """ - return self.estimate_poly(x, y, n=2) - - def estimate_cubic(self, x, y): - """Estimate coefficients for a degree 3 polynomial - - """ - return self.estimate_poly(x, y, n=3) - - def estimate_quartic(self, x, y): - """Estimate coefficients for a degree 4 polynomial - - """ - return self.estimate_poly(x, y, n=4) - - def estimate_quintic(self, x, y): - """Estimate coefficients for a degree 5 polynomial - - """ - return self.estimate_poly(x, y, n=5) - - def strip_bg(self, y): - """Return the strip background of y, using parameters from - :attr:`config` dictionary (*StripBackgroundFlag, StripWidth, - StripIterations, StripThresholdFactor*)""" - remove_strip_bg = self.config.get('StripBackgroundFlag', False) - if remove_strip_bg: - if self.config['SmoothingFlag']: - y = savitsky_golay(y, self.config['SmoothingWidth']) - strip_width = self.config['StripWidth'] - strip_niterations = self.config['StripIterations'] - strip_thr_factor = self.config['StripThresholdFactor'] - return strip(y, w=strip_width, - niterations=strip_niterations, - factor=strip_thr_factor) - else: - return numpy.zeros_like(y) - - def guess_yscaling(self, y): - """Estimate scaling for y prior to peak search. - A smoothing filter is applied to y to estimate the noise level - (chi-squared) - - :param y: Data array - :return: Scaling factor - """ - # ensure y is an array - yy = numpy.array(y, copy=False) - - # smooth - convolution_kernel = numpy.ones(shape=(3,)) / 3. - ysmooth = numpy.convolve(y, convolution_kernel, mode="same") - - # remove zeros - idx_array = numpy.fabs(y) > 0.0 - yy = yy[idx_array] - ysmooth = ysmooth[idx_array] - - # compute scaling factor - chisq = numpy.mean((yy - ysmooth)**2 / numpy.fabs(yy)) - if chisq > 0: - return 1. / chisq - else: - return 1.0 - - def peak_search(self, y, fwhm, sensitivity): - """Search for peaks in y array, after padding the array and - multiplying its value by a scaling factor. - - :param y: 1-D data array - :param int fwhm: Typical full width at half maximum for peaks, - in number of points. This parameter is used for to discriminate between - true peaks and background fluctuations. - :param float sensitivity: Sensitivity parameter. This is a threshold factor - for peak detection. Only peaks larger than the standard deviation - of the noise multiplied by this sensitivity parameter are detected. - :return: List of peak indices - """ - # add padding - ysearch = numpy.ones((len(y) + 2 * fwhm,), numpy.float64) - ysearch[0:fwhm] = y[0] - ysearch[-1:-fwhm - 1:-1] = y[len(y)-1] - ysearch[fwhm:fwhm + len(y)] = y[:] - - scaling = self.guess_yscaling(y) if self.config["AutoScaling"] else self.config["Yscaling"] - - if len(ysearch) > 1.5 * fwhm: - peaks = peak_search(scaling * ysearch, - fwhm=fwhm, sensitivity=sensitivity) - return [peak_index - fwhm for peak_index in peaks - if 0 <= peak_index - fwhm < len(y)] - else: - return [] - - def estimate_height_position_fwhm(self, x, y): - """Estimation of *Height, Position, FWHM* of peaks, for gaussian-like - curves. - - This functions finds how many parameters are needed, based on the - number of peaks detected. Then it estimates the fit parameters - with a few iterations of fitting gaussian functions. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Height, Position, FWHM*. - Fit constraints depend on :attr:`config`. - """ - fittedpar = [] - - bg = self.strip_bg(y) - - if self.config['AutoFwhm']: - search_fwhm = guess_fwhm(y) - else: - search_fwhm = int(float(self.config['FwhmPoints'])) - search_sens = float(self.config['Sensitivity']) - - if search_fwhm < 3: - _logger.warning("Setting peak fwhm to 3 (lower limit)") - search_fwhm = 3 - self.config['FwhmPoints'] = 3 - - if search_sens < 1: - _logger.warning("Setting peak search sensitivity to 1. " + - "(lower limit to filter out noise peaks)") - search_sens = 1 - self.config['Sensitivity'] = 1 - - npoints = len(y) - - # Find indices of peaks in data array - peaks = self.peak_search(y, - fwhm=search_fwhm, - sensitivity=search_sens) - - if not len(peaks): - forcepeak = int(float(self.config.get('ForcePeakPresence', 0))) - if forcepeak: - delta = y - bg - # get index of global maximum - # (first one if several samples are equal to this value) - peaks = [numpy.nonzero(delta == delta.max())[0][0]] - - # Find index of largest peak in peaks array - index_largest_peak = 0 - if len(peaks) > 0: - # estimate fwhm as 5 * sampling interval - sig = 5 * abs(x[npoints - 1] - x[0]) / npoints - peakpos = x[int(peaks[0])] - if abs(peakpos) < 1.0e-16: - peakpos = 0.0 - param = numpy.array( - [y[int(peaks[0])] - bg[int(peaks[0])], peakpos, sig]) - height_largest_peak = param[0] - peak_index = 1 - for i in peaks[1:]: - param2 = numpy.array( - [y[int(i)] - bg[int(i)], x[int(i)], sig]) - param = numpy.concatenate((param, param2)) - if param2[0] > height_largest_peak: - height_largest_peak = param2[0] - index_largest_peak = peak_index - peak_index += 1 - - # Subtract background - xw = x - yw = y - bg - - cons = numpy.zeros((len(param), 3), numpy.float64) - - # peak height must be positive - cons[0:len(param):3, 0] = CPOSITIVE - # force peaks to stay around their position - cons[1:len(param):3, 0] = CQUOTED - - # set possible peak range to estimated peak +- guessed fwhm - if len(xw) > search_fwhm: - fwhmx = numpy.fabs(xw[int(search_fwhm)] - xw[0]) - cons[1:len(param):3, 1] = param[1:len(param):3] - 0.5 * fwhmx - cons[1:len(param):3, 2] = param[1:len(param):3] + 0.5 * fwhmx - else: - shape = [max(1, int(x)) for x in (param[1:len(param):3])] - cons[1:len(param):3, 1] = min(xw) * numpy.ones( - shape, - numpy.float64) - cons[1:len(param):3, 2] = max(xw) * numpy.ones( - shape, - numpy.float64) - - # ensure fwhm is positive - cons[2:len(param):3, 0] = CPOSITIVE - - # run a quick iterative fit (4 iterations) to improve - # estimations - fittedpar, _, _ = leastsq(functions.sum_gauss, xw, yw, param, - max_iter=4, constraints=cons.tolist(), - full_output=True) - - # set final constraints based on config parameters - cons = numpy.zeros((len(fittedpar), 3), numpy.float64) - peak_index = 0 - for i in range(len(peaks)): - # Setup height area constrains - if not self.config['NoConstraintsFlag']: - if self.config['PositiveHeightAreaFlag']: - cons[peak_index, 0] = CPOSITIVE - cons[peak_index, 1] = 0 - cons[peak_index, 2] = 0 - peak_index += 1 - - # Setup position constrains - if not self.config['NoConstraintsFlag']: - if self.config['QuotedPositionFlag']: - cons[peak_index, 0] = CQUOTED - cons[peak_index, 1] = min(x) - cons[peak_index, 2] = max(x) - peak_index += 1 - - # Setup positive FWHM constrains - if not self.config['NoConstraintsFlag']: - if self.config['PositiveFwhmFlag']: - cons[peak_index, 0] = CPOSITIVE - cons[peak_index, 1] = 0 - cons[peak_index, 2] = 0 - if self.config['SameFwhmFlag']: - if i != index_largest_peak: - cons[peak_index, 0] = CFACTOR - cons[peak_index, 1] = 3 * index_largest_peak + 2 - cons[peak_index, 2] = 1.0 - peak_index += 1 - - return fittedpar, cons - - def estimate_agauss(self, x, y): - """Estimation of *Area, Position, FWHM* of peaks, for gaussian-like - curves. - - This functions uses :meth:`estimate_height_position_fwhm`, then - converts the height parameters to area under the curve with the - formula ``area = sqrt(2*pi) * height * fwhm / (2 * sqrt(2 * log(2))`` - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Area, Position, FWHM*. - Fit constraints depend on :attr:`config`. - """ - fittedpar, cons = self.estimate_height_position_fwhm(x, y) - # get the number of found peaks - npeaks = len(fittedpar) // 3 - for i in range(npeaks): - height = fittedpar[3 * i] - fwhm = fittedpar[3 * i + 2] - # Replace height with area in fittedpar - fittedpar[3 * i] = numpy.sqrt(2 * numpy.pi) * height * fwhm / ( - 2.0 * numpy.sqrt(2 * numpy.log(2))) - return fittedpar, cons - - def estimate_alorentz(self, x, y): - """Estimation of *Area, Position, FWHM* of peaks, for Lorentzian - curves. - - This functions uses :meth:`estimate_height_position_fwhm`, then - converts the height parameters to area under the curve with the - formula ``area = height * fwhm * 0.5 * pi`` - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Area, Position, FWHM*. - Fit constraints depend on :attr:`config`. - """ - fittedpar, cons = self.estimate_height_position_fwhm(x, y) - # get the number of found peaks - npeaks = len(fittedpar) // 3 - for i in range(npeaks): - height = fittedpar[3 * i] - fwhm = fittedpar[3 * i + 2] - # Replace height with area in fittedpar - fittedpar[3 * i] = (height * fwhm * 0.5 * numpy.pi) - return fittedpar, cons - - def estimate_splitgauss(self, x, y): - """Estimation of *Height, Position, FWHM1, FWHM2* of peaks, for - asymmetric gaussian-like curves. - - This functions uses :meth:`estimate_height_position_fwhm`, then - adds a second (identical) estimation of FWHM to the fit parameters - for each peak, and the corresponding constraint. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Height, Position, FWHM1, FWHM2*. - Fit constraints depend on :attr:`config`. - """ - fittedpar, cons = self.estimate_height_position_fwhm(x, y) - # get the number of found peaks - npeaks = len(fittedpar) // 3 - estimated_parameters = [] - estimated_constraints = numpy.zeros((4 * npeaks, 3), numpy.float64) - for i in range(npeaks): - for j in range(3): - estimated_parameters.append(fittedpar[3 * i + j]) - # fwhm2 estimate = fwhm1 - estimated_parameters.append(fittedpar[3 * i + 2]) - # height - estimated_constraints[4 * i, 0] = cons[3 * i, 0] - estimated_constraints[4 * i, 1] = cons[3 * i, 1] - estimated_constraints[4 * i, 2] = cons[3 * i, 2] - # position - estimated_constraints[4 * i + 1, 0] = cons[3 * i + 1, 0] - estimated_constraints[4 * i + 1, 1] = cons[3 * i + 1, 1] - estimated_constraints[4 * i + 1, 2] = cons[3 * i + 1, 2] - # fwhm1 - estimated_constraints[4 * i + 2, 0] = cons[3 * i + 2, 0] - estimated_constraints[4 * i + 2, 1] = cons[3 * i + 2, 1] - estimated_constraints[4 * i + 2, 2] = cons[3 * i + 2, 2] - # fwhm2 - estimated_constraints[4 * i + 3, 0] = cons[3 * i + 2, 0] - estimated_constraints[4 * i + 3, 1] = cons[3 * i + 2, 1] - estimated_constraints[4 * i + 3, 2] = cons[3 * i + 2, 2] - if cons[3 * i + 2, 0] == CFACTOR: - # convert indices of related parameters - # (this happens if SameFwhmFlag == True) - estimated_constraints[4 * i + 2, 1] = \ - int(cons[3 * i + 2, 1] / 3) * 4 + 2 - estimated_constraints[4 * i + 3, 1] = \ - int(cons[3 * i + 2, 1] / 3) * 4 + 3 - return estimated_parameters, estimated_constraints - - def estimate_pvoigt(self, x, y): - """Estimation of *Height, Position, FWHM, eta* of peaks, for - pseudo-Voigt curves. - - Pseudo-Voigt are a sum of a gaussian curve *G(x)* and a lorentzian - curve *L(x)* with the same height, center, fwhm parameters: - ``y(x) = eta * G(x) + (1-eta) * L(x)`` - - This functions uses :meth:`estimate_height_position_fwhm`, then - adds a constant estimation of *eta* (0.5) to the fit parameters - for each peak, and the corresponding constraint. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Height, Position, FWHM, eta*. - Constraint for the eta parameter can be set to QUOTED (0.--1.) - by setting :attr:`config`['QuotedEtaFlag'] to ``True``. - If this is not the case, the constraint code is set to FREE. - """ - fittedpar, cons = self.estimate_height_position_fwhm(x, y) - npeaks = len(fittedpar) // 3 - newpar = [] - newcons = numpy.zeros((4 * npeaks, 3), numpy.float64) - # find out related parameters proper index - if not self.config['NoConstraintsFlag']: - if self.config['SameFwhmFlag']: - j = 0 - # get the index of the free FWHM - for i in range(npeaks): - if cons[3 * i + 2, 0] != 4: - j = i - for i in range(npeaks): - if i != j: - cons[3 * i + 2, 1] = 4 * j + 2 - for i in range(npeaks): - newpar.append(fittedpar[3 * i]) - newpar.append(fittedpar[3 * i + 1]) - newpar.append(fittedpar[3 * i + 2]) - newpar.append(0.5) - # height - newcons[4 * i, 0] = cons[3 * i, 0] - newcons[4 * i, 1] = cons[3 * i, 1] - newcons[4 * i, 2] = cons[3 * i, 2] - # position - newcons[4 * i + 1, 0] = cons[3 * i + 1, 0] - newcons[4 * i + 1, 1] = cons[3 * i + 1, 1] - newcons[4 * i + 1, 2] = cons[3 * i + 1, 2] - # fwhm - newcons[4 * i + 2, 0] = cons[3 * i + 2, 0] - newcons[4 * i + 2, 1] = cons[3 * i + 2, 1] - newcons[4 * i + 2, 2] = cons[3 * i + 2, 2] - # Eta constrains - newcons[4 * i + 3, 0] = CFREE - newcons[4 * i + 3, 1] = 0 - newcons[4 * i + 3, 2] = 0 - if self.config['QuotedEtaFlag']: - newcons[4 * i + 3, 0] = CQUOTED - newcons[4 * i + 3, 1] = 0.0 - newcons[4 * i + 3, 2] = 1.0 - return newpar, newcons - - def estimate_splitpvoigt(self, x, y): - """Estimation of *Height, Position, FWHM1, FWHM2, eta* of peaks, for - asymmetric pseudo-Voigt curves. - - This functions uses :meth:`estimate_height_position_fwhm`, then - adds an identical FWHM2 parameter and a constant estimation of - *eta* (0.5) to the fit parameters for each peak, and the corresponding - constraints. - - Constraint for the eta parameter can be set to QUOTED (0.--1.) - by setting :attr:`config`['QuotedEtaFlag'] to ``True``. - If this is not the case, the constraint code is set to FREE. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Height, Position, FWHM1, FWHM2, eta*. - """ - fittedpar, cons = self.estimate_height_position_fwhm(x, y) - npeaks = len(fittedpar) // 3 - newpar = [] - newcons = numpy.zeros((5 * npeaks, 3), numpy.float64) - # find out related parameters proper index - if not self.config['NoConstraintsFlag']: - if self.config['SameFwhmFlag']: - j = 0 - # get the index of the free FWHM - for i in range(npeaks): - if cons[3 * i + 2, 0] != 4: - j = i - for i in range(npeaks): - if i != j: - cons[3 * i + 2, 1] = 4 * j + 2 - for i in range(npeaks): - # height - newpar.append(fittedpar[3 * i]) - # position - newpar.append(fittedpar[3 * i + 1]) - # fwhm1 - newpar.append(fittedpar[3 * i + 2]) - # fwhm2 estimate equal to fwhm1 - newpar.append(fittedpar[3 * i + 2]) - # eta - newpar.append(0.5) - # constraint codes - # ---------------- - # height - newcons[5 * i, 0] = cons[3 * i, 0] - # position - newcons[5 * i + 1, 0] = cons[3 * i + 1, 0] - # fwhm1 - newcons[5 * i + 2, 0] = cons[3 * i + 2, 0] - # fwhm2 - newcons[5 * i + 3, 0] = cons[3 * i + 2, 0] - # cons 1 - # ------ - newcons[5 * i, 1] = cons[3 * i, 1] - newcons[5 * i + 1, 1] = cons[3 * i + 1, 1] - newcons[5 * i + 2, 1] = cons[3 * i + 2, 1] - newcons[5 * i + 3, 1] = cons[3 * i + 2, 1] - # cons 2 - # ------ - newcons[5 * i, 2] = cons[3 * i, 2] - newcons[5 * i + 1, 2] = cons[3 * i + 1, 2] - newcons[5 * i + 2, 2] = cons[3 * i + 2, 2] - newcons[5 * i + 3, 2] = cons[3 * i + 2, 2] - - if cons[3 * i + 2, 0] == CFACTOR: - # fwhm2 connstraint depends on fwhm1 - newcons[5 * i + 3, 1] = newcons[5 * i + 2, 1] + 1 - # eta constraints - newcons[5 * i + 4, 0] = CFREE - newcons[5 * i + 4, 1] = 0 - newcons[5 * i + 4, 2] = 0 - if self.config['QuotedEtaFlag']: - newcons[5 * i + 4, 0] = CQUOTED - newcons[5 * i + 4, 1] = 0.0 - newcons[5 * i + 4, 2] = 1.0 - return newpar, newcons - - def estimate_apvoigt(self, x, y): - """Estimation of *Area, Position, FWHM1, eta* of peaks, for - pseudo-Voigt curves. - - This functions uses :meth:`estimate_pvoigt`, then converts the height - parameter to area. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *Area, Position, FWHM, eta*. - """ - fittedpar, cons = self.estimate_pvoigt(x, y) - npeaks = len(fittedpar) // 4 - # Assume 50% of the area is determined by the gaussian and 50% by - # the Lorentzian. - for i in range(npeaks): - height = fittedpar[4 * i] - fwhm = fittedpar[4 * i + 2] - fittedpar[4 * i] = 0.5 * (height * fwhm * 0.5 * numpy.pi) +\ - 0.5 * (height * fwhm / (2.0 * numpy.sqrt(2 * numpy.log(2))) - ) * numpy.sqrt(2 * numpy.pi) - return fittedpar, cons - - def estimate_ahypermet(self, x, y): - """Estimation of *area, position, fwhm, st_area_r, st_slope_r, - lt_area_r, lt_slope_r, step_height_r* of peaks, for hypermet curves. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each peak are: - *area, position, fwhm, st_area_r, st_slope_r, - lt_area_r, lt_slope_r, step_height_r* . - """ - yscaling = self.config.get('Yscaling', 1.0) - if yscaling == 0: - yscaling = 1.0 - fittedpar, cons = self.estimate_height_position_fwhm(x, y) - npeaks = len(fittedpar) // 3 - newpar = [] - newcons = numpy.zeros((8 * npeaks, 3), numpy.float64) - main_peak = 0 - # find out related parameters proper index - if not self.config['NoConstraintsFlag']: - if self.config['SameFwhmFlag']: - j = 0 - # get the index of the free FWHM - for i in range(npeaks): - if cons[3 * i + 2, 0] != 4: - j = i - for i in range(npeaks): - if i != j: - cons[3 * i + 2, 1] = 8 * j + 2 - main_peak = j - for i in range(npeaks): - if fittedpar[3 * i] > fittedpar[3 * main_peak]: - main_peak = i - - for i in range(npeaks): - height = fittedpar[3 * i] - position = fittedpar[3 * i + 1] - fwhm = fittedpar[3 * i + 2] - area = (height * fwhm / (2.0 * numpy.sqrt(2 * numpy.log(2))) - ) * numpy.sqrt(2 * numpy.pi) - # the gaussian parameters - newpar.append(area) - newpar.append(position) - newpar.append(fwhm) - # print "area, pos , fwhm = ",area,position,fwhm - # Avoid zero derivatives because of not calculating contribution - g_term = 1 - st_term = 1 - lt_term = 1 - step_term = 1 - if self.config['HypermetTails'] != 0: - g_term = self.config['HypermetTails'] & 1 - st_term = (self.config['HypermetTails'] >> 1) & 1 - lt_term = (self.config['HypermetTails'] >> 2) & 1 - step_term = (self.config['HypermetTails'] >> 3) & 1 - if g_term == 0: - # fix the gaussian parameters - newcons[8 * i, 0] = CFIXED - newcons[8 * i + 1, 0] = CFIXED - newcons[8 * i + 2, 0] = CFIXED - # the short tail parameters - if ((area * yscaling) < - self.config['MinGaussArea4ShortTail']) | \ - (st_term == 0): - newpar.append(0.0) - newpar.append(0.0) - newcons[8 * i + 3, 0] = CFIXED - newcons[8 * i + 3, 1] = 0.0 - newcons[8 * i + 3, 2] = 0.0 - newcons[8 * i + 4, 0] = CFIXED - newcons[8 * i + 4, 1] = 0.0 - newcons[8 * i + 4, 2] = 0.0 - else: - newpar.append(self.config['InitialShortTailAreaRatio']) - newpar.append(self.config['InitialShortTailSlopeRatio']) - newcons[8 * i + 3, 0] = CQUOTED - newcons[8 * i + 3, 1] = self.config['MinShortTailAreaRatio'] - newcons[8 * i + 3, 2] = self.config['MaxShortTailAreaRatio'] - newcons[8 * i + 4, 0] = CQUOTED - newcons[8 * i + 4, 1] = self.config['MinShortTailSlopeRatio'] - newcons[8 * i + 4, 2] = self.config['MaxShortTailSlopeRatio'] - # the long tail parameters - if ((area * yscaling) < - self.config['MinGaussArea4LongTail']) | \ - (lt_term == 0): - newpar.append(0.0) - newpar.append(0.0) - newcons[8 * i + 5, 0] = CFIXED - newcons[8 * i + 5, 1] = 0.0 - newcons[8 * i + 5, 2] = 0.0 - newcons[8 * i + 6, 0] = CFIXED - newcons[8 * i + 6, 1] = 0.0 - newcons[8 * i + 6, 2] = 0.0 - else: - newpar.append(self.config['InitialLongTailAreaRatio']) - newpar.append(self.config['InitialLongTailSlopeRatio']) - newcons[8 * i + 5, 0] = CQUOTED - newcons[8 * i + 5, 1] = self.config['MinLongTailAreaRatio'] - newcons[8 * i + 5, 2] = self.config['MaxLongTailAreaRatio'] - newcons[8 * i + 6, 0] = CQUOTED - newcons[8 * i + 6, 1] = self.config['MinLongTailSlopeRatio'] - newcons[8 * i + 6, 2] = self.config['MaxLongTailSlopeRatio'] - # the step parameters - if ((height * yscaling) < - self.config['MinGaussHeight4StepTail']) | \ - (step_term == 0): - newpar.append(0.0) - newcons[8 * i + 7, 0] = CFIXED - newcons[8 * i + 7, 1] = 0.0 - newcons[8 * i + 7, 2] = 0.0 - else: - newpar.append(self.config['InitialStepTailHeightRatio']) - newcons[8 * i + 7, 0] = CQUOTED - newcons[8 * i + 7, 1] = self.config['MinStepTailHeightRatio'] - newcons[8 * i + 7, 2] = self.config['MaxStepTailHeightRatio'] - # if self.config['NoConstraintsFlag'] == 1: - # newcons=numpy.zeros((8*npeaks, 3),numpy.float64) - if npeaks > 0: - if g_term: - if self.config['PositiveHeightAreaFlag']: - for i in range(npeaks): - newcons[8 * i, 0] = CPOSITIVE - if self.config['PositiveFwhmFlag']: - for i in range(npeaks): - newcons[8 * i + 2, 0] = CPOSITIVE - if self.config['SameFwhmFlag']: - for i in range(npeaks): - if i != main_peak: - newcons[8 * i + 2, 0] = CFACTOR - newcons[8 * i + 2, 1] = 8 * main_peak + 2 - newcons[8 * i + 2, 2] = 1.0 - if self.config['HypermetQuotedPositionFlag']: - for i in range(npeaks): - delta = self.config['DeltaPositionFwhmUnits'] * fwhm - newcons[8 * i + 1, 0] = CQUOTED - newcons[8 * i + 1, 1] = newpar[8 * i + 1] - delta - newcons[8 * i + 1, 2] = newpar[8 * i + 1] + delta - if self.config['SameSlopeRatioFlag']: - for i in range(npeaks): - if i != main_peak: - newcons[8 * i + 4, 0] = CFACTOR - newcons[8 * i + 4, 1] = 8 * main_peak + 4 - newcons[8 * i + 4, 2] = 1.0 - newcons[8 * i + 6, 0] = CFACTOR - newcons[8 * i + 6, 1] = 8 * main_peak + 6 - newcons[8 * i + 6, 2] = 1.0 - if self.config['SameAreaRatioFlag']: - for i in range(npeaks): - if i != main_peak: - newcons[8 * i + 3, 0] = CFACTOR - newcons[8 * i + 3, 1] = 8 * main_peak + 3 - newcons[8 * i + 3, 2] = 1.0 - newcons[8 * i + 5, 0] = CFACTOR - newcons[8 * i + 5, 1] = 8 * main_peak + 5 - newcons[8 * i + 5, 2] = 1.0 - return newpar, newcons - - def estimate_stepdown(self, x, y): - """Estimation of parameters for stepdown curves. - - The functions estimates gaussian parameters for the derivative of - the data, takes the largest gaussian peak and uses its estimated - parameters to define the center of the step and its fwhm. The - estimated amplitude returned is simply ``max(y) - min(y)``. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit newconstraints. - Parameters to be estimated for each stepdown are: - *height, centroid, fwhm* . - """ - crappyfilter = [-0.25, -0.75, 0.0, 0.75, 0.25] - cutoff = len(crappyfilter) // 2 - y_deriv = numpy.convolve(y, - crappyfilter, - mode="valid") - - # make the derivative's peak have the same amplitude as the step - if max(y_deriv) > 0: - y_deriv = y_deriv * max(y) / max(y_deriv) - - fittedpar, newcons = self.estimate_height_position_fwhm( - x[cutoff:-cutoff], y_deriv) - - data_amplitude = max(y) - min(y) - - # use parameters from largest gaussian found - if len(fittedpar): - npeaks = len(fittedpar) // 3 - largest_index = 0 - largest = [data_amplitude, - fittedpar[3 * largest_index + 1], - fittedpar[3 * largest_index + 2]] - for i in range(npeaks): - if fittedpar[3 * i] > largest[0]: - largest_index = i - largest = [data_amplitude, - fittedpar[3 * largest_index + 1], - fittedpar[3 * largest_index + 2]] - else: - # no peak was found - largest = [data_amplitude, # height - x[len(x)//2], # center: middle of x range - self.config["FwhmPoints"] * (x[1] - x[0])] # fwhm: default value - - # Setup constrains - newcons = numpy.zeros((3, 3), numpy.float64) - if not self.config['NoConstraintsFlag']: - # Setup height constrains - if self.config['PositiveHeightAreaFlag']: - newcons[0, 0] = CPOSITIVE - newcons[0, 1] = 0 - newcons[0, 2] = 0 - - # Setup position constrains - if self.config['QuotedPositionFlag']: - newcons[1, 0] = CQUOTED - newcons[1, 1] = min(x) - newcons[1, 2] = max(x) - - # Setup positive FWHM constrains - if self.config['PositiveFwhmFlag']: - newcons[2, 0] = CPOSITIVE - newcons[2, 1] = 0 - newcons[2, 2] = 0 - - return largest, newcons - - def estimate_slit(self, x, y): - """Estimation of parameters for slit curves. - - The functions estimates stepup and stepdown parameters for the largest - steps, and uses them for calculating the center (middle between stepup - and stepdown), the height (maximum amplitude in data), the fwhm - (distance between the up- and down-step centers) and the beamfwhm - (average of FWHM for up- and down-step). - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each slit are: - *height, position, fwhm, beamfwhm* . - """ - largestup, cons = self.estimate_stepup(x, y) - largestdown, cons = self.estimate_stepdown(x, y) - fwhm = numpy.fabs(largestdown[1] - largestup[1]) - beamfwhm = 0.5 * (largestup[2] + largestdown[1]) - beamfwhm = min(beamfwhm, fwhm / 10.0) - beamfwhm = max(beamfwhm, (max(x) - min(x)) * 3.0 / len(x)) - - y_minus_bg = y - self.strip_bg(y) - height = max(y_minus_bg) - - i1 = numpy.nonzero(y_minus_bg >= 0.5 * height)[0] - xx = numpy.take(x, i1) - position = (xx[0] + xx[-1]) / 2.0 - fwhm = xx[-1] - xx[0] - largest = [height, position, fwhm, beamfwhm] - cons = numpy.zeros((4, 3), numpy.float64) - # Setup constrains - if not self.config['NoConstraintsFlag']: - # Setup height constrains - if self.config['PositiveHeightAreaFlag']: - cons[0, 0] = CPOSITIVE - cons[0, 1] = 0 - cons[0, 2] = 0 - - # Setup position constrains - if self.config['QuotedPositionFlag']: - cons[1, 0] = CQUOTED - cons[1, 1] = min(x) - cons[1, 2] = max(x) - - # Setup positive FWHM constrains - if self.config['PositiveFwhmFlag']: - cons[2, 0] = CPOSITIVE - cons[2, 1] = 0 - cons[2, 2] = 0 - - # Setup positive FWHM constrains - if self.config['PositiveFwhmFlag']: - cons[3, 0] = CPOSITIVE - cons[3, 1] = 0 - cons[3, 2] = 0 - return largest, cons - - def estimate_stepup(self, x, y): - """Estimation of parameters for a single step up curve. - - The functions estimates gaussian parameters for the derivative of - the data, takes the largest gaussian peak and uses its estimated - parameters to define the center of the step and its fwhm. The - estimated amplitude returned is simply ``max(y) - min(y)``. - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - Parameters to be estimated for each stepup are: - *height, centroid, fwhm* . - """ - crappyfilter = [0.25, 0.75, 0.0, -0.75, -0.25] - cutoff = len(crappyfilter) // 2 - y_deriv = numpy.convolve(y, crappyfilter, mode="valid") - if max(y_deriv) > 0: - y_deriv = y_deriv * max(y) / max(y_deriv) - - fittedpar, cons = self.estimate_height_position_fwhm( - x[cutoff:-cutoff], y_deriv) - - # for height, use the data amplitude after removing the background - data_amplitude = max(y) - min(y) - - # find params of the largest gaussian found - if len(fittedpar): - npeaks = len(fittedpar) // 3 - largest_index = 0 - largest = [data_amplitude, - fittedpar[3 * largest_index + 1], - fittedpar[3 * largest_index + 2]] - for i in range(npeaks): - if fittedpar[3 * i] > largest[0]: - largest_index = i - largest = [fittedpar[3 * largest_index], - fittedpar[3 * largest_index + 1], - fittedpar[3 * largest_index + 2]] - else: - # no peak was found - largest = [data_amplitude, # height - x[len(x)//2], # center: middle of x range - self.config["FwhmPoints"] * (x[1] - x[0])] # fwhm: default value - - newcons = numpy.zeros((3, 3), numpy.float64) - # Setup constrains - if not self.config['NoConstraintsFlag']: - # Setup height constraints - if self.config['PositiveHeightAreaFlag']: - newcons[0, 0] = CPOSITIVE - newcons[0, 1] = 0 - newcons[0, 2] = 0 - - # Setup position constraints - if self.config['QuotedPositionFlag']: - newcons[1, 0] = CQUOTED - newcons[1, 1] = min(x) - newcons[1, 2] = max(x) - - # Setup positive FWHM constraints - if self.config['PositiveFwhmFlag']: - newcons[2, 0] = CPOSITIVE - newcons[2, 1] = 0 - newcons[2, 2] = 0 - - return largest, newcons - - def estimate_periodic_gauss(self, x, y): - """Estimation of parameters for periodic gaussian curves: - *number of peaks, distance between peaks, height, position of the - first peak, fwhm* - - The functions detects all peaks, then computes the parameters the - following way: - - - *distance*: average of distances between detected peaks - - *height*: average height of detected peaks - - *fwhm*: fwhm of the highest peak (in number of samples) if - field ``'AutoFwhm'`` in :attr:`config` is ``True``, else take - the default value (field ``'FwhmPoints'`` in :attr:`config`) - - :param x: Array of abscissa values - :param y: Array of ordinate values (``y = f(x)``) - :return: Tuple of estimated fit parameters and fit constraints. - """ - yscaling = self.config.get('Yscaling', 1.0) - if yscaling == 0: - yscaling = 1.0 - - bg = self.strip_bg(y) - - if self.config['AutoFwhm']: - search_fwhm = guess_fwhm(y) - else: - search_fwhm = int(float(self.config['FwhmPoints'])) - search_sens = float(self.config['Sensitivity']) - - if search_fwhm < 3: - search_fwhm = 3 - - if search_sens < 1: - search_sens = 1 - - if len(y) > 1.5 * search_fwhm: - peaks = peak_search(yscaling * y, fwhm=search_fwhm, - sensitivity=search_sens) - else: - peaks = [] - npeaks = len(peaks) - if not npeaks: - fittedpar = [] - cons = numpy.zeros((len(fittedpar), 3), numpy.float64) - return fittedpar, cons - - fittedpar = [0.0, 0.0, 0.0, 0.0, 0.0] - - # The number of peaks - fittedpar[0] = npeaks - - # The separation between peaks in x units - delta = 0.0 - height = 0.0 - for i in range(npeaks): - height += y[int(peaks[i])] - bg[int(peaks[i])] - if i != npeaks - 1: - delta += (x[int(peaks[i + 1])] - x[int(peaks[i])]) - - # delta between peaks - if npeaks > 1: - fittedpar[1] = delta / (npeaks - 1) - - # starting height - fittedpar[2] = height / npeaks - - # position of the first peak - fittedpar[3] = x[int(peaks[0])] - - # Estimate the fwhm - fittedpar[4] = search_fwhm - - # setup constraints - cons = numpy.zeros((5, 3), numpy.float64) - cons[0, 0] = CFIXED # the number of gaussians - if npeaks == 1: - cons[1, 0] = CFIXED # the delta between peaks - else: - cons[1, 0] = CFREE - j = 2 - # Setup height area constrains - if not self.config['NoConstraintsFlag']: - if self.config['PositiveHeightAreaFlag']: - # POSITIVE = 1 - cons[j, 0] = CPOSITIVE - cons[j, 1] = 0 - cons[j, 2] = 0 - j += 1 - - # Setup position constrains - if not self.config['NoConstraintsFlag']: - if self.config['QuotedPositionFlag']: - # QUOTED = 2 - cons[j, 0] = CQUOTED - cons[j, 1] = min(x) - cons[j, 2] = max(x) - j += 1 - - # Setup positive FWHM constrains - if not self.config['NoConstraintsFlag']: - if self.config['PositiveFwhmFlag']: - # POSITIVE=1 - cons[j, 0] = CPOSITIVE - cons[j, 1] = 0 - cons[j, 2] = 0 - j += 1 - return fittedpar, cons - - def configure(self, **kw): - """Add new / unknown keyword arguments to :attr:`config`, - update entries in :attr:`config` if the parameter name is a existing - key. - - :param kw: Dictionary of keyword arguments. - :return: Configuration dictionary :attr:`config` - """ - if not kw.keys(): - return self.config - for key in kw.keys(): - notdone = 1 - # take care of lower / upper case problems ... - for config_key in self.config.keys(): - if config_key.lower() == key.lower(): - self.config[config_key] = kw[key] - notdone = 0 - if notdone: - self.config[key] = kw[key] - return self.config - -fitfuns = FitTheories() - -THEORY = OrderedDict(( - ('Gaussians', - FitTheory(description='Gaussian functions', - function=functions.sum_gauss, - parameters=('Height', 'Position', 'FWHM'), - estimate=fitfuns.estimate_height_position_fwhm, - configure=fitfuns.configure)), - ('Lorentz', - FitTheory(description='Lorentzian functions', - function=functions.sum_lorentz, - parameters=('Height', 'Position', 'FWHM'), - estimate=fitfuns.estimate_height_position_fwhm, - configure=fitfuns.configure)), - ('Area Gaussians', - FitTheory(description='Gaussian functions (area)', - function=functions.sum_agauss, - parameters=('Area', 'Position', 'FWHM'), - estimate=fitfuns.estimate_agauss, - configure=fitfuns.configure)), - ('Area Lorentz', - FitTheory(description='Lorentzian functions (area)', - function=functions.sum_alorentz, - parameters=('Area', 'Position', 'FWHM'), - estimate=fitfuns.estimate_alorentz, - configure=fitfuns.configure)), - ('Pseudo-Voigt Line', - FitTheory(description='Pseudo-Voigt functions', - function=functions.sum_pvoigt, - parameters=('Height', 'Position', 'FWHM', 'Eta'), - estimate=fitfuns.estimate_pvoigt, - configure=fitfuns.configure)), - ('Area Pseudo-Voigt', - FitTheory(description='Pseudo-Voigt functions (area)', - function=functions.sum_apvoigt, - parameters=('Area', 'Position', 'FWHM', 'Eta'), - estimate=fitfuns.estimate_apvoigt, - configure=fitfuns.configure)), - ('Split Gaussian', - FitTheory(description='Asymmetric gaussian functions', - function=functions.sum_splitgauss, - parameters=('Height', 'Position', 'LowFWHM', - 'HighFWHM'), - estimate=fitfuns.estimate_splitgauss, - configure=fitfuns.configure)), - ('Split Lorentz', - FitTheory(description='Asymmetric lorentzian functions', - function=functions.sum_splitlorentz, - parameters=('Height', 'Position', 'LowFWHM', 'HighFWHM'), - estimate=fitfuns.estimate_splitgauss, - configure=fitfuns.configure)), - ('Split Pseudo-Voigt', - FitTheory(description='Asymmetric pseudo-Voigt functions', - function=functions.sum_splitpvoigt, - parameters=('Height', 'Position', 'LowFWHM', - 'HighFWHM', 'Eta'), - estimate=fitfuns.estimate_splitpvoigt, - configure=fitfuns.configure)), - ('Step Down', - FitTheory(description='Step down function', - function=functions.sum_stepdown, - parameters=('Height', 'Position', 'FWHM'), - estimate=fitfuns.estimate_stepdown, - configure=fitfuns.configure)), - ('Step Up', - FitTheory(description='Step up function', - function=functions.sum_stepup, - parameters=('Height', 'Position', 'FWHM'), - estimate=fitfuns.estimate_stepup, - configure=fitfuns.configure)), - ('Slit', - FitTheory(description='Slit function', - function=functions.sum_slit, - parameters=('Height', 'Position', 'FWHM', 'BeamFWHM'), - estimate=fitfuns.estimate_slit, - configure=fitfuns.configure)), - ('Atan', - FitTheory(description='Arctan step up function', - function=functions.atan_stepup, - parameters=('Height', 'Position', 'Width'), - estimate=fitfuns.estimate_stepup, - configure=fitfuns.configure)), - ('Hypermet', - FitTheory(description='Hypermet functions', - function=fitfuns.ahypermet, # customized version of functions.sum_ahypermet - parameters=('G_Area', 'Position', 'FWHM', 'ST_Area', - 'ST_Slope', 'LT_Area', 'LT_Slope', 'Step_H'), - estimate=fitfuns.estimate_ahypermet, - configure=fitfuns.configure)), - # ('Periodic Gaussians', - # FitTheory(description='Periodic gaussian functions', - # function=functions.periodic_gauss, - # parameters=('N', 'Delta', 'Height', 'Position', 'FWHM'), - # estimate=fitfuns.estimate_periodic_gauss, - # configure=fitfuns.configure)) - ('Degree 2 Polynomial', - FitTheory(description='Degree 2 polynomial' - '\ny = a*x^2 + b*x +c', - function=fitfuns.poly, - parameters=['a', 'b', 'c'], - estimate=fitfuns.estimate_quadratic)), - ('Degree 3 Polynomial', - FitTheory(description='Degree 3 polynomial' - '\ny = a*x^3 + b*x^2 + c*x + d', - function=fitfuns.poly, - parameters=['a', 'b', 'c', 'd'], - estimate=fitfuns.estimate_cubic)), - ('Degree 4 Polynomial', - FitTheory(description='Degree 4 polynomial' - '\ny = a*x^4 + b*x^3 + c*x^2 + d*x + e', - function=fitfuns.poly, - parameters=['a', 'b', 'c', 'd', 'e'], - estimate=fitfuns.estimate_quartic)), - ('Degree 5 Polynomial', - FitTheory(description='Degree 5 polynomial' - '\ny = a*x^5 + b*x^4 + c*x^3 + d*x^2 + e*x + f', - function=fitfuns.poly, - parameters=['a', 'b', 'c', 'd', 'e', 'f'], - estimate=fitfuns.estimate_quintic)), -)) -"""Dictionary of fit theories: fit functions and their associated estimation -function, parameters list, configuration function and description. -""" - - -def test(a): - from silx.math.fit import fitmanager - x = numpy.arange(1000).astype(numpy.float64) - p = [1500, 100., 50.0, - 1500, 700., 50.0] - y_synthetic = functions.sum_gauss(x, *p) + 1 - - fit = fitmanager.FitManager(x, y_synthetic) - fit.addtheory('Gaussians', functions.sum_gauss, ['Height', 'Position', 'FWHM'], - a.estimate_height_position_fwhm) - fit.settheory('Gaussians') - fit.setbackground('Linear') - - fit.estimate() - fit.runfit() - - y_fit = fit.gendata() - - print("Fit parameter names: %s" % str(fit.get_names())) - print("Theoretical parameters: %s" % str(numpy.append([1, 0], p))) - print("Fitted parameters: %s" % str(fit.get_fitted_parameters())) - - try: - from silx.gui import qt - from silx.gui.plot import plot1D - app = qt.QApplication([]) - - # Offset of 1 to see the difference in log scale - plot1D(x, (y_synthetic + 1, y_fit), "Input data + 1, Fit") - - app.exec_() - except ImportError: - _logger.warning("Unable to load qt binding, can't plot results.") - - -if __name__ == "__main__": - test(fitfuns) |