示例#1
0
def test_normal_interval():
    from coffea.hist.plot import normal_interval

    # Reference weighted efficiency and error from ROOTs TEfficiency

    denom = np.array([
        89.01457591590004,
        2177.066076428943,
        6122.5256890981855,
        0.0,
        100.27757990710668,
    ])
    num = np.array([
        75.14287743709515,
        2177.066076428943,
        5193.454723043864,
        0.0,
        84.97723540536361,
    ])
    denom_sumw2 = np.array([
        94.37919737476827, 10000.0, 6463.46795877633, 0.0, 105.90898005417333
    ])
    num_sumw2 = np.array(
        [67.2202147680005, 10000.0, 4647.983931785646, 0.0, 76.01275761253757])
    ref_hi = np.array([
        0.0514643476600107, 0.0, 0.0061403263960343, np.nan, 0.0480731185500146
    ])
    ref_lo = np.array([
        0.0514643476600107, 0.0, 0.0061403263960343, np.nan, 0.0480731185500146
    ])

    interval = normal_interval(num, denom, num_sumw2, denom_sumw2)
    threshold = 1e-6

    lo, hi = interval

    assert len(ref_hi) == len(hi)
    assert len(ref_lo) == len(lo)

    for i in range(len(ref_hi)):
        if np.isnan(ref_hi[i]):
            assert np.isnan(ref_hi[i])
        elif ref_hi[i] == 0.0:
            assert hi[i] == 0.0
        else:
            assert np.abs(hi[i] / ref_hi[i] - 1) < threshold

        if np.isnan(ref_lo[i]):
            assert np.isnan(ref_lo[i])
        elif ref_lo[i] == 0.0:
            assert lo[i] == 0.0
        else:
            assert np.abs(lo[i] / ref_lo[i] - 1) < threshold
示例#2
0
def plotratio(
    num,
    denom,
    ax=None,
    clear=True,
    overflow=False,
    error_opts=None,
    denom_fill_opts=None,
    guide_opts=None,
    unc="clopper-pearson",
    label=None,
    ratio_yticks=[],  # [start, stop, step] -> [0.5, 1.5, 0.1]
):
    if ax is None:
        fig, ax = plt.subplots(1, 1)
    else:
        if not isinstance(ax, plt.Axes):
            raise ValueError("ax must be a matplotlib Axes object")
        if clear:
            ax.clear()

    if error_opts is None and denom_fill_opts is None and guide_opts is None:
        error_opts = {}
        denom_fill_opts = {}

    (naxis,) = num.axes
    (daxis,) = denom.axes
    assert isinstance(naxis, (hist.axis.Regular, hist.axis.Variable))
    assert isinstance(daxis, (hist.axis.Regular, hist.axis.Variable))
    assert all(naxis.edges == daxis.edges)

    ax.set_xlabel(naxis.label)
    ax.set_ylabel("Ratio")
    edges = naxis.edges
    centers = naxis.centers

    sumw_num = num.view(flow=overflow)["value"]
    sumw2_num = num.view(flow=overflow)["variance"]
    sumw_denom = denom.view(flow=overflow)["value"]
    sumw2_denom = denom.view(flow=overflow)["variance"]

    rsumw = sumw_num / sumw_denom
    if unc == "clopper-pearson":
        rsumw_err = np.abs(clopper_pearson_interval(sumw_num, sumw_denom) - rsumw)
    elif unc == "poisson-ratio":
        # poisson ratio n/m is equivalent to binomial n/(n+m)
        rsumw_err = np.abs(clopper_pearson_interval(sumw_num, sumw_num + sumw_denom) - rsumw)
    elif unc == "num":
        rsumw_err = np.abs(poisson_interval(rsumw, sumw2_num / sumw_denom ** 2) - rsumw)
    elif unc == "normal":
        rsumw_err = np.abs(normal_interval(sumw_num, sumw_denom, sumw2_num, sumw2_denom))
    else:
        raise ValueError("Unrecognized uncertainty option: %r" % unc)

    if error_opts is not None:
        opts = {"label": label, "linestyle": "none"}
        opts.update(error_opts)
        emarker = opts.pop("emarker", "")
        errbar = ax.errorbar(x=centers, y=rsumw, yerr=rsumw_err, **opts)
        plt.setp(errbar[1], "marker", emarker)
    if denom_fill_opts is not None:
        unity = np.ones_like(sumw_denom)
        denom_unc = poisson_interval(unity, sumw2_denom / sumw_denom ** 2)
        opts = {"step": "post", "facecolor": (0, 0, 0, 0.3), "linewidth": 0}
        opts.update(denom_fill_opts)
        ax.fill_between(
            edges,
            np.r_[denom_unc[0], denom_unc[0, -1]],
            np.r_[denom_unc[1], denom_unc[1, -1]],
            **opts,
        )
    if guide_opts is not None:
        opts = {"linestyle": "--", "color": (0, 0, 0, 0.5), "linewidth": 1}
        opts.update(guide_opts)
        ax.axhline(1.0, **opts)

    if ratio_yticks:
        start, stop, step = ratio_yticks
        ax.set_yticks(np.arange(start, stop + step, step).tolist())
        for label in ax.yaxis.get_ticklabels()[::2]:
            label.set_visible(False)
        ax.set_ylim(start, stop)

    if clear:
        ax.autoscale(axis="x", tight=True)
        ax.set_ylim(0, None)

    return ax