import numpy as np
from matplotlib import pyplot as plt
from mcalf.profiles.voigt import double_voigt, voigt
from mcalf.utils.spec import reinterpolate_spectrum
__all__ = ['plot_ibis8542', 'plot_spectrum']
[docs]def plot_ibis8542(wavelengths, spectrum, fit=None, background=0, sigma=None, sigma_scale=70, stationary_line_core=None,
subtraction=False, separate=False, output=None, figsize=None, legend_position='best', dpi=None,
fontfamily=None, reduced_legend=False, show_intensity=True, hook=None):
"""Plot an IBIS8542Model fit
It is recommended to use the plot method on either an IBIS8542Model or a FitResult from an IBIS8542Model instead.
Parameters
----------
wavelengths : ndarray
The x-axis values.
spectrum : ndarray, length=n_wavelengths
The y-axis values.
fit : array_like, optional, default = None
The fitted parameters.
background : float or ndarray of length n_wavelengths, optional, default = 0
The background to add to the fitted profiles.
sigma : ndarray, length=n_wavelengths, optional, default = None
The sigma profile used when fitting the parameters to `spectrum`. If given, will be plotted as shaded regions.
sigma_scale : float, optional, default = 70
A factor to multiply the error bars to change their prominence.
stationary_line_core : float, optional, default = None
If given, will show a dashed line at this wavelength.
subtraction : bool, optional, default = False
Whether to plot the `spectrum` minus emission fit (if exists) instead.
separate : bool, optional, default = False
Whether to plot the fitted profiles separately (if multiple components exist).
output : str, optional, default = None
If present, the filename to save the plot as.
figsize : 2-tuple, optional
Size of the figure.
legend_position : str or int or pair of floats, optional, default = 'best'
Position of the legend. See `matplotlib.pyplot.legend` documentation from possible values.
dpi : int
The number of dots per inch. For controlling the quality of the outputted figure.
fontfamily : str, optional, default = None
If provided, this family string will be added to the 'font' rc params group.
reduced_legend : bool, optional, default = False
Whether to add to the legend the labels that would be displayed on an absorption only plot. Useful for saving
space when plotting both a single component fit and a multi-component fit alongside each other.
show_intensity : bool, optional, default = True
Whether to show the intensity axis tick labels and axis label.
hook : callable, optional, default = None
If provided this function must accept the current `plt' as a single argument such that it can operate upon
it and make changes to the plot.
See Also
--------
models.IBIS8542.plot : General plotting method
models.IBIS8542.plot_separate : Plot the fit parameters separately
models.IBIS8542.plot_subtraction : Plot the spectrum with the emission fit subtracted from it
models.FitResult.plot : Plotting method on the fit result
"""
# Choose the function to plot the fitted parameters with
if fit is not None and len(fit) == 8:
fit_function = double_voigt
else:
fit_function = voigt
data_label = 'observation' # Default label
if fontfamily is not None:
plt.rc('font', family=fontfamily)
plt.figure(figsize=figsize, dpi=dpi)
# If a subtraction is requested, make the relevant changes
if subtraction and fit is not None and len(fit) == 8:
spectrum = spectrum - voigt(wavelengths, *fit[4:], 0, clib=False)
sigma = None
fit = None
data_label = 'observation - emission profile'
# If sigma is given, make a shaded region around the spectral data.
if sigma is not None and fit is not None:
plt.fill_between(wavelengths, spectrum-sigma*sigma_scale, spectrum+sigma*sigma_scale, color='lightgrey')
# Plot the spectral data
label = None if reduced_legend else data_label
plt.plot(wavelengths, spectrum, color='#006BA4', label=label)
# Plot the fitted profiles if parameters are given
if fit is not None:
if separate and len(fit) > 4: # Plot each component separately
label = None if reduced_legend else 'combined profile'
plt.plot(wavelengths, double_voigt(wavelengths, *fit, background, clib=False),
color='#FF800E', label=label)
plt.plot(wavelengths, voigt(wavelengths, *fit[:4], background, clib=False),
color='#A2C8EC', label='absorption profile')
plt.plot(wavelengths, voigt(wavelengths, *fit[4:], 0, clib=False),
color='#595959', label='emission profile')
else: # Plot a combined profile
plt.plot(wavelengths, fit_function(wavelengths, *fit, background, clib=False),
color='#FF800E', label='fitted profile')
label = None if reduced_legend else 'absorption line core'
plt.axvline(x=fit[1], linestyle='--', color='#A2C8EC', label=label)
if fit_function is double_voigt:
plt.axvline(x=fit[5], linestyle=':', color='#595959', label='emission line core')
if stationary_line_core is not None: # Plot vertical dashed line
label = None if reduced_legend else 'stationary line core'
plt.axvline(x=stationary_line_core, linestyle='--', color='#ABABAB', label=label)
plt.xlabel('Wavelength (Å)')
if not reduced_legend and show_intensity:
plt.ylabel('Intensity')
if not show_intensity:
plt.gca().axes.get_yaxis().set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['left'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.legend(loc=legend_position)
plt.minorticks_on()
if hook is not None:
hook(plt)
if output is not None:
plt.savefig(output, dpi=dpi, bbox_inches='tight')
plt.show()
plt.close()
[docs]def plot_spectrum(wavelengths, spectrum, output=None, normalised=True, smooth=True,
figsize=(7, 3), dpi=600, fontfamily=None):
"""Plot a spectrum with the wavelength grid shown.
Intended for plotting the raw data.
Parameters
----------
wavelengths : ndarray
The x-axis values.
spectrum : ndarray, length=n_wavelengths
The y-axis values.
output : str, optional, default = None
If present, the filename to save the plot as.
normalised : bool, optional, default = True
Whether to normalise the spectrum using the last three spectral points.
smooth : bool, optional, default = True
Whether to smooth the `spectrum` with a spline.
figsize : 2-tuple, optional, default = None
Size of the figure.
dpi : int, optional, default = 600
The number of dots per inch. For controlling the quality of the outputted figure.
fontfamily : str, optional, default = None
If provided, this family string will be added to the 'font' rc params group.
"""
if fontfamily is not None:
plt.rc('font', family=fontfamily)
dense_wavelengths = np.linspace(wavelengths[0], wavelengths[-1], num=200) if smooth else wavelengths
dense_spectrum = reinterpolate_spectrum(spectrum, wavelengths, dense_wavelengths) if smooth else spectrum
if normalised:
norm = np.mean(spectrum[-3:])
dense_spectrum /= norm
spectrum /= norm
fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
for i in range(len(wavelengths)):
ax.axvline(x=wavelengths[i], linestyle='--', linewidth=0.5, c='black')
ax.plot(dense_wavelengths, dense_spectrum, color='black')
ax.plot(wavelengths, spectrum, marker='o', linestyle='None', color='black', markersize=4)
ylabel = 'Normalised Intensity ($I/I_c$)' if normalised else 'Intensity ($I$)'
ax.set_ylabel(ylabel)
ax.set_xlabel('Wavelength (Å)')
fig.subplots_adjust(bottom=0.15)
plt.show()
if output is not None and isinstance(output, str):
fig.savefig(output, bbox_inches='tight', dpi=dpi)