Plotting & visualisation
Every fit, posterior, and spectrum produced by the suite can be visualised with a small, consistent set of plotting helpers. They fall into three families:
Static (matplotlib) — publication-quality figures you save to PDF/PNG.
Interactive (plotly) — zoom/pan/hover figures for exploration, with an optional stacked 2-D rectified-spectrum panel.
MCMC diagnostics — corner plots, trace plots, and flux posteriors for
jwspecmcmcresults.
In addition, the redshift and
DLA result objects carry their own .plot() convenience
methods.
At a glance
Function |
Package |
Backend |
Input |
Use for |
|---|---|---|---|---|
|
matplotlib |
|
Publication figure of a completed fit (data + model + components + residuals) |
|
|
plotly |
|
Interactive inspection of a fit; optional 2-D panel |
|
|
plotly |
|
Look at one or more spectra before fitting; overplot lines |
|
|
matplotlib |
|
Static stacked 2-D + 1-D figure straight from a file |
|
|
matplotlib |
|
Posterior covariances / marginals |
|
|
matplotlib |
|
Chain mixing / convergence (emcee chains) |
|
|
matplotlib |
|
Flux posterior for one line |
Tip
An MCMCResult is accepted directly by plot_fit() and
plot_fit_interactive() — they auto-convert via
to_fit_result(). You do not need to convert manually to draw the fit.
Static fit figures — plot_fit
The workhorse for papers. Given a FitResult (from
fit_lines(), fit_with_broad(), or an
MCMCResult), it draws the data, the total model, the continuum, each
Gaussian component, and a residual panel — with the y-axis scaled to the
tallest emission line rather than to noise spikes.
import jwspecfit
result = jwspecfit.fit_lines("spectrum.fits", z=6.0)
# Quick look
fig = jwspecfit.plot_fit(result)
# Publication figure, saved straight to PDF
fig = jwspecfit.plot_fit(
result,
flux_unit="flam", # erg/s/cm²/Å instead of µJy
rest_frame=True, # divide wavelengths by (1+z)
show_components=True, # filled Gaussians; broad lines hatched
exclude_wave_A=[(13000, 13500)], # mask a noisy detector gap
save_path="fit.pdf",
)
Key parameters:
flux_unit"fnu"(µJy, default) or"flam"(erg/s/cm²/Å).wave_unit"A"(Ångström, default) or"um"(microns).rest_frameWhen
True, divides wavelengths by(1 + z)using the spectrum’s stored redshift. Axis labels switch to “Rest Wavelength”.show_componentsDraw each Gaussian as a filled curve. Broad Balmer components are drawn with hatching so they read clearly against the narrow lines.
show_residualsToggle the lower residual panel (default
True).y_padMultiplicative headroom above the tallest line peak (default
1.3).exclude_wave_AList of
(lo, hi)ranges in Ångström to hide — useful for masking detector gaps or contaminated regions.save_pathWrite the figure to disk (
.pdf,.png, …). Returns the figure either way.
See also
Full parameter list: jwspecfit.plot_fit().
Static 2-D + 1-D — plot_2d_1d
Renders a stacked figure — the 2-D rectified spectrum on top, the 1-D
extraction below — directly from an msaexp-style .spec.fits file, with no
fit required. Ideal for a first look at the data or a context panel in a
figure.
fig, (ax_2d, ax_1d) = jwspecfit.plot_2d_1d(
"target.spec.fits",
z=6.0, # places observed-frame line markers
cmap="Blues",
flux_scale=1000.0, # µJy → nJy on the 1-D panel
flux_label=r"$F_\nu$ [nJy]",
add_lines={"Mg II 2796": 2796.352}, # extra label not in the defaults
)
fig.savefig("context.pdf", dpi=300)
It returns the figure and both axes, so you can annotate or restyle
afterwards (e.g. ax_1d.set_xlim(...)). The lines / add_lines arguments
take the same forms as the interactive helpers — see
Marking emission lines.
See also
Full parameter list: jwspecfit.plot_2d_1d().
Interactive spectra — plot_spectrum_interactive
Open one or more 1-D spectra in plotly before fitting — zoom, pan, hover
read-outs, an optional 2-D panel, and overplotted line markers. Accepts a
Spectrum, a file path, or a list of either (to overplot several spectra).
# Single file
fig = jwspecfit.plot_spectrum_interactive("spectrum.fits", z=6.0)
# A rest-frame stack saved as .npz
fig = jwspecfit.plot_spectrum_interactive("stack.npz", rest_frame=True)
# An in-memory Spectrum, in F_lambda units
fig = jwspecfit.plot_spectrum_interactive(spec, flux_unit="flam")
# Overplot several spectra with custom legend labels
fig = jwspecfit.plot_spectrum_interactive(
["g140m.fits", "g235m.fits"],
z=6.0,
labels=["G140M", "G235M"],
)
fig.show()
The 2-D rectified panel appears automatically (show_2d="auto") whenever a
single spectrum is supplied and its sci_2d array is populated (e.g.
read from a .spec.fits file). Set show_2d=False to force 1-D only.
Any extra keyword arguments are forwarded to the file reader, so FITS column overrides work inline:
fig = jwspecfit.plot_spectrum_interactive(
"custom.fits", z=6.0, hdu="EXTRACT1D", wave_col="WAVELENGTH",
)
See also
Full parameter list: jwspecfit.plot_spectrum_interactive().
Interactive fits — plot_fit_interactive
The plotly counterpart to plot_fit. Same data/model/components/residual
layout, but zoomable and with an optional stacked 2-D panel above the fit.
fig = jwspecfit.plot_fit_interactive(result)
fig.show()
# Rest-frame, with a curated set of line markers added on top
fig = jwspecfit.plot_fit_interactive(
result, rest_frame=True, lines=None, # None opts in to default markers
)
By default (lines=False) no curated markers are drawn, because every
fitted line is already labelled at its peak above the model. Pass
lines=None for the package-default marker set, or an explicit list of line
keys to mark only those.
See also
Full parameter list: jwspecfit.plot_fit_interactive().
Marking emission lines
The interactive helpers and plot_2d_1d share a common vocabulary for
emission-line markers via the lines and add_lines arguments.
linesControls the curated default set.
Nonedraws the package defaults,Falsedraws none, and a list of keys fromjwspecfit.lines.REST_LINES_Amarks only those.add_linesExtra markers layered on top, in two accepted forms:
list of names from
REST_LINES_A— wavelengths looked up automatically, e.g.add_lines=["H8", "HEPSILON", "FeII_2382"].dict of
{label: rest_wavelength_A}for free-form labels at an explicit rest wavelength in Ångström, e.g.add_lines={"Mg II 2796": 2796.352}.
# What line names are available?
jwspecfit.show_lines()
Markers are redshifted by (1 + z) (the effective z comes from the z
argument, else the spectrum’s own spec.z); in rest-frame mode they sit at
the rest wavelengths.
MCMC diagnostics
These live in jwspecmcmc and take an MCMCResult. See the
jwspecmcmc guide for how to produce one.
Corner plots — plot_corner
Posterior marginals and pairwise covariances.
import jwspecmcmc
result = jwspecmcmc.fit_lines("spectrum.fits", z=6.0)
# All free parameters
fig = jwspecmcmc.plot_corner(result)
# A focused subset
fig = jwspecmcmc.plot_corner(
result,
params=["A_OIII_5007", "A_HBETA", "sigma_Ha"],
quantiles=[0.16, 0.5, 0.84], # default: 16/50/84th percentiles
)
Extra keyword arguments are forwarded to corner.corner.
Trace plots — plot_traces
Per-walker chains, for eyeballing mixing and convergence.
fig = jwspecmcmc.plot_traces(result)
fig = jwspecmcmc.plot_traces(result, params=["A_HBETA"])
Note
plot_traces needs per-chain samples, so it works for emcee results.
A nautilus/NUTS result without stored chains raises ValueError — judge
those runs with R̂ and ESS from result.summary() and with plot_corner.
Flux posteriors — plot_flux_posterior
Histogram of the integrated-flux posterior for a single line — the honest picture of a flux measurement and its (often asymmetric) uncertainty.
ax = jwspecmcmc.plot_flux_posterior(result, "OIII_5007", bins=50)
# Compose several onto shared axes
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
for line in ("OIII_5007", "HBETA"):
jwspecmcmc.plot_flux_posterior(result, line, ax=ax)
See also
Full parameter lists: jwspecmcmc.plot_corner(),
jwspecmcmc.plot_traces(), jwspecmcmc.plot_flux_posterior().
Result-object convenience plots
Redshift scan — RedshiftResult.plot()
fit_redshift() returns a RedshiftResult whose .plot()
draws a two-panel plotly diagnostic: Δχ²(z) with 1/2/3σ thresholds and
candidate peaks on top, and the spectrum at the best redshift below.
zres = jwspecfit.fit_redshift("spectrum.fits")
zres.plot().show()
DLA fit — DLAResult.plot()
fit_NHI() returns a DLAResult whose .plot() overlays the
fitted Lyα/DLA profile on the data with a residual panel.
result = jwspecfit.fit_NHI(spec, z=6.5, R=spec.R, n_live=400, seed=42)
print(result.summary())
result.plot(flux_unit="flam")
Choosing a backend
Static (matplotlib) |
Interactive (plotly) |
|
|---|---|---|
Best for |
Paper figures, batch scripts |
Exploration, picking masks, QA |
Output |
PDF / PNG via |
|
2-D panel |
|
|
Returns |
|
|
A common workflow: explore with plot_spectrum_interactive to choose
exclude_wave_A masks and confirm the redshift, fit, then render the final
figure with plot_fit(..., save_path=...).