# Example of using PyImfit to Estimate B/T Uncertainties

This is a Jupyter notebook demonstrating how to use PyImfit and bootstrap resampling to estimate uncertainties for derived quantities of fits, such as $B/T$ values.

If you are seeing this as part of the readthedocs.org HTML documentation, you can retrieve the original .ipynb file
[here](https://github.com/perwin/pyimfit/blob/master/docs/pyimfit_bootstrap_BtoT.ipynb).


## Introduction

PyImfit will estimate uncertainties for individual model parameters from a fit (if you use the default Levenberg-Marquardt minimizer) -- e.g., position $X0,Y0$, position-angles, ellipticities, scale lengths, etc.. You can also estimate parameter uncertainties via bootstrap resampling, or by using an external Markov-Chain Monte Carlo algorithm (see [here](./pyimfit_emcee.html) for an example of the latter).

Sometimes, you might also want to have some estimate of derived values based on a model, such as the total luminosity or the bulge/total ($B/T$) value (assuming you have some idea of which component in your model is the "bulge"). How do you determine the uncertainties for such quantities? This notebook shows a simple example of how one might do that, using PyImfit's bootstrap-resampling option.

The basic idea is to generate a set of model-parameter vectors via, e.g., bootstrap resampling (or from an MCMC chain). You then compute the resulting derived quantity from those parameter values. In this particular case, we use the `pyimfit.Imfit` object containing the model to compute "bulge" and total flux values for each parameter vector, and then take the ratio to get $B/T$ values. By doing this for all the parameter vectors, you end up with a distribution for the derived quantity.

**Preliminaries**

Some initial setup for nice-looking plots:


```python
%pylab inline

matplotlib.rcParams['figure.figsize'] = (8,6)
matplotlib.rcParams['xtick.labelsize'] = 16
matplotlib.rcParams['ytick.labelsize'] = 16
matplotlib.rcParams['axes.labelsize'] = 20
```

    Populating the interactive namespace from numpy and matplotlib


### Create an image-fitting model using PyImfit

Load the pymfit package; also load numpy and astropy.io.fits (so we can read FITS files):


```python
import numpy as np
import pyimfit
from astropy.io import fits
```

Load the data image (here, an SDSS $r$-band image cutout of VCC 1512) and corresponding mask:


```python
imageFile = "./pyimfit_bootstrap_BtoT_files/vcc1512rss_cutout.fits"
image_vcc1512 = fits.getdata(imageFile)
maskFile = "./pyimfit_bootstrap_BtoT_files/vcc1512rss_mask_cutout.fits"
mask_vcc1512 = fits.getdata(maskFile)
```

Create a ModelDescription instance based on an imfit configuration file (which specifies a Sersic + Exponential model):


```python
configFile = "./pyimfit_bootstrap_BtoT_files/config_imfit_vcc1512.dat"
model_desc = pyimfit.ModelDescription.load(configFile)
```


```python
print(model_desc)
```

    ORIGINAL_SKY	120.020408
    GAIN	4.725000
    READNOISE	4.300000
    X0		60.0
    Y0		73.0
    FUNCTION Sersic
    PA		155.0		90.0,180.0
    ell		0.2		0.0,0.5
    n		2.05		0.0,4.0
    I_e		120.0		0.0,10000.0
    r_e		4.5		0.0,20.0
    
    FUNCTION Exponential
    PA		140.0		90.0,180.0
    ell		0.28		0.0,0.8
    I_0		70.0		0.0,10000.0
    h		20.0		0.0,200.0
    


Create an Imfit instance containing the model, and add the image and mask data. Note that we are *not* doing PSF convolution, in order to save time (this is not meant to be a particular accurate model).


```python
imfit_fitter = pyimfit.Imfit(model_desc)
imfit_fitter.loadData(image_vcc1512, mask=mask_vcc1512)
```

Fit the model to the data (using the default Levenberg-Marquardt solver) and extract the best-fitting parameter values:


```python
results = imfit_fitter.doFit(getSummary=True)
```


```python
print(results)
```

                aic: 21156.824446201397
                bic: 21242.642276390998
       fitConverged: True
            fitStat: 21134.809840392267
     fitStatReduced: 1.169219398118625
              nIter: 10
          paramErrs: array([0.01518161, 0.0167467 , 1.88166351, 0.00733777, 0.01613089,
           1.9553319 , 0.05896027, 0.65080573, 0.00529781, 1.11196358,
           0.18740197])
             params: array([6.04336387e+01, 7.32059007e+01, 1.61799952e+02, 1.18947666e-01,
           9.56352657e-01, 1.21814611e+02, 4.86558532e+00, 1.38986928e+02,
           2.73912311e-01, 8.13853830e+01, 2.08521933e+01])
         solverName: 'LM'



```python
p_bestfit = results.params

print("Best-fitting parameter values:")
for i in range(len(p_bestfit) - 1):
    print("{0:g}".format(p_bestfit[i]), end=", ")
print("{0:g}\n".format(p_bestfit[-1]))
```

    Best-fitting parameter values:
    60.4336, 73.2059, 161.801, 0.118946, 0.956308, 121.821, 4.86538, 138.987, 0.273911, 81.389, 20.8517
    


### Run bootstrap-resampling to generate a set of parameter values (array of best-fit parameter vectors)

OK, now we're going to do some bootstrap resampling to build up a set of several hundred alternate "best-fit" parameter values.

Note that you coul also generate a set of parameter vectors using MCMC; we're doing bootstrap resampling mainly because it's faster.

Run 500 iterations of bootstrap resamplng. More would be better; this is just to save time (takes about 1 minute on a 2017 MacBook Pro).


```python
bootstrap_params_array = imfit_fitter.runBootstrap(500)
```


```python
bootstrap_params_array.shape
```




    (500, 11)



### Use these parameter vectors to calculate range of B/T values

We define a function to calculate the $B/T$ value, given a parameter vector (for this model it's simple, 
but you might have a more complicated model where the first component isn't necessarily the "bulge").


```python
def GetBtoT( fitter, params ):
    """
    Get the B/T value for a model parameter vector (where "bulge" is the first component
    in the model)
    
    Parameters
    ----------
    fitter : instance of PyImfit's Imfit class
        The Imfit instance containing the model and data to be fit
    params : 1D sequence of float
        The parameter vector corresponding to the model
    
    Returns
    -------
    B/T : float
    """
    total_flux, component_fluxes = fitter.getModelFluxes(params)
    # here, we assume the first component in the model is the "bulge"
    return component_fluxes[0] / total_flux

```

The $B/T$ value for the best-fit model:


```python
GetBtoT(imfit_fitter, p_bestfit)
```




    0.1557485598370547



Now calculate the $B/T$ values for the bootstrap-generated set of parameter vectors:


```python
n_param_vectors = params_array.shape[0]
b2t_values = [GetBtoT(imfit_fitter, bootstrap_params_array[i]) for i in range(n_param_vectors)]
b2t_values = np.array(b2t_values)
```

And now we can analyze the vector of B/T values ...

For example:


```python
np.mean(b2t_values)
```




    0.15852423639167879



A histogram of the $B/T$ values (vertical line = best-fit value):


```python
hist(b2t_values, bins=np.arange(0.14,0.2,0.0025));xlabel(r"$B/T$");ylabel(r"$N$")
axvline(GetBtoT(imfit_fitter, p_bestfit), color='k')
```




    <matplotlib.lines.Line2D at 0x12c671a10>




![png](pyimfit_bootstrap_BtoT_files/pyimfit_bootstrap_BtoT_37_1.png)



```python

```
