Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
95f40d6
initial fitting structure
brechmos May 29, 2018
01e8977
added more documentation
brechmos May 29, 2018
83a91bc
fixed for rename
brechmos May 30, 2018
a215257
added first pass at fitting document
brechmos May 30, 2018
d975f2c
added documentation, small dir refactor
brechmos May 30, 2018
98659a9
fixed *two* typos in the automodapi
brechmos May 30, 2018
084dba7
fixed change in directory structure
brechmos May 30, 2018
8cc476b
added in region cutouts (excise) of spectra.
brechmos Jun 25, 2018
b20ce6a
updated api and tests
brechmos Jul 16, 2018
5a29bd5
changed name to fit_lines to match erick's notebook
brechmos Jul 16, 2018
6e237d5
udpated the continuum fitting and tests
brechmos Jul 16, 2018
dcf981f
set the tolerance
brechmos Jul 16, 2018
f14f0f9
first pass at cleanup of fitting (model and compoundmodels)
brechmos Aug 8, 2018
fa0c5f6
continuum fixes
brechmos Aug 9, 2018
52ef187
fixed for Chebyshev1D model
brechmos Aug 14, 2018
a023b4b
remove units, fit, add units back
brechmos Aug 14, 2018
8bf3ecb
works for just adding models
brechmos Aug 15, 2018
d9f4d89
works for single models, compound models with units
brechmos Aug 15, 2018
7b3f8d3
all tests confirmed based on plotting
brechmos Aug 15, 2018
d616d74
fixed for polynomial based models
brechmos Aug 17, 2018
c9b77eb
fix docstring line continuation
brechmos Aug 17, 2018
3b4597f
added fitting into index doctree
brechmos Aug 17, 2018
ffa15f4
added text re initial guess code will be added later.
brechmos Sep 4, 2018
6c37563
updates and mods based on PR comments
brechmos Sep 4, 2018
b9fe3e4
updated to changed method name
brechmos Sep 4, 2018
a8d55a2
added dynamically generated plots
brechmos Sep 4, 2018
906bef9
removed unnecesary static images
brechmos Sep 4, 2018
cdc3972
added continuum fitting
brechmos Sep 4, 2018
3273b62
more contextual words about fitting in the docs
eteq Sep 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions docs/fitting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
===================
Spectrum Fitting
===================

Specutils has the ability to fit the flux of a `~specutils.Spectrum1D` object.
The fit routine takes the `~specutils.Spectrum1D` object and a list of
`~astropy.modeling.Model` that have initial guesses for each of the parameters.

The internal functionality uses `~astropy.modeling.fitting` routines. The flux
is extracted from the `~specutils.Spectrum1D` and is passed along with a
compound model created from the model initial guesses.

Model Fitting
-------------

The first step is to create a set of models with initial guesses as the parameters. Even
better is to include a set of bounds for each parameter, but that is optional.

.. code-block:: python

>>> from astropy.modeling import models
>>> from specutils import Spectrum1D
>>> from specutils import fitting
>>> import astropy.units as u
>>> import numpy as np
>>> np.random.seed(42)


For the purpose of the example, build a ``spectrum1d`` variable that will be used in the fitting:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"variable" -> "object"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k


.. code-block:: python

>>> # Create the wavelength array
>>> wave_um = np.linspace(0.4, 1.05, 100) * u.um

>>> # Create the models
>>> g1 = models.Gaussian1D(amplitude=2000*u.mJy, mean=0.52*u.um, stddev=0.01*u.um)
>>> g2 = models.Gaussian1D(amplitude=500*u.mJy, mean=0.64*u.um, stddev=0.02*u.um)
>>> g3 = models.Gaussian1D(amplitude=-350*u.mJy, mean=0.78*u.um, stddev=0.01*u.um)

>>> # Create the flux array
>>> base_flux = g1(wave_um) + g2(wave_um) + g3(wave_um)

>>> # Create the `specutils.Spectrum1D` object.
>>> flux_e1 = base_flux + 100*np.random.random(base_flux.shape)*u.mJy
>>> spectrum1d = Spectrum1D(spectral_axis=wave_um, flux=flux_e1)


.. figure:: img/fitting_original.jpg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the result of the above, right? If you use the plot sphinx directive, it will automatically do this instead of you having to manage the image files.

See e.g. http://docs.astropy.org/en/stable/visualization/normalization.html#matplotlib-normalization
and the corresponding rst at
https://github.com/astropy/astropy/blob/master/docs/visualization/normalization.rst

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look at this.

:figwidth: 500px
:align: center

Spectrum to be fit.

Now that there is a ``spectrum1d`` to fit, the real fitting setup must happen. First create
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

``spectrum1d``

->

`~specutils.Spectrum1D` 

(to make it a live link)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k

the `~astropy.modeling.Model` to be used in the fitting routine.

.. code-block:: python

>>> # Create the initial guesses
>>> fg1 = models.Gaussian1D(amplitude=1900*u.mJy, mean=0.5*u.um, stddev=0.02*u.um,
... bounds={'amplitude': (1700*u.mJy, 2400*u.mJy),
... 'mean': (0.3*u.um, 0.6*u.um),
... 'stddev': (0.001*u.um, 0.03*u.um)})
>>> fg2 = models.Gaussian1D(amplitude=400*u.mJy, mean=0.63*u.um, stddev=0.03*u.um,
... bounds={'amplitude': (200*u.mJy, 600*u.mJy),
... 'mean': (0.6*u.um, 0.7*u.um),
... 'stddev': (0.001*u.um, 0.03*u.um)})
>>> fg3 = models.Gaussian1D(amplitude=-500*u.mJy, mean=0.79*u.um, stddev=0.02*u.um,
... bounds={'amplitude': (-600*u.mJy, -300*u.mJy),
... 'mean': (0.7*u.um, 0.82*u.um),
... 'stddev': (0.001*u.um, 0.03*u.um)})

Now comes the actual fitting:

.. code-block:: python

>>> # And... now we can do the fitting
>>> fitted_models = fitting.fit_lines(spectrum1d, fg1+fg2+fg3)

The output of the fitting routine is another set of models whose parameters contain the result of the fit.
(There is a corresponding output model for each input model.)

.. figure:: img/fitting_original_fit.jpg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same comment here about the .. plot:: directive as the above one)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

:figwidth: 500px
:align: center

Original spectrum (blue) and fitted spectrum (orange).

.. automodapi:: specutils.fitting
Binary file added docs/img/fitting_original.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/fitting_original_fit.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Using specutils
basic_analysis
arithmetic
smoothing
fitting

.. toctree::
:maxdepth: 1
Expand Down
4 changes: 4 additions & 0 deletions specutils/fitting/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from __future__ import absolute_import

from .fitmodels import *
from .continuum import *
100 changes: 100 additions & 0 deletions specutils/fitting/continuum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from __future__ import division

import itertools
import operator

from astropy.modeling.polynomial import Chebyshev1D
from astropy.modeling.fitting import SLSQPLSQFitter

from ..fitting import fit_lines
from ..manipulation.smoothing import median_smooth


__all__ = ['fit_continuum', 'fit_continuum_generic']


def fit_continuum_generic(spectrum, model=Chebyshev1D(3), fitter=SLSQPLSQFitter(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about fit_generic_continuum instead? I get the motivation to have parallel names, but in general I think it's better to use "real words" - that is, make the code literally readable as english where practical.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

exclude_regions=None, weights=None):
"""
Basic fitting of the continuum of an input spectrum. The input
spectrum is smoothed using a median filter to remove the spikes.

Parameters
----------
spectrum : Spectrum1D
The spectrum object overwhich the equivalent width will be calculated.

model : list of `~astropy.modeling.Model`
The list of models that contain the initial guess.

exclude_regions : list of 2-tuples
List of regions to exclude in the fitting. Passed through
to the fitmodels routine.

weights : list (NOT IMPLEMENTED YET)
List of weights to define importance of fitting regions.

Returns
-------
continuum_spectrum : Spectrum `~specutils.Spectrum1D`
Underlying continuum based on a Chebyshev1D fit (default).

Notes
-----
* Could add functionality to set the bounds in
``model`` if they are not set.
* The models in the list of ``model`` are added together and passed as a
compound model to the `~astropy.modeling.fitting.Fitter` class instance.

"""

#
# Simple median smooth to remove spikes and peaks
#

spectrum_smoothed = median_smooth(spectrum, 3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest having the median filter window be variable? I.e., just change the 3 to a keyword.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in the keyword


#
# Return the fitted continuum
#

return fit_continuum(spectrum_smoothed, model, fitter, exclude_regions, weights)


def fit_continuum(spectrum, model=Chebyshev1D(3), fitter=SLSQPLSQFitter(),
exclude_regions=None, weights=None):
"""
Entry point for fitting using the `~astropy.modeling.fitting`
machinery.

Parameters
----------
spectrum : Spectrum1D
The spectrum object overwhich the equivalent width will be calculated.

model: list of `~astropy.modeling.Model`
The list of models that contain the initial guess.

fitmodels_type: str
String representation of fit method to use as defined by the dict fitmodels_types.

window : tuple of wavelengths (NOT IMPLEMENTED YET)
Start and end wavelengths used for fitting.

weights : list (NOT IMPLEMENTED YET)
List of weights to define importance of fitting regions.

Returns
-------
models : list of `~astropy.modeling.Model`
The list of models that contain the fitted model parmeters.

"""

#
# Fit the flux to the model.
#

continuum_spectrum = fit_lines(spectrum, model, fitter, exclude_regions, weights)

return continuum_spectrum
Loading