Skip to content

Fraction Scan Plots#

In the following a small example how to plot the rejections for the different fraction value combinations. In this example, value from numpy arange are used.

"""Example of fraction scan plot."""

from __future__ import annotations

import numpy as np
from ftag import get_discriminant

from puma import Line2D, Line2DPlot
from puma.metrics import calc_eff
from puma.utils import get_dummy_2_taggers, logger

# The line below generates dummy data which is similar to a NN output
df = get_dummy_2_taggers(size=100_000)

logger.info("caclulate tagger discriminants")

# defining boolean arrays to select the different flavour classes
is_light = df["HadronConeExclTruthLabelID"] == 0
is_c = df["HadronConeExclTruthLabelID"] == 4
is_b = df["HadronConeExclTruthLabelID"] == 5


fc_values = np.linspace(0.0, 1.0, 101)
SIG_EFF = 0.77

dips_scores = df[["dips_pu", "dips_pc", "dips_pb"]]


def calc_effs(fc_value: float):
    """Tagger efficiency for fixed working point.

    Parameters
    ----------
    fc_value : float
        Value for the charm fraction used in discriminant calculation.

    Returns
    -------
    tuple
        Tuple of shape (, 3) containing (fc_value, ujets_eff, cjets_eff)
    """
    disc = get_discriminant(dips_scores, "dips", signal="bjets", fc=fc_value)
    ujets_eff = calc_eff(disc[is_b], disc[is_light], SIG_EFF)
    cjets_eff = calc_eff(disc[is_b], disc[is_c], SIG_EFF)

    return [fc_value, ujets_eff, cjets_eff]


eff_results = np.array(list(map(calc_effs, fc_values)))


# Init example x- and y values of the fraction scan. Theses
# are the x- and y values for the rejections for different fractions.
# You can plot 2 rejections against each other. These values can either
# be arrays, lists, ints or floats but the dtype must be the same and also
# the number of values inside!
x_values = eff_results[:, 2]
y_values = eff_results[:, 1]

# If you want to mark a specific point with a marker, you just need to
# define the x- and y values again for the marker (This must be floats)
MARKER_X = eff_results[30, 2]
MARKER_Y = eff_results[30, 1]

# You can give several plotting options to the plot itself

# Now init a fraction scan plot
frac_plot = Line2DPlot()

# Add our x- and y values as a new line
# The colour and linestyle are optional here
frac_plot.add(
    Line2D(
        x_values=x_values,
        y_values=y_values,
        label="Tagger 1",
        colour="r",
        linestyle="-",
    )
)

# Add a marker for the just added fraction scan. If you don't
# set the colour here, the colour of the last added element will be used.
# marker, markersize and markeredgewidth are optional. The here
# given values are the default values.
# The is_marker bool tells the plot that this is a marker and not a line
frac_plot.add(
    Line2D(
        x_values=MARKER_X,
        y_values=MARKER_Y,
        colour="r",
        marker="x",
        label=rf"$f_c={eff_results[30, 0]}$",
        markersize=15,
        markeredgewidth=2,
    ),
    is_marker=True,
)

# Adding labels
frac_plot.ylabel = "Light-flavour jets efficiency"
frac_plot.xlabel = "$c$-jets efficiency"

# Draw and save the plot
frac_plot.draw()
frac_plot.savefig("FractionScanPlot_test.png")