Interactive fits¶
This notebook showcases the interactive fitting capability of iminuit. Interactive fitting is useful to find good starting values and to debug the fit.
Note: If you see this notebook on ReadTheDocs or otherwise statically rendered, changing the sliders won’t change the plot. This requires a running Jupyter kernel.
[1]:
from iminuit import cost
from iminuit import Minuit
from numba_stats import norm, t, bernstein, truncexpon
import numpy as np
from matplotlib import pyplot as plt
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
File __init__.pxd:942, in numpy.import_array()
RuntimeError: module compiled against API version 0x10 but this version of numpy is 0xf
During handling of the above exception, another exception occurred:
ImportError Traceback (most recent call last)
Input In [1], in <cell line: 3>()
1 from iminuit import cost
2 from iminuit import Minuit
----> 3 from numba_stats import norm, t, bernstein, truncexpon
4 import numpy as np
5 from matplotlib import pyplot as plt
File ~/python-iminuit/src/python-iminuit/test-env/lib/python3.10/site-packages/numba_stats/norm.py:9, in <module>
1 """
2 Normal distribution.
3
(...)
6 scipy.stats.norm: Scipy equivalent.
7 """
8 import numpy as np
----> 9 from ._special import erfinv as _erfinv
10 from ._util import _jit, _trans, _generate_wrappers, _prange
11 from math import erf as _erf
File ~/python-iminuit/src/python-iminuit/test-env/lib/python3.10/site-packages/numba_stats/_special.py:7, in <module>
5 from numba.extending import get_cython_function_address
6 from numba.types import WrapperAddressProtocol, float64
----> 7 import scipy.special.cython_special as cysp
10 def get(name, signature):
11 # create new function object with correct signature that numba can call by extracting
12 # function pointer from scipy.special.cython_special; uses scipy/cython internals
13 index = 1 if signature.return_type is float64 else 0
File /usr/lib/python3.10/site-packages/scipy/special/__init__.py:649, in <module>
1 """
2 ========================================
3 Special functions (:mod:`scipy.special`)
(...)
644
645 """
647 from ._sf_error import SpecialFunctionWarning, SpecialFunctionError
--> 649 from . import _ufuncs
650 from ._ufuncs import *
652 from . import _basic
File /usr/lib/python3.10/site-packages/scipy/special/_ufuncs.pyx:1, in init scipy.special._ufuncs()
File scipy/special/_ufuncs_extra_code_common.pxi:34, in init scipy.special._ufuncs_cxx()
File __init__.pxd:944, in numpy.import_array()
ImportError: numpy.core.multiarray failed to import
UnbinnedNLL¶
[2]:
rng = np.random.default_rng(1)
s = rng.normal(0.5, 0.1, size=1000)
b = rng.exponential(1, size=1000)
b = b[b < 1]
x = np.append(s, b)
truth = len(s) / len(x), 0.5, 0.1, 1.0
n, xe = np.histogram(x, bins=50)
def model(x, f, mu, sigma, slope):
return f * norm.pdf(x, mu, sigma) + (1 - f) * truncexpon.pdf(x, 0, 1, 0, slope)
c = cost.UnbinnedNLL(x, model)
m = Minuit(c, *truth)
m.limits["f", "mu"] = (0, 1)
m.limits["sigma", "slope"] = (0, None)
m.interactive(model_points=1000)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [2], in <cell line: 1>()
----> 1 rng = np.random.default_rng(1)
3 s = rng.normal(0.5, 0.1, size=1000)
4 b = rng.exponential(1, size=1000)
NameError: name 'np' is not defined
ExtendedUnbinnedNLL¶
[3]:
rng = np.random.default_rng(1)
s = rng.normal(0.5, 0.1, size=1000)
b = rng.exponential(1, size=1000)
b = b[b < 1]
x = np.append(s, b)
truth = len(s), 0.5, 0.1, len(b), 1.0
n, xe = np.histogram(x, bins=50)
def model(x, s, mu, sigma, b, slope):
x = s * norm.pdf(x, mu, sigma) + b * truncexpon.pdf(x, 0, 1, 0, slope)
return s + b, x
c = cost.ExtendedUnbinnedNLL(x, model)
m = Minuit(c, *truth)
m.limits["mu"] = (0, 1)
m.limits["sigma", "slope", "s", "b"] = (0, None)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [3], in <cell line: 1>()
----> 1 rng = np.random.default_rng(1)
3 s = rng.normal(0.5, 0.1, size=1000)
4 b = rng.exponential(1, size=1000)
NameError: name 'np' is not defined
BinnedNLL¶
[4]:
def model(xe, f, mu, sigma, nuinv, slope):
nu = 1 / nuinv
a, b = t.cdf((0, 1), nu, mu, sigma)
sn = f * (t.cdf(xe, nu, mu, sigma) - a) / (b - a)
bn = (1 - f) * truncexpon.cdf(xe, 0, 1, 0, slope)
return sn + bn
rng = np.random.default_rng(1)
truth = 0.5, 0.5, 0.1, 0.1, 1
xe = np.linspace(0, 1, 100)
sm = truth[0] * np.diff(model(xe, 1, *truth[1:]))
bm = (1 - truth[0]) * np.diff(model(xe, 0, *truth[1:]))
n = rng.poisson(1000 * np.diff(model(xe, *truth)))
c = cost.BinnedNLL(n, xe, model)
m = Minuit(c, *truth)
m.limits["sigma", "slope"] = (0, None)
m.limits["mu", "f", "nuinv"] = (0, 1)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [4], in <cell line: 8>()
5 bn = (1 - f) * truncexpon.cdf(xe, 0, 1, 0, slope)
6 return sn + bn
----> 8 rng = np.random.default_rng(1)
10 truth = 0.5, 0.5, 0.1, 0.1, 1
12 xe = np.linspace(0, 1, 100)
NameError: name 'np' is not defined
[5]:
c = cost.BinnedNLL(n, xe, model)
cx = 0.5 * (xe[1:] + xe[:-1])
c.mask = np.abs(cx - 0.5) > 0.3
m = Minuit(c, *truth)
m.limits["sigma", "slope"] = (0, None)
m.limits["mu", "f", "nuinv"] = (0, 1)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [5], in <cell line: 1>()
----> 1 c = cost.BinnedNLL(n, xe, model)
3 cx = 0.5 * (xe[1:] + xe[:-1])
4 c.mask = np.abs(cx - 0.5) > 0.3
NameError: name 'n' is not defined
ExtendedBinnedNLL¶
[6]:
def model(xe, s, mu, sigma, nuinv, b1, b2, b3):
nu = 1 / nuinv
sn = s * t.cdf(xe, nu, mu, sigma)
bn = bernstein.integral(xe, (b1, b2, b3), 0, 1)
return sn + bn
truth = 1000., 0.5, 0.1, 0.1, 1000., 3000., 2000.
xe = np.linspace(0, 1, 100)
rng = np.random.default_rng(1)
n = rng.poisson(np.diff(model(xe, *truth)))
c = cost.ExtendedBinnedNLL(n, xe, model)
m = Minuit(c, *truth)
m.limits["s", "sigma", "b1", "b2", "b3"] = (0, None)
m.limits["mu", "nuinv"] = (0, 1)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [6], in <cell line: 9>()
5 return sn + bn
7 truth = 1000., 0.5, 0.1, 0.1, 1000., 3000., 2000.
----> 9 xe = np.linspace(0, 1, 100)
11 rng = np.random.default_rng(1)
12 n = rng.poisson(np.diff(model(xe, *truth)))
NameError: name 'np' is not defined
You can pass a custom plotting routine with Minuit.interactive
to draw more detail. A simple function works that accesses data from the outer scope, but we create a class in the following example to store the cost function, which has all data we need, because we override the variables in the outer scope in this notebook.
[7]:
# Visualize signal and background components with different colors
class Plotter:
def __init__(self, cost):
self.cost = cost
def __call__(self, args):
xe = self.cost.xe
n = self.cost.data
cx = 0.5 * (xe[1:] + xe[:-1])
plt.errorbar(cx, n, n ** 0.5, fmt="ok")
sm = np.diff(self.cost.scaled_cdf(xe, *args[:4], 0, 0, 0))
bm = np.diff(self.cost.scaled_cdf(xe, 0, *args[1:]))
plt.stairs(bm, xe, fill=True, color="C1")
plt.stairs(bm + sm, xe, baseline = bm, fill=True, color="C0")
m.interactive(Plotter(c))
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [7], in <cell line: 16>()
13 plt.stairs(bm, xe, fill=True, color="C1")
14 plt.stairs(bm + sm, xe, baseline = bm, fill=True, color="C0")
---> 16 m.interactive(Plotter(c))
NameError: name 'm' is not defined
[8]:
c = cost.ExtendedBinnedNLL(n, xe, model)
cx = 0.5 * (xe[1:] + xe[:-1])
c.mask = np.abs(cx - 0.5) > 0.3
m = Minuit(c, *truth)
m.limits["s", "sigma", "nuinv", "b1", "b2", "b3"] = (0, None)
m.limits["mu", "nuinv"] = (0, 1)
m.fixed["s", "mu", "sigma", "nuinv"] = True
m.values["s"] = 0
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [8], in <cell line: 1>()
----> 1 c = cost.ExtendedBinnedNLL(n, xe, model)
3 cx = 0.5 * (xe[1:] + xe[:-1])
4 c.mask = np.abs(cx - 0.5) > 0.3
NameError: name 'n' is not defined
BarlowBeestonLite¶
[9]:
xe = np.linspace(0, 1, 20)
bm = np.diff(truncexpon.cdf(xe, 0, 1, 0, 1))
sm = np.diff(norm.cdf(xe, 0.5, 0.1))
rng = np.random.default_rng(1)
n = rng.poisson(1000 * bm + 100 * sm)
b = rng.poisson(1e4 * bm)
s = rng.poisson(1e2 * sm)
c = cost.BarlowBeestonLite(n, xe, (b, s))
m = Minuit(c, 1000, 100, name=("b", "s"))
m.limits = (0, None)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [9], in <cell line: 1>()
----> 1 xe = np.linspace(0, 1, 20)
2 bm = np.diff(truncexpon.cdf(xe, 0, 1, 0, 1))
3 sm = np.diff(norm.cdf(xe, 0.5, 0.1))
NameError: name 'np' is not defined
[10]:
c = cost.BarlowBeestonLite(n, xe, (b, s))
cx = 0.5 * (xe[1:] + xe[:-1])
c.mask = np.abs(cx - 0.5) > 0.2
m = Minuit(c, 1000, 100, name=("b", "s"))
m.limits = (0, None)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [10], in <cell line: 1>()
----> 1 c = cost.BarlowBeestonLite(n, xe, (b, s))
2 cx = 0.5 * (xe[1:] + xe[:-1])
3 c.mask = np.abs(cx - 0.5) > 0.2
NameError: name 'n' is not defined
LeastSquares¶
[11]:
def model(x, a, b):
return a + b * x
truth = (1., 2.)
x = np.linspace(0, 1)
ym = model(x, *truth)
ye = 0.1
y = rng.normal(ym, ye)
c = cost.LeastSquares(x, y, ye, model)
m = Minuit(c, *truth)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [11], in <cell line: 5>()
2 return a + b * x
4 truth = (1., 2.)
----> 5 x = np.linspace(0, 1)
6 ym = model(x, *truth)
7 ye = 0.1
NameError: name 'np' is not defined
[12]:
c = cost.LeastSquares(x, y, ye, model)
c.mask = (x > 0.6) | (x < 0.2)
m = Minuit(c, *truth)
m.interactive()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [12], in <cell line: 1>()
----> 1 c = cost.LeastSquares(x, y, ye, model)
2 c.mask = (x > 0.6) | (x < 0.2)
4 m = Minuit(c, *truth)
NameError: name 'x' is not defined