summaryrefslogtreecommitdiff
path: root/silx/math/fit/bgtheories.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/math/fit/bgtheories.py')
-rw-r--r--silx/math/fit/bgtheories.py440
1 files changed, 0 insertions, 440 deletions
diff --git a/silx/math/fit/bgtheories.py b/silx/math/fit/bgtheories.py
deleted file mode 100644
index 3cdac98..0000000
--- a/silx/math/fit/bgtheories.py
+++ /dev/null
@@ -1,440 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-#
-# Copyright (c) 2004-2017 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 defines a set of background model functions and associated
-estimation functions in a format that can be imported into a
-:class:`silx.math.fit.FitManager` object.
-
-A background function is a function that you want to add to a regular fit
-function prior to fitting the sum of both functions. This is useful, for
-instance, if you need to fit multiple gaussian peaks in an array of
-measured data points when the measurement is polluted by a background signal.
-
-The models include common background models such as a constant value or a
-linear background.
-
-It also includes background computation filters - *strip* and *snip* - that
-can extract a more complex low-curvature background signal from a signal with
-peaks having higher curvatures.
-
-The source code of this module can serve as a template for defining your
-own fit background theories. The minimal skeleton of such a theory definition
-file is::
-
- from silx.math.fit.fittheory import FitTheory
-
- def bgfunction1(x, y0, …):
- bg_signal = …
- return bg_signal
-
- def estimation_function1(x, y):
- …
- estimated_params = …
- constraints = …
- return estimated_params, constraints
-
- THEORY = {
- 'bg_theory_name1': FitTheory(
- description='Description of theory 1',
- function=bgfunction1,
- parameters=('param name 1', 'param name 2', …),
- estimate=estimation_function1,
- configure=configuration_function1,
- derivative=derivative_function1,
- is_background=True),
- 'theory_name_2': …,
- }
-"""
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "16/01/2017"
-
-from collections import OrderedDict
-import numpy
-from silx.math.fit.filters import strip, snip1d,\
- savitsky_golay
-from silx.math.fit.fittheory import FitTheory
-
-CONFIG = {
- "SmoothingFlag": False,
- "SmoothingWidth": 5,
- "AnchorsFlag": False,
- "AnchorsList": [],
- "StripWidth": 2,
- "StripIterations": 5000,
- "StripThresholdFactor": 1.0,
- "SnipWidth": 16,
- "EstimatePolyOnStrip": True
-}
-
-# to avoid costly computations when parameters stay the same
-_BG_STRIP_OLDY = numpy.array([])
-_BG_STRIP_OLDPARS = [0, 0]
-_BG_STRIP_OLDBG = numpy.array([])
-
-_BG_SNIP_OLDY = numpy.array([])
-_BG_SNIP_OLDWIDTH = None
-_BG_SNIP_OLDBG = numpy.array([])
-
-
-_BG_OLD_ANCHORS = []
-_BG_OLD_ANCHORS_FLAG = None
-
-_BG_SMOOTH_OLDWIDTH = None
-_BG_SMOOTH_OLDFLAG = None
-
-
-def _convert_anchors_to_indices(x):
- """Anchors stored in CONFIG["AnchorsList"] are abscissa.
- Convert then to indices (take first index where x >= anchor),
- then return the list of indices.
-
- :param x: Original array of abscissa
- :return: List of indices of anchors in x array.
- If CONFIG['AnchorsFlag'] is False or None, or if the list
- of indices is empty, return None.
- """
- # convert anchor X abscissa to index
- if CONFIG['AnchorsFlag'] and CONFIG['AnchorsList'] is not None:
- anchors_indices = []
- for anchor_x in CONFIG['AnchorsList']:
- if anchor_x <= x[0]:
- continue
- # take the first index where x > anchor_x
- indices = numpy.nonzero(x >= anchor_x)[0]
- if len(indices):
- anchors_indices.append(min(indices))
- if not len(anchors_indices):
- anchors_indices = None
- else:
- anchors_indices = None
-
- return anchors_indices
-
-
-def strip_bg(x, y0, width, niter):
- """Extract and return the strip bg from y0.
-
- Use anchors coordinates in CONFIG["AnchorsList"] if flag
- CONFIG["AnchorsFlag"] is True. Convert anchors from x coordinate
- to array index prior to passing it to silx.math.fit.filters.strip
-
- :param x: Abscissa array
- :param x: Ordinate array (data values at x positions)
- :param width: strip width
- :param niter: strip niter
- """
- global _BG_STRIP_OLDY
- global _BG_STRIP_OLDPARS
- global _BG_STRIP_OLDBG
- global _BG_SMOOTH_OLDWIDTH
- global _BG_SMOOTH_OLDFLAG
- global _BG_OLD_ANCHORS
- global _BG_OLD_ANCHORS_FLAG
-
- parameters_changed =\
- _BG_STRIP_OLDPARS != [width, niter] or\
- _BG_SMOOTH_OLDWIDTH != CONFIG["SmoothingWidth"] or\
- _BG_SMOOTH_OLDFLAG != CONFIG["SmoothingFlag"] or\
- _BG_OLD_ANCHORS_FLAG != CONFIG["AnchorsFlag"] or\
- _BG_OLD_ANCHORS != CONFIG["AnchorsList"]
-
- # same parameters
- if not parameters_changed:
- # same data
- if numpy.sum(_BG_STRIP_OLDY == y0) == len(y0):
- # same result
- return _BG_STRIP_OLDBG
-
- _BG_STRIP_OLDY = y0
- _BG_STRIP_OLDPARS = [width, niter]
- _BG_SMOOTH_OLDWIDTH = CONFIG["SmoothingWidth"]
- _BG_SMOOTH_OLDFLAG = CONFIG["SmoothingFlag"]
- _BG_OLD_ANCHORS = CONFIG["AnchorsList"]
- _BG_OLD_ANCHORS_FLAG = CONFIG["AnchorsFlag"]
-
- y1 = savitsky_golay(y0, CONFIG["SmoothingWidth"]) if CONFIG["SmoothingFlag"] else y0
-
- anchors_indices = _convert_anchors_to_indices(x)
-
- background = strip(y1,
- w=width,
- niterations=niter,
- factor=CONFIG["StripThresholdFactor"],
- anchors=anchors_indices)
-
- _BG_STRIP_OLDBG = background
-
- return background
-
-
-def snip_bg(x, y0, width):
- """Compute the snip bg for y0"""
- global _BG_SNIP_OLDY
- global _BG_SNIP_OLDWIDTH
- global _BG_SNIP_OLDBG
- global _BG_SMOOTH_OLDWIDTH
- global _BG_SMOOTH_OLDFLAG
- global _BG_OLD_ANCHORS
- global _BG_OLD_ANCHORS_FLAG
-
- parameters_changed =\
- _BG_SNIP_OLDWIDTH != width or\
- _BG_SMOOTH_OLDWIDTH != CONFIG["SmoothingWidth"] or\
- _BG_SMOOTH_OLDFLAG != CONFIG["SmoothingFlag"] or\
- _BG_OLD_ANCHORS_FLAG != CONFIG["AnchorsFlag"] or\
- _BG_OLD_ANCHORS != CONFIG["AnchorsList"]
-
- # same parameters
- if not parameters_changed:
- # same data
- if numpy.sum(_BG_SNIP_OLDY == y0) == len(y0):
- # same result
- return _BG_SNIP_OLDBG
-
- _BG_SNIP_OLDY = y0
- _BG_SNIP_OLDWIDTH = width
- _BG_SMOOTH_OLDWIDTH = CONFIG["SmoothingWidth"]
- _BG_SMOOTH_OLDFLAG = CONFIG["SmoothingFlag"]
- _BG_OLD_ANCHORS = CONFIG["AnchorsList"]
- _BG_OLD_ANCHORS_FLAG = CONFIG["AnchorsFlag"]
-
- y1 = savitsky_golay(y0, CONFIG["SmoothingWidth"]) if CONFIG["SmoothingFlag"] else y0
-
- anchors_indices = _convert_anchors_to_indices(x)
-
- if anchors_indices is None or not len(anchors_indices):
- anchors_indices = [0, len(y1) - 1]
-
- background = numpy.zeros_like(y1)
- previous_anchor = 0
- for anchor_index in anchors_indices:
- if (anchor_index > previous_anchor) and (anchor_index < len(y1)):
- background[previous_anchor:anchor_index] =\
- snip1d(y1[previous_anchor:anchor_index],
- width)
- previous_anchor = anchor_index
-
- if previous_anchor < len(y1):
- background[previous_anchor:] = snip1d(y1[previous_anchor:],
- width)
-
- _BG_SNIP_OLDBG = background
-
- return background
-
-
-def estimate_linear(x, y):
- """
- Estimate the linear parameters (constant, slope) of a y signal.
-
- Strip peaks, then perform a linear regression.
- """
- bg = strip_bg(x, y,
- width=CONFIG["StripWidth"],
- niter=CONFIG["StripIterations"])
- n = float(len(bg))
- Sy = numpy.sum(bg)
- Sx = float(numpy.sum(x))
- Sxx = float(numpy.sum(x * x))
- Sxy = float(numpy.sum(x * bg))
-
- deno = n * Sxx - (Sx * Sx)
- if deno != 0:
- bg = (Sxx * Sy - Sx * Sxy) / deno
- slope = (n * Sxy - Sx * Sy) / deno
- else:
- bg = 0.0
- slope = 0.0
- estimated_par = [bg, slope]
- # code = 0: FREE
- constraints = [[0, 0, 0], [0, 0, 0]]
- return estimated_par, constraints
-
-
-def estimate_strip(x, y):
- """Estimation function for strip parameters.
-
- Return parameters as defined in CONFIG dict,
- set constraints to FIXED.
- """
- estimated_par = [CONFIG["StripWidth"],
- CONFIG["StripIterations"]]
- constraints = numpy.zeros((len(estimated_par), 3), numpy.float)
- # code = 3: FIXED
- constraints[0][0] = 3
- constraints[1][0] = 3
- return estimated_par, constraints
-
-
-def estimate_snip(x, y):
- """Estimation function for snip parameters.
-
- Return parameters as defined in CONFIG dict,
- set constraints to FIXED.
- """
- estimated_par = [CONFIG["SnipWidth"]]
- constraints = numpy.zeros((len(estimated_par), 3), numpy.float)
- # code = 3: FIXED
- constraints[0][0] = 3
- return estimated_par, constraints
-
-
-def poly(x, y, *pars):
- """Order n polynomial.
- The order of the polynomial is defined by the number of
- coefficients (``*pars``).
-
- """
- p = numpy.poly1d(pars)
- return p(x)
-
-
-def estimate_poly(x, y, deg=2):
- """Estimate polynomial coefficients.
-
- """
- # extract bg signal with strip, to estimate polynomial on background
- if CONFIG["EstimatePolyOnStrip"]:
- y = strip_bg(x, y,
- CONFIG["StripWidth"],
- CONFIG["StripIterations"])
- pcoeffs = numpy.polyfit(x, y, deg)
- cons = numpy.zeros((deg + 1, 3), numpy.float)
- return pcoeffs, cons
-
-
-def estimate_quadratic_poly(x, y):
- """Estimate quadratic polynomial coefficients.
- """
- return estimate_poly(x, y, deg=2)
-
-
-def estimate_cubic_poly(x, y):
- """Estimate cubic polynomial coefficients.
- """
- return estimate_poly(x, y, deg=3)
-
-
-def estimate_quartic_poly(x, y):
- """Estimate degree 4 polynomial coefficients.
- """
- return estimate_poly(x, y, deg=4)
-
-
-def estimate_quintic_poly(x, y):
- """Estimate degree 5 polynomial coefficients.
- """
- return estimate_poly(x, y, deg=5)
-
-
-def configure(**kw):
- """Update the CONFIG dict
- """
- # inspect **kw to find known keys, update them in CONFIG
- for key in CONFIG:
- if key in kw:
- CONFIG[key] = kw[key]
-
- return CONFIG
-
-
-THEORY = OrderedDict(
- (('No Background',
- FitTheory(
- description="No background function",
- function=lambda x, y0: numpy.zeros_like(x),
- parameters=[],
- is_background=True)),
- ('Constant',
- FitTheory(
- description='Constant background',
- function=lambda x, y0, c: c * numpy.ones_like(x),
- parameters=['Constant', ],
- estimate=lambda x, y: ([min(y)], [[0, 0, 0]]),
- is_background=True)),
- ('Linear',
- FitTheory(
- description="Linear background, parameters 'Constant' and"
- " 'Slope'",
- function=lambda x, y0, a, b: a + b * x,
- parameters=['Constant', 'Slope'],
- estimate=estimate_linear,
- configure=configure,
- is_background=True)),
- ('Strip',
- FitTheory(
- description="Compute background using a strip filter\n"
- "Parameters 'StripWidth', 'StripIterations'",
- function=strip_bg,
- parameters=['StripWidth', 'StripIterations'],
- estimate=estimate_strip,
- configure=configure,
- is_background=True)),
- ('Snip',
- FitTheory(
- description="Compute background using a snip filter\n"
- "Parameter 'SnipWidth'",
- function=snip_bg,
- parameters=['SnipWidth'],
- estimate=estimate_snip,
- configure=configure,
- is_background=True)),
- ('Degree 2 Polynomial',
- FitTheory(
- description="Quadratic polynomial background, Parameters "
- "'a', 'b' and 'c'\ny = a*x^2 + b*x +c",
- function=poly,
- parameters=['a', 'b', 'c'],
- estimate=estimate_quadratic_poly,
- configure=configure,
- is_background=True)),
- ('Degree 3 Polynomial',
- FitTheory(
- description="Cubic polynomial background, Parameters "
- "'a', 'b', 'c' and 'd'\n"
- "y = a*x^3 + b*x^2 + c*x + d",
- function=poly,
- parameters=['a', 'b', 'c', 'd'],
- estimate=estimate_cubic_poly,
- configure=configure,
- is_background=True)),
- ('Degree 4 Polynomial',
- FitTheory(
- description="Quartic polynomial background\n"
- "y = a*x^4 + b*x^3 + c*x^2 + d*x + e",
- function=poly,
- parameters=['a', 'b', 'c', 'd', 'e'],
- estimate=estimate_quartic_poly,
- configure=configure,
- is_background=True)),
- ('Degree 5 Polynomial',
- FitTheory(
- description="Quaintic polynomial background\n"
- "y = a*x^5 + b*x^4 + c*x^3 + d*x^2 + e*x + f",
- function=poly,
- parameters=['a', 'b', 'c', 'd', 'e', 'f'],
- estimate=estimate_quintic_poly,
- configure=configure,
- is_background=True))))