Source code for phys_581_2021.plotting

"""Tools for plotting."""
import numpy as np

import scipy.stats

from matplotlib import pyplot as plt

import uncertainties
from uncertainties import unumpy as unp

sp = scipy


[docs]def corner_plot( a, C=None, labels=None, levels=None, sigmas=(1, 2, 3, 4), axes=None, fig=None, Nxy=(100, 101), contour_kw=None, ): # pragma: no cover """Make a corner-plot of the variables `a`. Parameters ---------- a : [float] or [uncertainties.ufloat] Parameter values. These can be `ufloat` values from the :py:mod:`uncertainties` package. C : array-like, optional Covariance matrix. If `a` is a list of `ufloat`s, then the correlation matrix will be computed with `C = uncertainties.covariance_matrix(a)` if not provided. labels : [str], optional Labels for plot. If not provided, then if `a._fields` exists, these will be used, otherwise, they will be labelled `a_n`. levels : array-like, optional Contours to draw. If not, then we will assume the variables are gaussian and use the ``nu=2`` degree-of-freedom chi square distribution to convert from `sigmas`. sigmas : array-like, optional If provided and `levels` is `None`, then use to generate levels. axes : array of Axes, optional If provided, then the we will draw in these. Nxy : (int, int) Size of grid for contour plot. contour_kw : dict, optional Additional arguments for :py:meth:`matplotlib.axes.Axes.contour` like `linestyles`, and `colors`. """ Na = len(a) if C is None: C = np.asarray(uncertainties.covariance_matrix(a)) if labels is None: labels = getattr(a, "_fields", [f"$a_{_n}$" for _n in range(Na)]) a = unp.nominal_values(a) sigmas = np.asarray(sigmas) sigma_max = 1.5 * max(sigmas) if levels is None: q = sp.stats.norm.cdf(sigmas) - sp.stats.norm.cdf(-sigmas) levels = sp.stats.chi2.ppf(q, df=2) axs = axes if axs is None: if fig is None: fig = plt.figure(figsize=(10, 10)) axs = fig.subplots( Na, Na, sharex="col", sharey="row", gridspec_kw=dict(hspace=0, wspace=0), ) if contour_kw is None: contour_kw = {} for i, ai in enumerate(a): for j, aj in enumerate(a): if i <= j: if axes is None: # Only toggle visibility if we generated axes axs[i, j].set(visible=False) continue ax = axs[i, j] inds = np.array([[i, j]]) C2 = C[inds.T, inds] sigma_i, sigma_j = np.sqrt([C[i, i], C[j, j]]) dai = np.linspace(-sigma_max * sigma_i, sigma_max * sigma_i, Nxy[0]) daj = np.linspace(-sigma_max * sigma_j, sigma_max * sigma_j, Nxy[1]) das = np.meshgrid(dai, daj, indexing="ij", sparse=False) dchi2 = np.einsum("xij,yij,xy->ij", das, das, np.linalg.inv(C2)) ax.contour( a[j] + daj, a[i] + dai, dchi2, levels=levels, **contour_kw, ) if j == 0: ax.set(ylabel=f"{labels[i]}") if i == len(a) - 1: ax.set(xlabel=f"{labels[j]}") return fig, axs