Source code for gammapy.visualization.utils

import numpy as np
from scipy.interpolate import CubicSpline
import matplotlib.pyplot as plt
from gammapy.maps import MapAxis
from gammapy.maps.utils import edges_from_lo_hi

__all__ = [
    "plot_contour_line",
    "plot_spectrum_datasets_off_regions",
    "plot_theta_squared_table",
]


[docs]def plot_spectrum_datasets_off_regions( datasets, ax=None, legend=None, legend_kwargs=None, **kwargs ): """Plot the off regions of spectrum datasets. Parameters ---------- datasets : `~gammapy.datasets.Datasets` of or sequence of `~gammapy.datasets.SpectrumDatasetOnOff` List of spectrum on-off datasets. ax : `~astropy.visualization.wcsaxes.WCSAxes` Axes object to plot on. legend : bool Whether to add/display the labels of the off regions in a legend. By default True if ``len(datasets) <= 10``. legend_kwargs : dict Keyword arguments used in `matplotlib.axes.Axes.legend`. The ``handler_map`` cannot be overridden. **kwargs : dict Keyword arguments used in `gammapy.maps.RegionNDMap.plot_region`. Can contain a `~cycler.Cycler` in a ``prop_cycle`` argument. Notes ----- Properties from the ``prop_cycle`` have maximum priority, except ``color``, ``edgecolor``/``color`` is selected from the sources below in this order: ``kwargs["edgecolor"]``, ``kwargs["prop_cycle"]``, ``matplotlib.rcParams["axes.prop_cycle"]`` ``matplotlib.rcParams["patch.edgecolor"]``, ``matplotlib.rcParams["patch.facecolor"]`` is never used. Examples -------- Plot forcibly without legend and with thick circles:: plot_spectrum_datasets_off_regions(datasets, ax, legend=False, linewidth=2.5) Plot that quantifies the overlap of off regions:: plot_spectrum_datasets_off_regions(datasets, ax, alpha=0.3, facecolor='black') Plot that cycles through colors (``edgecolor``) and line styles together:: plot_spectrum_datasets_off_regions(datasets, ax, prop_cycle=plt.cycler(color=list('rgb'), ls=['--', '-', ':'])) Plot that uses a modified `~matplotlib.rcParams`, has two legend columns, static and dynamic colors, but only shows labels for ``datasets1`` and ``datasets2``. Note that ``legend_kwargs`` only applies if it's given in the last function call with ``legend=True``:: plt.rc('legend', columnspacing=1, fontsize=9) plot_spectrum_datasets_off_regions(datasets1, ax, legend=True, edgecolor='cyan') plot_spectrum_datasets_off_regions(datasets2, ax, legend=True, legend_kwargs=dict(ncol=2)) plot_spectrum_datasets_off_regions(datasets3, ax, legend=False, edgecolor='magenta') """ from matplotlib.legend_handler import HandlerPatch, HandlerTuple from matplotlib.patches import CirclePolygon, Patch if ax is None: ax = plt.subplot(projection=datasets[0].counts_off.geom.wcs) legend = legend or legend is None and len(datasets) <= 10 legend_kwargs = legend_kwargs or {} handles, labels = [], [] kwargs.setdefault("facecolor", "none") prop_cycle = kwargs.pop("prop_cycle", plt.rcParams["axes.prop_cycle"]) plot_kwargs = kwargs.copy() for props, dataset in zip(prop_cycle(), datasets): props = props.copy() color = props.pop("color", plt.rcParams["patch.edgecolor"]) plot_kwargs["edgecolor"] = kwargs.get("edgecolor", color) plot_kwargs.update(props) dataset.counts_off.plot_region(ax, **plot_kwargs) # create proxy artist for the custom legend if legend: handle = Patch(**plot_kwargs) handles.append(handle) labels.append(dataset.name) if legend: legend = ax.get_legend() if legend: handles = legend.legendHandles + handles labels = [text.get_text() for text in legend.texts] + labels handles = [(handle, handle) for handle in handles] tuple_handler = HandlerTuple(ndivide=None, pad=0) def patch_func( legend, orig_handle, xdescent, ydescent, width, height, fontsize ): radius = width / 2 return CirclePolygon((radius - xdescent, height / 2 - ydescent), radius) patch_handler = HandlerPatch(patch_func) legend_kwargs.setdefault("handletextpad", 0.5) legend_kwargs["handler_map"] = {Patch: patch_handler, tuple: tuple_handler} ax.legend(handles, labels, **legend_kwargs)
[docs]def plot_contour_line(ax, x, y, **kwargs): """Plot smooth curve from contour points""" # close contour xf = np.append(x, x[0]) yf = np.append(y, y[0]) # curve parametrization must be strictly increasing # so we use the cumulative distance of each point from the first one dist = np.sqrt(np.diff(xf) ** 2.0 + np.diff(yf) ** 2.0) dist = [0] + list(dist) t = np.cumsum(dist) ts = np.linspace(0, t[-1], 50) # 1D cubic spline interpolation cs = CubicSpline(t, np.c_[xf, yf], bc_type="periodic") out = cs(ts) # plot if "marker" in kwargs.keys(): marker = kwargs.pop("marker") else: marker = "+" if "color" in kwargs.keys(): color = kwargs.pop("color") else: color = "b" ax.plot(out[:, 0], out[:, 1], "-", color=color, **kwargs) ax.plot(xf, yf, linestyle="", marker=marker, color=color)
[docs]def plot_theta_squared_table(table): """Plot the theta2 distribution of ON, OFF counts, excess and signifiance in each theta2bin. Take the table containing the ON counts, the OFF counts, the acceptance, the off acceptance and the alpha (normalisation between ON and OFF) for each theta2 bin Parameters ---------- table : `~astropy.table.Table` Required columns: theta2_min, theta2_max, counts, counts_off and alpha """ theta2_edges = edges_from_lo_hi( table["theta2_min"].quantity, table["theta2_max"].quantity ) theta2_axis = MapAxis.from_edges(theta2_edges, interp="lin", name="theta_squared") ax0 = plt.subplot(2, 1, 1) x = theta2_axis.center.value x_edges = theta2_axis.edges.value xerr = (x - x_edges[:-1], x_edges[1:] - x) ax0.errorbar( x, table["counts"], xerr=xerr, yerr=np.sqrt(table["counts"]), linestyle="None", label="Counts", ) ax0.errorbar( x, table["counts_off"], xerr=xerr, yerr=np.sqrt(table["counts_off"]), linestyle="None", label="Counts Off", ) ax0.errorbar( x, table["excess"], xerr=xerr, yerr=(-table["excess_errn"], table["excess_errp"]), fmt="+", linestyle="None", label="Excess", ) ax0.set_ylabel("Counts") ax0.set_xticks([]) ax0.set_xlabel("") ax0.legend() ax1 = plt.subplot(2, 1, 2) ax1.errorbar(x, table["sqrt_ts"], xerr=xerr, linestyle="None") ax1.set_xlabel(f"Theta [{theta2_axis.unit}]") ax1.set_ylabel("Significance")