from typing import List, Tuple import matplotlib.pyplot as plt import numpy as np from matplotlib.figure import Figure def hist_n_particles(q: List[int], label: str) -> Figure: """Generate histogram for particle counts on events. :param q: Count per event. :type q: List[int] :param label: Plot title. :type label: str :return: Figure with histogram and histogram ratio. :rtype: Figure """ fig, ax = plt.subplots(nrows=1, figsize=(15, 8)) bins, edges = np.histogram(q, bins=15, range=(0, 15)) for idx, val in enumerate(bins[::-1]): if val > 0: max_idx = len(bins) - idx - 1 edges = edges[:max_idx] bins = bins[:max_idx] break ax.bar(edges, bins, width=1, alpha=0.6) ax.set_title(label, fontsize=20, y=1.04) ax.set_xticks(edges) return fig def hist_var(q: List[float], ax: plt.Axes, **kwargs) -> plt.Axes: """Create histogram with error bars. :param q: Values to create histogram. :type q: List[float] :param ax: Axes in which histrogram is plotted. :type ax: plt.Axes :return: Axes with histogram. :rtype: plt.Axes """ bins, edges, _ = ax.hist( q, alpha=0.6, histtype="step", align="left", linewidth=4, **kwargs ) errors = np.sqrt(bins) bin_width = edges[1] - edges[0] ax.bar( x=edges[:-1], bottom=bins, height=errors, width=bin_width, alpha=0.0, color="w", hatch="/", ) ax.bar( x=edges[:-1], bottom=bins, height=-errors, width=bin_width, alpha=0.0, color="w", hatch="/", ) return ax def ratio_hist( processes_q: List[List[float]], hist_labels: List[str], reference_label: str, n_bins: int, hist_range: Tuple[int, int], title: str, figsize=(15, 8), ) -> Figure: """Generate histrograms with ratio pad :param processes_q: Quantity for each event and process :type processes_q: List[List[float]] :param hist_labels: Labels for each process :type hist_labels: List[str] :param reference_label: Label of process taken as the denominator of ratios :type reference_label: str :param n_bins: Number of bins for histograms :type n_bins: int :param hist_range: Range for histogram bins :type hist_range: Tuple[int] :param title: Plot title :type title: str :param figsize: Figure size for output plot :type figsize: Tuple[int] :return: Figure with histogram and histogram ratio. :rtype: Figure """ fig, ax = plt.subplots( nrows=len(processes_q), ncols=1, gridspec_kw={"height_ratios": [3] + [1] * (len(processes_q) - 1)}, sharex=True, figsize=figsize, ) legends = [] p_bins = {} p_edges = {} p_errors = {} edges = None for p, label in zip(processes_q, hist_labels): bins = n_bins if edges is not None: bins = edges bins, edges, _ = ax[0].hist( x=p, bins=bins, range=hist_range, fill=False, label=label, align="left", histtype="step", linewidth=4, ) p_bins[label] = bins p_edges[label] = edges p_errors[label] = np.sqrt(bins) legends += [label] bin_width = edges[1] - edges[0] for label in hist_labels: ax[0].bar( x=p_edges[label][:-1], bottom=p_bins[label], height=p_errors[label], width=bin_width, alpha=0.0, color="w", hatch="/", ) ax[0].bar( x=p_edges[label][:-1], bottom=p_bins[label], height=-p_errors[label], width=bin_width, alpha=0.0, color="w", hatch="/", ) legends += ["_", "_"] ax[0].set_ylabel("Events", fontsize=15) ax[0].set_title(title, fontsize=20) legends[-1] = "Stat. Uncertainty" ax[0].legend(legends) plot_idx = 1 ref_bins = p_bins[reference_label] ref_edges = p_edges[reference_label] ref_frac_error = p_errors[reference_label] / ref_bins for label in hist_labels: if label == reference_label: continue ratios = p_bins[label] / ref_bins error_ratio = ratios * (ref_frac_error + p_errors[label] / p_bins[label]) ax[plot_idx].bar( bottom=1.0, height=error_ratio, x=ref_edges[:-1], width=bin_width, alpha=0.3, color="blue", ) ax[plot_idx].bar( bottom=1.0, height=-error_ratio, x=ref_edges[:-1], width=bin_width, alpha=0.3, color="blue", ) ax[plot_idx].scatter(ref_edges[:-1], ratios, marker="o", color="black") ax[plot_idx].set_ylabel(f"{label}/{reference_label}") plot_idx += 1 return fig