Saba: Sherpa-Astropy Bridge¶
The Saba package provides a bridge between the convenient model definition
language provided in the astropy.modeling package and the powerful fitting
capabilities of the Sherpa modeling and fitting package. In particular,
Sherpa has a selection of robust optimization algorithms coupled with
configurable fit statistics. Once the model fit is complete Sherpa has three
different ways to estimate parameter confidence intervals, including methods
that allow for coupled non-gaussian errors. Finally, Sherpa has an MCMC
sampler that can be used to generate draws from the probability distribution
assuming given priors.
Once Saba and Sherpa are installed, the Saba package exposes the above
Sherpa functionality within the astropy.modeling.fitting package via
a single SherpaFitter class which acts as a fitting backend
within astropy. A plugin registry system automatically makes the
SherpaFitter class available within the
astropy.modeling.fitting module without requiring an explicit
import.
Saba is the Sherpa people’s word for “bridge”.
Installation¶
The following installation notes apply to the development version of Saba and assume use of the conda + Anaconda package system.
Prerequisites¶
Python 3.5+
numpy 1.12+
astropy 2.0 or higher (3.0 or higher recommended, as support for 2.0 will be dropped soon)
sherpa 4.11
conda install numpy
To make use of the entry points plugin registry which automatically makes the SherpaFitter class available within astropy.modeling.fitting install astropy version >= 1.3.
Otherwise one can just use the latest stable astropy via:
conda install astropy
Next install Sherpa using the conda sherpa channel. Note that Sherpa
currently needs to be installed after astropy on Mac OSX.
conda install -c sherpa sherpa
Finally install saba using pip:
pip install saba
Getting started¶
If you are not already familiar with astropy.modeling, now is a good time to
review the introductory documentation there along with the
astropy.modeling.fitting module details.
To start with Saba let’s import the SherpaFitter class which is the interface
with Sherpa’s fitting routines. SherpaFitter is available in one of
two ways, either directly from saba or through astropy.modeling.fitting
through the plugin registry system. The latter method is preferred but requires
astropy version >= 1.3 or the latest development (master) version. Use:
from saba import SherpaFitter
or
from astropy.modeling.fitting import SherpaFitter
Initialization¶
To initialize a fitter we provide string values to define the statistic,
optimizer and estmethod (error estimation method). The available
values for those can be found in the docstring of SherpaFitter and
relate to objects within sherpa.stats, sherpa.optmethods and
sherpa.estmethods.
sfit = SherpaFitter(statistic='chi2', optimizer='levmar', estmethod='confidence')
Now that we have a fitter instance we need something to fit. So let’s import an
astropy model, specifically Gaussian1D. A
full description astropy’s model and capabilities can be found in the astropy
Instantiating and Evaluating Models section.
from astropy.modeling.models import Gaussian1D
We also need some data so let’s make some data with some added noise.
import numpy as np
from astropy.modeling.models import Gaussian1D, Gaussian2D
import matplotlib.pyplot as plt
np.random.seed(0x1337)
true = Gaussian1D(amplitude=3, mean=0.9, stddev=0.5)
err = 0.05
step = 0.1
x = np.arange(-3, 3, step)
y = true(x) + err * np.random.uniform(-1, 1, size=len(x))
yerrs = err * np.random.uniform(0.2, 1, size=len(x))
binwidth = step * np.ones(x.shape)
fit_model = true.copy() # ofset fit model from true
fit_model.amplitude = 2
fit_model.mean = 0
fit_model.stddev = 0.2
plt.plot(x, true(x), label="True")
plt.errorbar(x, y, xerr=binwidth, yerr=yerrs, ls="", label="Data")
plt.plot(x, fit_model(x), label="Starting fit model")
plt.legend(loc=2, frameon=False)
plt.xlim((-3, 3))
plt.show()
(Source code, png, hires.png, pdf)
Fitting¶
Now we have some data let’s fit it and hopefully we get something similar to
“True” back. The sfit fitter object has already been initialized (as would
be done for other astropy.modeling.fitting fitters) so we just call it with
some data and an astropy model and we get the fitted model returned.
fitted_model = sfit(fit_model, x, y, xbinsize=binwidth, err=yerrs)
plt.plot(x, true(x), label="True")
plt.errorbar(x, y, xerr=binwidth, yerr=yerrs, ls="", label="Data")
plt.plot(x, fit_model(x), label="Starting fit model")
plt.plot(x, fitted_model(x), label="Fitted model")
plt.legend(loc=2, frameon=False)
plt.xlim((-3, 3))
Once again plotting the data.
(Source code, png, hires.png, pdf)
Now we have a fit we can look at the outputs by doing:
print(sfit.fit_info)
datasets = None
itermethodname = none
methodname = levmar
statname = chi2
succeeded = True
parnames = ('wrap_.amplitude', 'wrap_.mean', 'wrap_.stddev')
parvals = (3.0646789274093185, 0.77853851419777986, 0.50721937454701504)
statval = 82.7366242121
istatval = 553.030876852
dstatval = 470.29425264
numpoints = 30
dof = 27
qval = 1.44381192266e-07
rstat = 3.06431941526
message = successful termination
nfev = 84
Note that the fit_info attribute is custom to the SherpaFitter class and
provides a direct link to the internal fitting results from the Sherpa fit
process.
Uncertainty estimation¶
One of the main drivers for Saba is to get access the uncertainty estimation
methods provided by Sherpa. This is done through the
est_errors method which uses the Sherpa’s
est_errors method. To get the errors make a call such as:
param_errors = sfit.est_errors(sigma=3) # Note that sigma can be an input
In return we get a tuple of (parameter_name, best_fit_value,
lower_value , upper_value). For the sake of plotting them we make
models for the upper and lower values, and then output the values while we’re at it.
min_model = fitted_model.copy()
max_model = fitted_model.copy()
for pname, pval, pmin, pmax in list(zip(*param_errors)):
print(pname, pval, pmin, pmax)
getattr(min_model, pname).value = pval + pmin
getattr(max_model, pname).value = pval + pmax
('amplitude', 3.0646789274093185, -0.50152026852144349, 0.56964617033348119)
('mean', 0.77853851419777986, -0.096264447380365548, 0.10293940565584792)
('stddev', 0.50721937454701504, -0.098092469817728456, 0.11585973498734969)
(Source code, png, hires.png, pdf)
Using Saba¶
API/Reference¶
Credit¶
The development of this package was made possible by the generous support of the Google Summer of Code program in 2016 under the OpenAstronomy by Michele Costa with the support and advice of mentors Tom Aldcroft, Omar Laurino, Moritz Guenther, and Doug Burke.