Samples, variables, cuts and styles¶
rf.plot accepts plain strings everywhere, which is all you need for a quick
look. Analysis scripts that make dozens of plots from the same inputs are
clearer when the pieces are named once. Four small frozen dataclasses do
that; none of them opens a file or holds data.
Sample¶
Where data comes from, how it is labelled, and how it is drawn.
sig = rf.Sample("sig_*.root", tree="events", label="Signal", weight="mc_weight", color="tab:red")
bkg = rf.Sample(
["bkg_a.root", "bkg_b.root"],
tree="events",
label="Background",
selection="passTrigger",
weight="mc_weight",
scale=0.98,
)
data = rf.Sample("data.root:events", label="Data", is_data=True)
mem = rf.Sample({"x": awkward_array, "w": weights}, label="in memory")
datamay be a path, glob,"path:tree", a list of those, a mapping of arrays, an Awkward record array, a NumPy structured array, or any object implementing theSourceprotocol.selectionandweightbelong to the sample and combine with the ones given toplot()(&and*respectively).is_data=Truedraws points with error bars (in the style's text colour unlesscoloris set), keeps the sample out of stacks and makes it the numerator of ratios.xsecandngendescribe simulated processes: the cross section (pb, or a string with a unit such as"1.2 fb") and the number of generated events (a number, the name of an object in the file holding it, e.g. FCCAnalyses'"eventsProcessed"TParameteror a sum-of-weights histogram, orNonefor the number of entries). Withlumi=given toplot(),cutflow(),summarize(), ... every weight is multiplied byxsec × lumi / ngen:
zh = rf.Sample("p8_ee_ZH_ecm240.root", label="ZH", xsec="0.201 pb", ngen="eventsProcessed")
ww = rf.Sample("p8_ee_WW_ecm240.root", label="WW", xsec="16.4 pb", ngen="eventsProcessed")
rf.plot([ww, zh], "recoil_mass", lumi="10.8 ab^-1", stack=True) # expected yields
Without a luminosity such samples raise a LuminosityError.
- entry_start/entry_stop restrict reading for quick looks at large files
(a plot reads every needed branch of every file into memory at once). For a
ready-made FileSource, give the range to the source itself; passing it to
Sample afterwards raises a SourceError.
- systematics={name: variation} lists the sample's sources of systematic
uncertainty: weight expressions (("w_up", "w_down")), normalisation
uncertainties (0.05, (1.1, 0.95)), varied branches
({"Jet_pt": ("Jet_pt_up", "Jet_pt_down")}) and varied files
(Systematic.samples). Sources with
the same name are correlated across samples; see
Systematic uncertainties.
- sample.replace(label="...") returns a copy with the given fields changed; new values are
validated like constructor arguments.
Passing a list of files to plot() creates one sample per file. To merge
several files into one sample, use a glob or a Sample.
Variable¶
What to histogram and how to present it.
pt = rf.Variable("Muon_pt", bins=(50, 0, 200), label=r"$p_T^{\mu}$", unit="GeV")
met = rf.Variable(
"MET / 1000", bins=40, range="auto", label=r"$E_T^{miss}$", unit="TeV", log=True, name="met"
)
bins: anint(range inferred from the data),(n, low, high), a sequence of edges (e.g.rf.log_bins(30, 1, 1000)), or ahist.axis.Regular/Variable.range:(low, high),"robust"(the default: ignores far outliers such as-999sentinels and cuts a thin tail, both of which then land in the under/overflow) or"auto"(the finite min/max over all samples). See Binning and range.labelandunitform the axis labellabel [unit]; the unit also appears in the automatic y label (Events / 4 GeV).nameis used for file names byPlot.save(directory); it must be a plain file stem (no path separators).
bins, range, xlabel and unit given to plot() override the variable.
Cut¶
base = rf.Cut("nMuon >= 1", label="1 muon")
sr = base & "abs(Muon_eta) < 2.5" & ~rf.Cut("isCosmic")
See Expressions and selections.
Style¶
Appearance, applied only while a figure is drawn (global matplotlib state is
untouched unless you call rf.use_style).
atlas = rf.Style(
experiment="ATLAS", status="Internal", lumi=140, com=13.6, text=[r"$Z \to \mu\mu$ selection"]
)
neutral = rf.Style(
figsize=(6, 5), legend="upper left", colors=["#1b9e77", "#d95f02"], rc={"font.size": 12}
)
experimentselects the matching mplhep style sheet and label helper (ATLAS, CMS, LHCb, ALICE, DUNE); any other name still gets a label with the neutral style. Nothing is drawn unless you ask for it.status,lumi,com,textfill the label;simulationcontrols the "Simulation" word (default: shown when there is no data sample; a status that already contains it is not doubled).lumiandcomare numbers inlumi_unit(default fb⁻¹) andcom_unit(default TeV), or strings with their own unit:Style(experiment="FCC-ee", com="240 GeV", lumi="10.8 ab^-1"). The energy is shown only whencomis given.basecan be any mplhep or matplotlib style name ("ATLAS","ggplot", ...) or a mapping of rcParams;rcadds overrides on top;colorsreplaces the colour cycle.label_locoverrides the experiment's convention: 0 puts the experiment and secondary text above the frame; 3 puts the experiment above and secondary text inside; 1, 2, and 4 put both inside. Luminosity stays above for locations 0–3 and inside for 4. 2D histograms and correlation matrices default to location 0; onlylabel_loc=1,2, or4moves the experiment inside the frame.- The centre-of-mass energy and luminosity appear only when
com/lumiare given; nothing is invented for you. legendisTrue,Falseor a location string;legend_kwargsare forwarded toAxes.legend.
A bare string is accepted too: style="CMS".
Putting it together¶
samples = [bkg, sig]
variables = [pt, met, rf.Variable("nMuon", bins=(8, -0.5, 7.5))]
for var in variables:
p = rf.plot(samples, var, observed=data, selection=sr, stack=True, ratio=True, style=atlas)
p.save("plots/") # plots/Muon_pt.pdf, plots/met.pdf, plots/nMuon.pdf
p.close()