Source code for brutus.plotting.summary

"""
Combined summary plot: corner plot + posterior predictive SED in one figure.

Places the posterior predictive SED in the upper-right triangle of the
corner plot to eliminate white space and provide a single diagnostic view.
"""

import numpy as np

from .corner import cornerplot
from .sed import posterior_predictive


[docs] def summary_plot( models, idxs, reds, dreds, dists, params, data=None, data_err=None, data_mask=None, offset=None, coord=None, parallax=None, parallax_err=None, labels=None, weights=None, show_titles=True, color="black", vcolor="black", pcolor="black", R_solar=8.2, Z_solar=0.025, figsize=None, **corner_kwargs, ): """ Generate a combined corner plot with posterior predictive SED inset. Creates a corner plot of the stellar parameter posteriors with the posterior predictive SED placed in the upper-right triangle, making efficient use of the figure space. Parameters ---------- models : `~numpy.ndarray` of shape `(Nmodels, Nfilts, Ncoeffs)` Model magnitude polynomial coefficients. idxs : `~numpy.ndarray` of shape `(Nsamps)` Resampled model indices. reds : `~numpy.ndarray` of shape `(Nsamps)` A(V) samples. dreds : `~numpy.ndarray` of shape `(Nsamps)` R(V) samples. dists : `~numpy.ndarray` of shape `(Nsamps)` Distance samples in kpc. params : structured `~numpy.ndarray` with shape `(Nmodels,)` Model parameters for the corner plot axes. data : `~numpy.ndarray` of shape `(Nfilt)`, optional Observed flux data for the posterior predictive. data_err : `~numpy.ndarray` of shape `(Nfilt)`, optional Observed flux errors. data_mask : `~numpy.ndarray` of shape `(Nfilt)`, optional Binary mask for valid bands. offset : `~numpy.ndarray` of shape `(Nfilt)`, optional Multiplicative photometric offsets. coord : tuple, optional Galactic (l, b) coordinates in degrees. parallax : float, optional Parallax in mas. parallax_err : float, optional Parallax error in mas. labels : list, optional Filter names for the SED plot x-axis. weights : `~numpy.ndarray`, optional Importance weights for the samples. show_titles : bool, optional Whether to show parameter titles on the corner plot. color : str, optional Color for the corner plot. Default is ``'black'``. vcolor : str, optional Color for the SED violin plots. Default is ``'black'``. pcolor : str, optional Color for the SED data points. Default is ``'black'``. R_solar : float, optional Solar galactocentric radius in kpc. Default is 8.2. Z_solar : float, optional Solar height above midplane in kpc. Default is 0.025. figsize : tuple, optional Figure size. If None, determined automatically. **corner_kwargs Additional keyword arguments passed to ``cornerplot``. Returns ------- fig : `~matplotlib.figure.Figure` The combined figure. axes : `~numpy.ndarray` The corner plot axes array. ax_sed : `~matplotlib.axes.Axes` The posterior predictive SED axes. """ # Build parallax kwargs plx_kwargs = {} applied_parallax = False if parallax is not None and parallax_err is not None: if np.isfinite(parallax) and np.isfinite(parallax_err): plx_kwargs["parallax"] = parallax plx_kwargs["parallax_err"] = parallax_err applied_parallax = True # Generate the corner plot fig, axes = cornerplot( idxs, (dists, reds, dreds), params, coord=coord, applied_parallax=applied_parallax, weights=weights, show_titles=show_titles, color=color, R_solar=R_solar, Z_solar=Z_solar, **plx_kwargs, **corner_kwargs, ) # Find the upper-right triangle region for the SED inset. # The corner plot has ndim × ndim axes; upper-right is empty. ndim = axes.shape[0] # Place the SED in the upper-right block. # Use gridspec coordinates spanning the top rows and right columns. # The upper-right quadrant spans rows 0:ndim//2, cols ndim//2:ndim # For a nice layout, span from row 0 to row ndim//2-1, col ndim//2 to ndim-1 r_start = 0 r_end = max(ndim // 2, 2) c_start = r_end c_end = ndim # Get the position of the corner subplot grid to place the SED # by reading the bounding box of the relevant axes. # Add padding to avoid label collisions with the corner plot. pos_tl = axes[r_start, c_start].get_position() pos_br = axes[r_end - 1, c_end - 1].get_position() # Shrink and offset the SED inset to add padding on the left and # bottom edges (where corner plot labels live). pad_left = 0.06 pad_bottom = 0.18 shrink = 0.80 # shrink to 80% of available space full_left = pos_tl.x0 full_bottom = pos_br.y0 full_width = pos_br.x1 - pos_tl.x0 full_height = pos_tl.y1 - pos_br.y0 left = full_left + pad_left * full_width bottom = full_bottom + pad_bottom * full_height width = full_width * shrink height = full_height * shrink ax_sed = fig.add_axes([left, bottom, width, height]) # Save the corner plot's subplot params before calling posterior_predictive, # which calls plt.tight_layout() and would destroy the spacing. sp = fig.subplotpars saved = dict( left=sp.left, right=sp.right, top=sp.top, bottom=sp.bottom, wspace=sp.wspace, hspace=sp.hspace, ) # Generate the posterior predictive SED on the inset axes posterior_predictive( models, idxs=idxs, reds=reds, dreds=dreds, dists=dists, weights=weights, data=data, data_err=data_err, data_mask=data_mask, offset=offset, vcolor=vcolor, pcolor=pcolor, labels=labels, fig=(fig, ax_sed), ) ax_sed.set_title("Posterior Predictive SED", fontsize=9) # Restore the corner plot's subplot params (undoes plt.tight_layout()) fig.subplots_adjust(**saved) # Re-set the SED axes position (it was computed before tight_layout) ax_sed.set_position([left, bottom, width, height]) return fig, axes, ax_sed