API reference¶
Top level¶
rootfig.plot ¶
plot(
data: Any,
variable: str | Variable | None = None,
*,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
bins: Bins | None = None,
range: RangeSpec = None,
label: str | Sequence[str] | None = None,
observed: Any = None,
xlabel: str | None = None,
ylabel: str | None = None,
unit: str | None = None,
title: str | None = None,
normalize: NormalizeSpec = None,
normalize_uncertainty: NormalizeUncertainty = "scale",
stack: StackSpec = False,
panel: ComparisonKind | None = None,
reference: str | None = None,
panel_ylim: tuple[float, float] | None = None,
panel_label: str | None = None,
panel_uncertainty: UncertaintyMode | None = None,
goodness_of_fit: bool | GoodnessOfFitTest = False,
goodness_of_fit_format: Literal[
"quotient", "fraction"
] = "quotient",
logx: bool | None = None,
logy: bool = False,
flow: FlowSpec = "hint",
histtype: HistType | None = None,
errorbars: bool | None = None,
data_errors: DataErrors | None = None,
xlim: tuple[float, float] | None = None,
ylim: tuple[float | None, float | None] | None = None,
xbreak: tuple[float, float] | None = None,
legend: bool | str | None = None,
stats: bool | str = False,
text: str | Sequence[str] | None = None,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
nonfinite: NonFinitePolicy = "drop",
systematics: Mapping[str, SystematicLike] | None = None,
variances_from_contents: bool = False,
functions: Mapping[str, Callable[..., Any]]
| None = None,
save: str | None = None,
) -> Plot
Histogram a variable from one or more samples and draw it.
This is the TTree::Draw of rootfig: read only the branches needed,
evaluate the expressions, apply the selection with the documented
per-event/per-object semantics, fill hist.Hist objects with a binning
shared by all samples, and render them with mplhep.
The same call draws histograms that already exist. A variable that is a
bare name and addresses a TH1 stored in the files (a histogram written by
an analysis framework) is read instead of filled, summed over each sample's
files and scaled like a filled one; an explicit tree= always means a
branch. Histogram objects (hist.Hist or
:class:~rootfig.histograms.Histogram, one or a list) passed as data
are drawn as they are, with label naming them and variable optional.
Options that need event data, such as selection and weight, raise
for both. Range inference ("auto", "robust") is a no-op for them; an
explicit range crops their axis, moving the rest into flow bins.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
What to plot: a file path or glob, |
required |
variable
|
str | Variable | None
|
Branch name or expression (see :mod: |
None
|
tree
|
str | None
|
Tree name for file inputs; auto-detected when a file holds one tree. |
None
|
selection
|
CutLike | None
|
Boolean expression or :class: |
None
|
weight
|
str | None
|
Weight expression, e.g. |
None
|
lumi
|
float | str | None
|
Integrated luminosity to scale simulated samples to, in fb^-1 or as a
string with a unit ( |
None
|
bins
|
Bins | None
|
Binning: an |
None
|
range
|
RangeSpec
|
Range for integer |
None
|
label
|
str | Sequence[str] | None
|
Legend label(s) for samples given as plain files or as histogram objects. |
None
|
observed
|
Any
|
A sample or group of observed data (or the file(s) for one; histogram
objects when |
None
|
xlabel
|
str | None
|
Axis labels; defaults come from the variable (or the stored axis title),
the normalisation and the bin width ( |
None
|
ylabel
|
str | None
|
Axis labels; defaults come from the variable (or the stored axis title),
the normalisation and the bin width ( |
None
|
unit
|
str | None
|
Axis labels; defaults come from the variable (or the stored axis title),
the normalisation and the bin width ( |
None
|
title
|
str | None
|
Axis labels; defaults come from the variable (or the stored axis title),
the normalisation and the bin width ( |
None
|
normalize
|
NormalizeSpec
|
|
None
|
normalize_uncertainty
|
NormalizeUncertainty
|
What normalising to a histogram's own total ( |
'scale'
|
stack
|
StackSpec
|
|
False
|
panel
|
ComparisonKind | None
|
What a lower panel shows: |
None
|
reference
|
str | None
|
The label of the one histogram the panel compares with, the background of a significance: every other histogram, data included, is compared with it (every other non-data histogram, as a signal, for a significance). By default data is compared with the stack total, or with the first non-data histogram without a stack; with a stack but no data, every overlaid histogram with the total, and with neither (or with observed data alone), every histogram after the first with the first. A significance takes the overlaid non-data histograms as signals over the stack, or otherwise the last non-data histogram over the sum of the others. |
None
|
panel_ylim
|
tuple[float, float] | None
|
Lower panel range, y label, and uncertainty treatment of a ratio,
relative difference or difference ( |
None
|
panel_label
|
tuple[float, float] | None
|
Lower panel range, y label, and uncertainty treatment of a ratio,
relative difference or difference ( |
None
|
panel_uncertainty
|
tuple[float, float] | None
|
Lower panel range, y label, and uncertainty treatment of a ratio,
relative difference or difference ( |
None
|
goodness_of_fit
|
bool | GoodnessOfFitTest
|
Test every histogram a ratio panel would compare with its reference
(data with the stack total by default; |
False
|
goodness_of_fit_format
|
Literal['quotient', 'fraction']
|
How the chi-square per degree of freedom is written: |
'quotient'
|
logx
|
bool | None
|
Logarithmic axes. |
None
|
logy
|
bool | None
|
Logarithmic axes. |
None
|
flow
|
FlowSpec
|
Under/overflow display: |
'hint'
|
histtype
|
HistType | None
|
Default drawing style for overlaid samples: |
None
|
errorbars
|
bool | None
|
Draw statistical error bars on overlaid histograms; ignored for stacked
histograms. |
None
|
data_errors
|
DataErrors | None
|
The statistical uncertainty of observed data, in the main and the lower
panel alike. |
None
|
xlim
|
tuple[float, float] | None
|
Axis limits; |
None
|
ylim
|
tuple[float, float] | None
|
Axis limits; |
None
|
xbreak
|
tuple[float, float] | None
|
|
None
|
legend
|
bool | str | None
|
|
None
|
stats
|
bool | str
|
Add a box with entries, mean and standard deviation per sample;
|
False
|
text
|
str | Sequence[str] | None
|
Extra text line(s) drawn with the experiment label. |
None
|
style
|
StyleLike
|
:class: |
None
|
figsize
|
tuple[float, float] | None
|
Figure size in inches. |
None
|
ax
|
AxesLike
|
Draw into existing axes ( |
None
|
nonfinite
|
NonFinitePolicy
|
|
'drop'
|
systematics
|
Mapping[str, SystematicLike] | None
|
Sources of systematic uncertainty applied to every simulated sample, in
the forms |
None
|
variances_from_contents
|
bool
|
Accept a histogram (stored or object) with a plain count storage that
was filled with weights or rescaled, so |
False
|
functions
|
Mapping[str, Callable[..., Any]] | None
|
Functions of your own that the variable, selection and weights may call
besides the built-in ones, |
None
|
save
|
str | None
|
Path to save the figure to (also returned in the :class: |
None
|
Returns:
| Type | Description |
|---|---|
Plot
|
The figure, axes, histograms and comparisons. |
rootfig.plot2d ¶
plot2d(
data: Any,
x: str | Variable | None = None,
y: str | Variable | None = None,
*,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
bins: Bins | tuple[Bins, Bins] | None = None,
normalize: NormalizeSpec = None,
logz: bool = False,
logx: bool | None = None,
logy: bool | None = None,
cmap: str | Any = "viridis",
colorbar: bool = True,
zlabel: str | None = None,
title: str | None = None,
text: str | Sequence[str] | None = None,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
nonfinite: NonFinitePolicy = "drop",
variances_from_contents: bool = False,
functions: Mapping[str, Callable[..., Any]]
| None = None,
save: str | None = None,
) -> Plot
Draw a two-dimensional histogram of y versus x for one sample.
x and y must have the same structure (both per-event, or both
per-object from the same collection). bins applies to both axes unless
it is a pair of binning specifications, one per axis (so (40, 20) is
two bin counts, never a range; a range needs (n, low, high)); per-axis
ranges, labels and logarithmic scales (logx/logy default to the
variables' log flags) are best given through
:class:~rootfig.model.Variable objects. The two axes infer their ranges
robustly and independently, and unlike the 1D plots there is no flow
indicator, so give every axis that needs its full extent its own
range="auto": rf.Variable(x, range="auto") leaves the y axis
inferring robustly.
Like :func:plot, it also draws histograms that already exist: x alone
may name a TH2 stored in the file (its label, unit and log flag then
describe the x axis; the y axis keeps the stored title, and shows its axis
name when the file has none), and data may be a 2D hist.Hist or
:class:~rootfig.histograms.Histogram. For such an object x and y
are optional and describe its axes as variable does in :func:plot:
label, unit, log flag, a name for the axis and bins/range to crop
and merge to;
the object itself is left untouched. For both, bins crops and merges bins per axis
as in :func:plot: an integer count, or edges that coincide with the
existing ones. A range without bins keeps the bins between its ends, which
must be existing edges too; cropped
content moves into the flow bins. variances_from_contents accepts such a histogram without
variances, as in :func:plot. functions ({name: callable}) are functions of
your own the expressions may call, as in :func:plot.
rootfig.histogram ¶
histogram(
data: Any,
variable: str | Variable,
*,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
bins: Bins | None = None,
range: RangeSpec = None,
normalize: NormalizeSpec = None,
normalize_uncertainty: NormalizeUncertainty = "scale",
nonfinite: NonFinitePolicy = "drop",
variances_from_contents: bool = False,
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> Hist
Fill a single histogram and return it as a plain hist.Hist.
See :func:plot for the arguments. data is one sample or one
:class:~rootfig.model.Group, whose samples are summed. An integer bins
without a range infers one robustly (range="auto" for the full finite
minimum and maximum). A variable naming a histogram stored in the file
returns that histogram (summed over the files, in Weight storage).
Examples:
>>> h = rf.histogram(
... "events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=(50, 0, 200)
... )
>>> h.values().sum()
rootfig.histograms ¶
histograms(
data: Any,
variable: str | Variable,
*,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
bins: Bins | None = None,
range: RangeSpec = None,
label: str | Sequence[str] | None = None,
normalize: NormalizeSpec = None,
normalize_uncertainty: NormalizeUncertainty = "scale",
nonfinite: NonFinitePolicy = "drop",
systematics: Mapping[str, SystematicLike] | None = None,
variances_from_contents: bool = False,
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> list[Histogram]
Fill one :class:~rootfig.histograms.Histogram per sample or group with shared binning.
See :func:plot for the meaning of the arguments; this function stops
before drawing. An integer bins without a range infers one robustly
(range="auto" for the full finite minimum and maximum). Systematic
variations are in :attr:~rootfig.histograms.Histogram.variations and
summarised by :func:~rootfig.histograms.uncertainty. A variable naming
a histogram stored in the files is read instead of filled, as in
:func:plot.
rootfig.load ¶
load(
data: Any,
expressions: str
| Sequence[str]
| Mapping[str, str]
| None = None,
*,
tree: str | None = None,
selection: CutLike | None = None,
entry_start: int | None = None,
entry_stop: int | None = None,
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> ak.Array
Read branches (or evaluate expressions) into an Awkward record array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
File path(s), glob, |
required |
expressions
|
str | Sequence[str] | Mapping[str, str] | None
|
Branch names or expressions to evaluate. A mapping gives the output
field names explicitly ( |
None
|
tree
|
str | None
|
Tree name when |
None
|
selection
|
CutLike | None
|
An event-level boolean expression; events failing it are dropped. A
per-object selection raises :class: |
None
|
entry_start
|
int | None
|
Entry range to read (ignored for a |
None
|
entry_stop
|
int | None
|
Entry range to read (ignored for a |
None
|
functions
|
Mapping[str, Callable[..., Any]] | None
|
Functions of your own the expressions and the selection may call,
|
None
|
Returns:
| Type | Description |
|---|---|
Array
|
A record array with one field per expression. |
rootfig.summarize ¶
summarize(
data: Any,
variables: str | Variable | Sequence[str | Variable],
*,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
label: str | Sequence[str] | None = None,
nonfinite: NonFinitePolicy = "drop",
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> SummaryTable
Compute entries, mean, standard deviation, skewness, ... for variables and samples.
The options work as in :func:plot, functions ({name: callable}, functions
of your own the expressions may call) included.
Examples:
>>> table = rf.summarize(
... "events.root", ["MET", "Muon_pt"], tree="events", selection="nMuon > 0"
... )
>>> print(table)
>>> table.get("MET").mean
rootfig.correlation ¶
correlation(
data: Any,
variables: Sequence[str | Variable],
*,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
labels: Sequence[str] | None = None,
percent: bool = False,
cmap: str | Any = "RdBu_r",
annotate: bool = True,
title: str | None = None,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
nonfinite: NonFinitePolicy = "drop",
functions: Mapping[str, Callable[..., Any]]
| None = None,
save: str | None = None,
) -> Plot
Draw the correlation matrix of several variables for one sample.
All variables must share the same structure (all per-event, or all
per-object from one collection). The matrix is available as
Plot.matrix. The matrix is titled "<sample>: correlation"; a style
with an experiment draws that experiment's label above the matrix
instead, and an explicit title is always shown. functions
({name: callable}) are functions of your own the expressions may call,
as in :func:plot.
rootfig.cutflow ¶
cutflow(
data: Any,
cuts: Sequence[CutLike],
*,
tree: str | None = None,
weight: str | None = None,
lumi: float | str | None = None,
label: str | Sequence[str] | None = None,
nonfinite: NonFinitePolicy = "drop",
interval: EfficiencyInterval = "auto",
cl: float = ONE_SIGMA,
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> CutflowTable
Count events and weighted yields after each successive cut, per sample.
The first row holds all events (after the sample's own selection, if any);
every further row applies one more cut. Per-object cuts pass an event when
any object passes. weight, lumi, nonfinite and functions work as in
:func:plot: events with a nan/inf weight are excluded from all
steps with a warning, or raise for nonfinite="error". Yields carry
sqrt(sum w^2); efficiencies carry the confidence interval interval
names (:attr:~rootfig.Cutflow.efficiency_errors): "auto" is ROOT's
TEfficiency default for each efficiency, Clopper-Pearson when the event
weights of both steps (before the sample's scale and luminosity factor) are
unweighted as ROOT decides (their sum equals the sum of their squares:
weights of 1, or 0 and 1) and the normal approximation otherwise; the other
frequentist methods of :func:efficiency can be named, at confidence level
cl. Systematics are not propagated.
Examples:
>>> table = rf.cutflow(
... [sig, bkg], ["nMuon >= 2", rf.Cut("MET > 50", label="MET"), "any(Jet_btag > 0.8)"]
... )
>>> print(table)
>>> table.get("Signal").efficiencies
>>> table.get("Signal").efficiency_errors # (down, up)
rootfig.efficiency ¶
efficiency(
data: Any,
variable: str | Variable,
*,
passed: CutLike,
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
bins: Bins | None = None,
range: RangeSpec = None,
label: str | Sequence[str] | None = None,
xlabel: str | None = None,
ylabel: str | None = None,
unit: str | None = None,
title: str | None = None,
logx: bool | None = None,
xlim: tuple[float, float] | None = None,
ylim: tuple[float | None, float | None] | None = None,
panel: ComparisonKind | None = None,
reference: str | None = None,
panel_ylim: tuple[float, float] | None = None,
panel_label: str | None = None,
legend: bool | str | None = None,
text: str | Sequence[str] | None = None,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
cl: float = ONE_SIGMA,
interval: EfficiencyInterval = "auto",
show_empty: bool = False,
nonfinite: NonFinitePolicy = "drop",
functions: Mapping[str, Callable[..., Any]]
| None = None,
save: str | None = None,
) -> Plot
Plot the fraction of entries passing passed as a function of variable.
For every sample two histograms are filled with the same binning, all
entries satisfying selection (the denominator) and those also
satisfying passed (the numerator); the ratio is drawn as points with
confidence intervals of confidence level cl (one standard deviation,
68.27 %, by default, as ROOT's). interval="auto" gives what ROOT's
TEfficiency gives: Clopper-Pearson for unweighted entries, the normal
approximation for weighted ones. The sample's scale and luminosity
factor cancel in an efficiency and are left out, so an unweighted sample
stays unweighted. interval names another method, as ROOT means it:
"clopper-pearson", "normal", "wilson" and
"agresti-coull" (all but the normal approximation for unweighted
entries only), the Bayesian
"jeffreys" and "uniform" or any rf.Bayesian(alpha, beta, mode=,
shortest=) prior (weighted entries too; the efficiency is then the
posterior's mean or mode), and "wilson-effective", the Wilson interval
of the effective entries for weighted samples (see
:data:~rootfig.histograms.binomial.EfficiencyInterval). A binomial
interval (any but the normal approximation) is not drawn, with a warning, for
a bin that an entry with a negative weight falls into, which keeps its
efficiency. Empty bins are left out; show_empty=True draws them as
ROOT's "e0" does (0 in [0, 1], or the prior for a Bayesian interval;
for weighted samples 0 without a width, or no point for a Bayesian
interval), while a bin whose signed weights cancel holds entries and stays
undefined.
The :class:~rootfig.histograms.Efficiency
objects are returned in Plot.efficiencies. An integer bins without a
range infers one robustly, shared by numerator and denominator (see
:func:plot).
panel compares the efficiencies in a lower panel ("ratio" for a
scale factor, "relative_difference", "difference", "pull",
"asymmetry"): data over the first simulated sample, or every further
sample over the first, or every other one over the sample reference
names; the intervals are propagated as independent, keeping their
asymmetry. panel_ylim and panel_label set its range and y label, and
Plot.comparisons holds the :class:~rootfig.histograms.Comparison objects.
functions ({name: callable}) are functions of your own the variable,
selection and passed may call, as in :func:plot.
Examples:
>>> rf.efficiency(
... "reco.root", "TrueMuon_pt", passed="TrueMuon_matched", bins=(20, 0, 100)
... )
rootfig.profile ¶
profile(
data: Any,
x: str | Variable,
y: str | Variable,
*,
statistic: ProfileStatistic = "mean",
tree: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
bins: Bins | None = None,
range: RangeSpec = None,
label: str | Sequence[str] | None = None,
xlabel: str | None = None,
ylabel: str | None = None,
unit: str | None = None,
title: str | None = None,
logx: bool | None = None,
logy: bool | None = None,
xlim: tuple[float, float] | None = None,
ylim: tuple[float | None, float | None] | None = None,
panel: ComparisonKind | None = None,
reference: str | None = None,
panel_ylim: tuple[float, float] | None = None,
panel_label: str | None = None,
legend: bool | str | None = None,
text: str | Sequence[str] | None = None,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
nonfinite: NonFinitePolicy = "drop",
functions: Mapping[str, Callable[..., Any]]
| None = None,
save: str | None = None,
) -> Plot
Plot the mean (or standard deviation) of y in bins of x, per sample.
statistic="mean" draws the weighted mean with its standard error, the
profile histogram of ROOT; "std" draws the standard deviation with its
error, the usual resolution-versus-variable plot (make y the residual,
e.g. "(reco_pt - true_pt) / true_pt"). x and y must have the same
structure (both per-event or both per-object of one collection). xlabel
and unit describe the x axis; logx/logy default to the
variables' log flags. With negative weights a bin whose total weight
is negative keeps its mean but has no error, and a bin whose weighted
variance is negative has no standard deviation (nan). An integer
bins without a range infers the x range robustly (see :func:plot).
The :class:~rootfig.histograms.Profile objects are returned in
Plot.profiles. panel, reference, panel_ylim and
panel_label add a lower panel comparing the profiles, as for
:func:efficiency (panel="difference" compares the response or
resolution of two configurations). functions ({name: callable}) are
functions of your own the expressions may call, as in :func:plot.
Examples:
>>> rf.profile(
... "reco.root",
... "true_pt",
... "(reco_pt - true_pt) / true_pt",
... statistic="std",
... bins=(20, 0, 100),
... unit="GeV",
... )
rootfig.evaluate ¶
evaluate(
expression: ExpressionLike,
arrays: Mapping[str, Any] | Array,
*,
length: int | None = None,
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> ak.Array
Parse (if needed) and evaluate expression on arrays.
This is a convenience wrapper around :func:parse and
:meth:Expression.evaluate; length sizes constant expressions when
arrays is empty. functions ({name: callable}) may be called
besides the built-in ones; like any rootfig call, this one sees no others.
Examples:
>>> import awkward as ak
>>> arrays = {"pt": ak.Array([[10.0, 30.0], [], [50.0]])}
>>> evaluate("pt > 20", arrays).tolist()
[[False, True], [], [True]]
>>> evaluate("count(pt)", arrays).tolist()
[2, 0, 1]
>>> evaluate("twice(count(pt))", arrays, functions={"twice": lambda x: 2 * x}).tolist()
[4, 0, 2]
rootfig.compare ¶
compare(
numerator: Hist | Histogram | Efficiency | Profile,
reference: Hist | Histogram | Efficiency | Profile,
*,
kind: ComparisonKind = "ratio",
uncertainty: UncertaintyMode = "propagate",
) -> Comparison
Compare numerator with reference bin by bin, with uncertainties.
With n and d the contents and vn and vd the variances:
"ratio"isn / d,"relative_difference"n / d - 1(same uncertainties),"difference"n - d;"asymmetry"is(n - d) / (n + d), both sides propagated;"pull"is(n - d) / sqrt(vn + vd + syst**2), wheresystis the systematic uncertainty ofn - don the side facing the reference (the lower one wheren > d, the upper one elsewhere): a Gaussian diagnostic of the error bars, not a likelihood (deviance) residual;"s/sqrt(b)"and"s/sqrt(s+b)"are per-bin significances of the numerator as signal over the reference as background, statistical only.
Statistical uncertainties of the two sides are uncorrelated and propagated
to first order from each side's (down, up) errors
(:meth:Histogram.errors() <rootfig.histograms.Histogram.errors>; sqrt
of the variances for a plain hist.Hist): the lower error of the result
takes each side's lower or upper error, whichever lowers it, so
asymmetric errors stay asymmetric, and a pull divides by the errors
facing the other side. With symmetric errors these are the formulas
above. Histogram
inputs with systematic variations enter source by source through the varied
comparison itself: a source present on both sides varies both together (a
shared luminosity uncertainty cancels in a ratio), a source on one side only
varies that side against the other's nominal contents. The shifts of
different sources then combine like those of one histogram (see
:mod:rootfig.histograms.systematics). With uncertainty="numerator"
only the numerator's sources enter the error bars; the reference's are the
band. uncertainty="poisson-ratio" takes the exact interval of the ratio
of two Poisson means instead of the propagated one, for two histograms of
counts (see :data:UncertaintyMode). In a ratio or a relative difference,
a variation that empties a reference bin leaves that bin's systematic
uncertainty nan, with a :class:~rootfig.errors.RootfigWarning.
Two :class:~rootfig.histograms.Efficiency or two
:class:~rootfig.histograms.Profile objects compare their values the same
way, their intervals propagated as independent, so the asymmetric intervals
of an efficiency stay asymmetric. Significances, which count events, and
"numerator", which needs a band, and "poisson-ratio", which needs
counts, are refused for them.
Raises:
| Type | Description |
|---|---|
BinningError
|
If the inputs do not share the same one-dimensional binning. |
TypeError
|
For a histogram compared with an efficiency or a profile, or an efficiency with a profile. |
ValueError
|
For an unknown |
rootfig.goodness_of_fit ¶
goodness_of_fit(
a: Hist | Histogram,
b: Hist | Histogram,
*,
test: GoodnessOfFitTest = "chi2",
) -> GoodnessOfFit
Test whether two one-dimensional histograms agree, from their own uncertainties.
"chi2"is ROOT'sTH1::Chi2Test, a test of homogeneity: whether both are drawn from one shape, whatever their normalisations (ndfis the number of bins less one). Histograms known to hold counts (:meth:Histogram.counts() <rootfig.histograms.Histogram.counts>, scaled counts included, as ROOT's"UU NORM") enter as Poisson counts, every other one as weighted, so a weighted histogram is never taken for counts: counts with counts is ROOT's"UU", counts with a weighted histogram"UW"(in either order) and two weighted histograms"WW". Statistical only. As in ROOT, an empty bin of the weighted histogram of"UW"takes the variancesum(w^2) / sum(w), which does not scale like a variance, so there the result depends on that histogram's normalisation. A histogram withstat_errorsis weighted and enters with their squares, whatever counts it holds. Counts whose factor varies from bin to bin (divided by unequal bin widths, say) enter as counts only with counts of the same factors, and otherwise as weighted, with their contents."chi2-absolute"also tests the normalisation:r C^-1 rwithr = a - bandCthe statistical variances ofr, each side's error taken towards the other histogram as in a pull, plus one matrix per systematic source, fully correlated across bins (a source shared by both histograms moves them together, as in :func:compare);ndfis the number of bins. A Gaussian approximation, poor for bins with few entries."ks"is ROOT'sTH1::KolmogorovTest: the largest distance between the cumulative shapes, its probability from the effective entries of both. A histogram without uncertainties is compared as a function. For binned data the p-value is biased high, the less so the finer the binning relative to the features compared (ROOT's NOTE 3). Categories have no order to accumulate along, so it refuses a category axis.
"chi2" and "ks" need one variance per bin: the sum of squared
weights, or the square of symmetric stat_errors; asymmetric
stat_errors are refused, as no variance describes them.
Bins empty on both histograms do not enter the chi-squares; flow bins do
not enter at all. For "chi2-absolute" a bin is empty only without an
uncertainty too: one whose weights cancel to zero still enters, with its
sum of squared weights.
Every test takes the bins' uncertainties as independent, so histograms
normalised with uncertainty="shape", whose bins are correlated (see
:func:~rootfig.histograms.shape_covariance), are refused: test them
before normalising.
Raises:
| Type | Description |
|---|---|
BinningError
|
If the histograms do not share one one-dimensional binning. |
ValueError
|
For an unknown |
rootfig.log_bins ¶
log_bins(n: int, low: float, high: float) -> np.ndarray
Return n + 1 logarithmically spaced bin edges between low and high.
Examples:
>>> log_bins(3, 1, 1000).tolist()
[1.0, 10.0, 100.0, 1000.0]
rootfig.use_style ¶
use_style(style: StyleLike = None) -> Style
Apply style globally (until :func:matplotlib.pyplot.rcdefaults), return it.
This is the one function in rootfig that changes global matplotlib state;
everything else uses :func:style_context.
rootfig.dark_theme ¶
dark_theme() -> Iterator[None]
Draw everything made inside the block for a dark page.
The block runs under :data:DARK_THEME, so figures, axes and artists made
with plain matplotlib (plt.subplots() for ax=, p.ax.text(...))
match. rootfig figures apply it again after their style sheet, so it also
holds for experiment styles that fix a white background (ATLAS, LHCb,
ALICE, DUNE); Style.rc still wins. The background is transparent, and
:meth:Plot.save <rootfig.Plot.save> keeps it so, so one image suits any
dark page.
Descriptions¶
rootfig.Sample
dataclass
¶
Sample(
data: Any,
*,
tree: str | None = None,
label: str | None = None,
selection: CutLike | None = None,
weight: str | None = None,
is_data: bool = False,
color: str | None = None,
histtype: HistType | None = None,
scale: float = 1.0,
xsec: float | str | None = None,
ngen: float | str | None = None,
entry_start: int | None = None,
entry_stop: int | None = None,
systematics: Mapping[str, SystematicLike] | None = None,
)
A dataset: where it comes from, how it is labelled, and how it is drawn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
File path(s), glob pattern(s), |
required |
tree
|
str | None
|
Tree name for file inputs. Auto-detected when the file holds exactly one
|
None
|
label
|
str | None
|
Legend label. Defaults to the file stem. |
None
|
selection
|
CutLike | None
|
A selection applied to this sample only, combined with the selection
given to :func: |
None
|
weight
|
str | None
|
Expression for per-event (or per-object) weights, e.g. |
None
|
is_data
|
bool
|
Mark as observed data: drawn as points with error bars, excluded from stacks, and by default compared with the prediction in a ratio, relative difference, difference or pull panel (never a signal of a significance). |
False
|
color
|
str | None
|
Matplotlib colour. Defaults to the style's colour cycle. |
None
|
histtype
|
HistType | None
|
How to draw this sample; see :data: |
None
|
scale
|
float
|
Constant multiplied into every weight. |
1.0
|
xsec
|
float | str | None
|
Cross section of the process, in pb or as a string with a unit
( |
None
|
ngen
|
float | str | None
|
Number of generated events the cross section refers to: a number, the
name of an object in the file(s) holding it (a sum-of-weights histogram
or a |
None
|
entry_start
|
int | None
|
Read only this range of entries (counted across files). |
None
|
entry_stop
|
int | None
|
Read only this range of entries (counted across files). |
None
|
systematics
|
Mapping[str, SystematicLike] | None
|
Sources of systematic uncertainty, |
None
|
Examples:
>>> import rootfig as rf
>>> sig = rf.Sample("sig.root", tree="events", label="Signal", weight="mc_w")
>>> bkg = rf.Sample(["bkg_*.root"], tree="events", label="Background", color="gray")
>>> data = rf.Sample("data.root", tree="events", label="Data", is_data=True)
>>> mc = rf.Sample(
... "mc.root", weight="w", systematics={"pileup": ("w_pu_up", "w_pu_down"), "xsec": 0.05}
... )
files
property
¶
files: tuple[str, ...]
The resolved input files, or an empty tuple for in-memory sources.
generated_events ¶
generated_events() -> float
Return the number of generated events ngen refers to (see the class docstring).
lumi_scale ¶
lumi_scale(lumi: float | str | None) -> float
Factor turning weights into expected yields at lumi; 1 without a cross section.
xsec (pb) times lumi (fb^-1, or a string with a unit) divided by
:meth:generated_events.
replace ¶
replace(**changes: Any) -> Sample
Return a copy with the given fields changed, e.g. sample.replace(label="B").
New values are validated and normalised exactly as by the
constructor (selection accepts a string, source anything
:func:~rootfig.io.as_source accepts, scale must be finite, ...).
rootfig.Group
dataclass
¶
Group(
components: Sequence[Sample | Group],
*,
label: str,
color: str | None = None,
histtype: HistType | None = None,
)
Several samples drawn as one histogram.
The components are filled independently, each with its own source,
selection, weight, scale, cross section and systematics, and their
histograms are summed afterwards; same-named systematic sources stay
correlated (see :func:~rootfig.histograms.sum_histograms). Normalisation
applies to the sum. The summed histogram carries the group's label and
drawing hints, no sample and no unbinned statistics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
components
|
Sequence[Sample | Group]
|
:class: |
required |
label
|
str
|
Legend label of the summed histogram. |
required |
color
|
str | None
|
Drawing hints for the summed histogram, |
None
|
histtype
|
str | None
|
Drawing hints for the summed histogram, |
None
|
Examples:
>>> import rootfig as rf
>>> ww = rf.Sample("ww.root", label="WW", xsec="16.4 pb", ngen="eventsProcessed")
>>> zz = rf.Sample("zz.root", label="ZZ", xsec="1.4 pb", ngen="eventsProcessed")
>>> vv = rf.Group([ww, zz], label="VV", color="C0")
rootfig.Variable
dataclass
¶
Variable(
expression: str,
bins: Bins | None = None,
range: RangeSpec = DEFAULT_RANGE,
label: str | None = None,
unit: str | None = None,
log: bool = False,
name: str | None = None,
)
What to histogram and how to present it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expression
|
str
|
A rootfig expression (a branch name or a formula, see
:mod: |
required |
bins
|
Bins | None
|
Binning specification, see :data: |
None
|
range
|
RangeSpec
|
Range used when |
DEFAULT_RANGE
|
label
|
str | None
|
Axis label; may contain matplotlib math text. Defaults to the expression. |
None
|
unit
|
str | None
|
Physical unit appended to the axis label as |
None
|
log
|
bool
|
Draw the x axis with a logarithmic scale. |
False
|
name
|
str | None
|
Short identifier used for file names (:meth: |
None
|
safe_name
property
¶
safe_name: str
The file name component of this variable: name, else one made from the expression.
Both pass :func:~rootfig.model.check_file_stem, so :meth:Plot.save with a
directory and :class:~rootfig.PlotBook use it as it is.
replace ¶
replace(**changes: Any) -> Variable
Return a copy with the given fields changed, e.g. var.replace(bins=20).
rootfig.Cut
dataclass
¶
Cut(expression: str, label: str | None = None)
A selection expression that composes with &, | and ~.
Plain strings are accepted wherever a Cut is; the class exists so
selections can be built up and named in analysis scripts::
base = rf.Cut("nMuon >= 2", label="2 muons")
signal_region = base & "abs(Muon_eta) < 2.4" & ~rf.Cut("has_bjet")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expression
|
str
|
A rootfig expression producing booleans (see :mod: |
required |
label
|
str | None
|
Optional short description for legends or logging. |
None
|
rootfig.Style
dataclass
¶
Style(
experiment: str | None = None,
status: str | None = None,
text: str | Sequence[str] | None = None,
lumi: float | str | None = None,
com: float | str | None = None,
lumi_unit: str = "fb^{-1}",
com_unit: str = "TeV",
simulation: bool | None = None,
label_loc: int | None = None,
base: str | Mapping[str, Any] | None = None,
rc: Mapping[str, Any] = dict(),
figsize: tuple[float, float] | None = None,
colors: Sequence[str] | None = None,
legend: bool | str = True,
legend_kwargs: Mapping[str, Any] = dict(),
)
Appearance settings applied while a figure is drawn.
All fields are optional. Passing Style() (or nothing) gives the neutral
rootfig look; setting experiment switches to that experiment's mplhep
style and adds its label.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
experiment
|
str | None
|
Experiment name written in the label, e.g. |
None
|
status
|
str | None
|
Text after the experiment name: |
None
|
text
|
str | Sequence[str] | None
|
Extra line(s) below the label, e.g. |
None
|
lumi
|
float | str | None
|
Integrated luminosity shown in the label: a number in |
None
|
com
|
float | str | None
|
Centre-of-mass energy shown in the label: a number in |
None
|
lumi_unit
|
str
|
Units for numeric |
'fb^{-1}'
|
com_unit
|
str
|
Units for numeric |
'fb^{-1}'
|
simulation
|
bool | None
|
Whether to add "Simulation" to the label. |
None
|
label_loc
|
int | None
|
Placement of the experiment label (mplhep |
None
|
base
|
str | Mapping[str, Any] | None
|
Name of an mplhep style ( |
None
|
rc
|
Mapping[str, Any]
|
Additional rcParams overrides applied on top of |
dict()
|
figsize
|
tuple[float, float] | None
|
Figure size in inches. Defaults to the style's |
None
|
colors
|
Sequence[str] | None
|
Colour cycle for samples without an explicit colour. |
None
|
legend
|
bool | str
|
Draw a legend ( |
True
|
legend_kwargs
|
Mapping[str, Any]
|
Extra keyword arguments forwarded to :meth: |
dict()
|
lumi_parts
property
¶
lumi_parts: tuple[str, str] | None
(value, unit) of the luminosity, e.g. ("10.8", "ab^{-1}"), or None.
com_parts
property
¶
com_parts: tuple[str, str] | None
(value, unit) of the centre-of-mass energy, e.g. ("240", "GeV"), or None.
replace ¶
replace(**changes: Any) -> Style
Return a copy with the given fields changed, e.g. style.replace(lumi=140).
rootfig.Systematic
dataclass
¶
Systematic(kind: SystematicKind, up: Any, down: Any = None)
One source of systematic uncertainty, as an up and an optional down variation.
Sample(systematics=...) and rf.plot(systematics=...) take a mapping
from source name to one of these forms; only variations from other data
need a Systematic written out (:meth:samples):
===================================== =============================================
form variation
===================================== =============================================
"w_up", ("w_up", "w_down") weight expression(s) replacing Sample.weight
0.05 the nominal histogram scaled by 1 +- 0.05; 0 <= u < 1
(1.10, 0.95) the nominal histogram scaled by these factors
{"pt": ("pt_up", "pt_down")} branches replaced in variable, selection and weight
Systematic.samples(up, down) other files or arrays (see :meth:samples)
===================================== =============================================
Every pair is (up, down). A variation without a down direction is
symmetrised: the down variation shifts every bin by the opposite of the up
shift. The plot-level weight=, the sample's scale and the luminosity
scaling multiply every weight variation.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
SystematicKind
|
See :data: |
up, down |
The up and down variation: a weight expression ( |
symmetric
property
¶
symmetric: bool
True if the down variation is derived by mirroring the up variation.
samples
classmethod
¶
samples(up: Any, down: Any = None) -> Systematic
Take the variation from other data: file path(s), in-memory arrays or a Sample.
Anything that is not a Sample replaces only the nominal sample's data;
selection, weight, cross section, tree name and entry range are kept. A
Sample is used exactly as given.
rootfig.Bayesian
dataclass
¶
Bayesian(
alpha: float = 1.0,
beta: float = 1.0,
mode: bool = False,
shortest: bool | None = None,
)
The Bayesian interval of an efficiency with a Beta(alpha, beta) prior.
passed of total entries make the posterior Beta(passed + alpha,
total - passed + beta); with weights, the sums are scaled to the effective
entries of the total first (sum w / sum w^2), as ROOT's TEfficiency
does. The efficiency is the posterior mean, or its mode with mode=True,
and the interval the central one, or the shortest with shortest=True
(ROOT's kPosteriorMode and kShortestInterval). shortest follows
mode unless given, as TEfficiency::SetPosteriorMode also sets the
shortest interval. interval="jeffreys" is Bayesian(0.5, 0.5),
"uniform" is Bayesian(1, 1).
Raises:
| Type | Description |
|---|---|
ValueError
|
Unless |
rootfig.ONE_SIGMA
module-attribute
¶
ONE_SIGMA = math.erf(1.0 / math.sqrt(2.0))
The confidence level of one standard deviation, 68.27 %: ROOT's default for intervals.
Batch plotting¶
rootfig.PlotBook
dataclass
¶
PlotBook(
data: Any,
variables: str
| Variable
| Sequence[str | Variable]
| _AllVariables,
*,
selections: Mapping[str, CutLike | None] | None = None,
variants: Mapping[str, Mapping[str, Any]] | None = None,
plot_kwargs: Mapping[str, Any] | None = None,
include: str | Sequence[str] | None = None,
exclude: str | Sequence[str] | None = None,
)
Many :func:~rootfig.plot calls described once: variables, selections and variants.
A book holds what every plot shares (data and plot_kwargs) and the
three axes that vary. :meth:tasks lists the Cartesian product in a fixed
order (variables, then selections, then variants, each in insertion order),
:meth:plots runs it one plot at a time, :meth:save writes one file per
task under deterministic names and :meth:save_pdf one multipage PDF with
several plots per page. Each plot is what the corresponding
:func:~rootfig.plot call returns, so samples, groups, stored histograms,
arrays and histogram objects behave exactly as they do there; data is
passed on as given, never copied. The book only reads the files less often:
once per batch of variables, for every selection and variant (see
:meth:plots). The configuration mappings are copied, the values inside them
(a systematics= mapping, a Style) are shared. Names of selections and
variants are file name components and must be usable on every platform
(:func:~rootfig.model.check_file_stem). Two tasks whose file names differ
only in case or Unicode normalisation are one file on many file systems and
are rejected as a collision.
The variables are listed explicitly, in which case nothing is inspected when
the book is built, or discovered with variables=rf.ALL, which inspects
the metadata of the inputs (branch types, object classes, array types) and
never their contents; the result is an ordinary :attr:variables tuple.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
What to plot, exactly as :func: |
required |
variables
|
str | Variable | Sequence[str | Variable] | _AllVariables
|
Variables (names, expressions or :class: |
required |
selections
|
Mapping[str, CutLike | None] | None
|
Named selections, |
None
|
variants
|
Mapping[str, Mapping[str, Any]] | None
|
Named drawing variants, |
None
|
plot_kwargs
|
Mapping[str, Any] | None
|
Keywords passed to every :func: |
None
|
include
|
str | Sequence[str] | None
|
With |
None
|
exclude
|
str | Sequence[str] | None
|
With |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
No variable, an empty |
TypeError
|
|
SourceError
|
With |
Examples:
>>> book = rf.PlotBook(
... [background, signal],
... [mass, pt],
... selections={"baseline": "nMuon >= 2", "sr": "(nMuon >= 2) & (mass > 120)"},
... variants={"lin": {}, "log": {"logy": True}},
... plot_kwargs={"stack": True, "style": style},
... )
>>> book.save("plots", formats=["pdf", "png"])
>>> rf.PlotBook(
... [background, signal], rf.ALL, include=["Muon_*", "MET*"], exclude="*Index"
... ).variables
(Variable('MET'), Variable('Muon_eta'), Variable('Muon_pt'), ...)
tasks ¶
tasks() -> tuple[PlotTask, ...]
All plots of the book, variables outermost and variants innermost.
Pure: nothing is read or drawn. An implicit axis contributes one task with
the name None.
plots ¶
plots() -> Iterator[tuple[PlotTask, Plot]]
Run the tasks lazily, yielding (task, plot) one figure at a time.
Each result is what plot(data, task.variable, selection=task.selection,
**task.kwargs) returns, produced in batches: the variables are taken a
few dozen at a time, the branches they and every selection need are read
once per sample for the batch, and variants that only change the drawing
(logy, normalize, panel, style, ...) are drawn from one
set of prepared histograms, each from its own copy. A variant that
changes how the histograms are prepared (bins, weight,
observed, systematics, ...) is prepared on its own. The tasks
themselves are built one at a time and nothing is read before the first
one runs. The caller owns the figures that are yielded and closes them
(:meth:Plot.close); the figure a failing call had already created is
closed here. An error keeps its type and gains a note naming the task
(variable=..., selection=..., variant=...); one raised while reading
a batch names the batch's first task.
save ¶
save(
directory: str | PathLike[str],
*,
formats: str | Sequence[str] = DEFAULT_FORMATS,
**savefig_kwargs: Any,
) -> list[Path]
Draw every task and write it to directory as <stem>.<format>.
The directory is created if needed. Every figure is closed once it has been
written, and also when drawing or writing it fails, so memory stays bounded;
use :meth:plots to keep or customise figures. Writing goes through
:meth:Plot.save.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
str | PathLike[str]
|
Where to write. It is created if it does not exist; an existing path
that is not a directory raises :class: |
required |
formats
|
str | Sequence[str]
|
One format or several ( |
DEFAULT_FORMATS
|
**savefig_kwargs
|
Any
|
Forwarded to :meth: |
{}
|
Returns:
| Type | Description |
|---|---|
list[Path]
|
The written files, in task order and then format order. |
save_pdf ¶
save_pdf(
path: str | PathLike[str],
*,
layout: LayoutSpec = "auto",
figsize: tuple[float, float] | None = None,
**savefig_kwargs: Any,
) -> Path
Draw every task into one multipage PDF, several plots per page.
The plots follow :meth:tasks order, filling each page row by row, and
each is drawn as :func:~rootfig.plot would draw it on a figure of its
own (its panels, broken axis, style and labels), inside a cell of the page.
Pages are written one after another and each figure is closed before the
next page begins; the histograms are prepared as for :meth:plots, with
the same batched reads. The file is written next to its final name and
renamed onto it once complete, so an existing file survives a failure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
The PDF to write. |
required |
layout
|
LayoutSpec
|
|
'auto'
|
figsize
|
tuple[float, float] | None
|
Size of a whole page in inches. By default a page is as large as the
grid of figures the plots would have on their own, so a cell shows its
plot at the usual size (a lower panel makes the cells of its page
taller, a :class: |
None
|
**savefig_kwargs
|
Any
|
Forwarded to :meth: |
{}
|
Returns:
| Type | Description |
|---|---|
Path
|
The written file. |
Raises:
| Type | Description |
|---|---|
ValueError
|
A |
IsADirectoryError
|
|
Examples:
>>> book.save_pdf("overview.pdf")
PosixPath('overview.pdf')
>>> book.save_pdf("overview", layout=(2, 3), figsize=(16, 10))
PosixPath('overview.pdf')
select ¶
select(
*,
variables: str | Sequence[str] | None = None,
selections: str | Sequence[str] | None = None,
variants: str | Sequence[str] | None = None,
) -> PlotBook
Return a new book restricted to the named variables, selections and variants.
Names are :attr:~rootfig.model.Variable.safe_name values and the keys of
the selections and variants mappings; None leaves an axis as it
is and the book's order is kept. An unknown name, or a filter that names
nothing, raises and lists the choices. An implicit axis (selections=None
or variants=None) has the single choice "all" or "default" and
stays implicit.
rootfig.PlotTask
dataclass
¶
PlotTask(
variable: Variable,
selection_name: str | None,
selection: Cut | None,
variant_name: str | None,
kwargs: Mapping[str, Any],
)
One plot of a :class:PlotBook: a variable, a selection and the keywords to draw it with.
Tasks compare and hash by their :attr:stem, which is unique within a book, so
they can be collected in a set or used as dictionary keys. The keyword values
are not compared: valid ones such as bins=np.array(...) have no scalar
equality.
Attributes:
| Name | Type | Description |
|---|---|---|
variable |
Variable
|
The :class: |
selection_name |
str | None
|
The key of the selection in the book's |
selection |
Cut | None
|
The :class: |
variant_name |
str | None
|
The key of the variant in the book's |
kwargs |
Mapping[str, Any]
|
The keywords passed to :func: |
selection_id
property
¶
selection_id: str
The selection name, or "all" for the implicit no-selection choice (display only).
variant_id
property
¶
variant_id: str
The variant name, or "default" for the implicit single variant (display only).
stem
property
¶
stem: str
The output file name without suffix: <variable>[__<selection>][__<variant>].
Only axes the book was given explicitly appear, so a book without
selections= writes mass.pdf while selections={"all": ...} writes
mass__all.pdf: an explicit name is never dropped, whatever it is.
describe ¶
describe() -> str
Name the task for messages: variable='mass', selection='sr', variant='log'.
rootfig.ALL
module-attribute
¶
ALL: Final = _AllVariables()
Discover the variables of a :class:PlotBook from its inputs: PlotBook(data, rf.ALL).
Every branch of the trees (TTree or RNTuple) holding numbers or booleans,
lists and fixed-size arrays of them included, and every 1D histogram stored in
files read without a tree, when every sample provides it under every variant;
include= and exclude= narrow the set with shell patterns. Only metadata
is read. A distinct object rather than a string, so that a branch or histogram
named "all" stays an ordinary variable name.
rootfig.discover_variables ¶
discover_variables(
data: Any,
*,
selections: Mapping[str, CutLike | None] | None = None,
variants: Mapping[str, Mapping[str, Any]] | None = None,
plot_kwargs: Mapping[str, Any] | None = None,
include: str | Sequence[str] | None = None,
exclude: str | Sequence[str] | None = None,
) -> tuple[Variable, ...]
Return the variables PlotBook(data, rf.ALL, ...) would plot, without building the book.
The same discovery from metadata alone, under the same keywords (see
:class:PlotBook): every branch or stored TH1 that every sample,
observed= included, provides the same way under every variant and that
nothing in the configuration is bound to refuse, filtered by include and
exclude and sorted by source name. Unlike the book it does not check that
the output file names are distinct: two variables that sanitise to one name
(a-b and a_b) are both returned, to be told apart with
:meth:~rootfig.model.Variable.replace before they are given to a book.
Raises:
| Type | Description |
|---|---|
(SourceError, TypeError, ValueError)
|
As :class: |
Examples:
>>> variables = rf.discover_variables(samples, include="jet*")
>>> renamed = [
... v.replace(name="jet1_btag") if v.expression == "`jet1_b-tag`" else v for v in variables
... ]
>>> book = rf.PlotBook(samples, renamed)
rootfig.model.check_file_stem ¶
check_file_stem(value: str, *, what: str) -> str
Return value if it can be a file name component on every platform, else raise.
Checked once, when a :class:~rootfig.model.Variable name or a
:class:~rootfig.PlotBook selection or variant name is given, rather than
when save() reaches a file system that refuses it. what names the value
in the message, which also offers :func:safe_file_stem of the value when
that leaves anything: a spelling that passes this check.
rootfig.model.safe_file_stem ¶
safe_file_stem(text: str) -> str
Reduce text to a component that passes :func:check_file_stem, or to "".
Runs of anything but ASCII letters, digits and _ become one _, and a
stem Windows reserves (CON, COM1) gets a trailing _. "" means
nothing usable was left, as for "?". :attr:~rootfig.model.Variable.safe_name
builds the automatic name of a variable from its expression this way.
Results¶
rootfig.Plot
dataclass
¶
Plot(
fig: Figure,
ax: Axes,
panel_ax: Axes | None = None,
ax_right: Axes | None = None,
panel_ax_right: Axes | None = None,
histograms: list[Histogram] = list(),
comparisons: list[Comparison] = list(),
variable: Variable | None = None,
matrix: FloatArray | None = None,
efficiencies: list[Efficiency] = list(),
profiles: list[Profile] = list(),
stack: Histogram | None = None,
goodness_of_fit: list[GoodnessOfFit] = list(),
)
A finished figure plus the objects it was built from.
Everything is a standard matplotlib or hist object, so further
customisation is ordinary matplotlib code::
p = rf.plot(...)
p.ax.set_ylim(top=500)
p.ax.axvline(91.2, color="gray", ls="--")
p.save("z_mass.pdf")
Attributes:
| Name | Type | Description |
|---|---|---|
fig |
Figure
|
The :class: |
ax |
Axes
|
The main :class: |
panel_ax |
Axes | None
|
The lower panel's axes ( |
ax_right, panel_ax_right |
The right-hand segments when the x axis is broken ( |
|
histograms |
list[Histogram]
|
The :class: |
stack |
Histogram | None
|
The summed stacked histograms, labelled |
comparisons |
list[Comparison]
|
The :class: |
goodness_of_fit |
list[GoodnessOfFit]
|
The :class: |
variable |
Variable | None
|
The :class: |
matrix |
FloatArray | None
|
For correlation plots, the correlation matrix. |
efficiencies, profiles |
For :func: |
axes
property
¶
axes: tuple[Axes, ...]
All axes in reading order: main (left, right), then the lower panel (left, right).
uncertainty ¶
uncertainty(label: str | None = None) -> Uncertainty
Statistical and systematic uncertainties of the stack or one histogram.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str | None
|
The label of one histogram. |
None
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If no histogram has |
ValueError
|
If |
save ¶
save(
path: str | PathLike[str],
*,
formats: Sequence[str] | None = None,
**kwargs: Any,
) -> list[Path]
Save the figure and return the written paths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
Output file. If it is an existing directory (or ends with a
separator), the file name is derived from the variable
( |
required |
formats
|
Sequence[str] | None
|
Optional list of formats ( |
None
|
**kwargs
|
Any
|
Forwarded to :meth: |
{}
|
rootfig.Histogram
dataclass
¶
Histogram(
hist: Hist,
label: str,
sample: Sample | None = None,
stats: Summary | None = None,
is_data: bool = False,
color: str | None = None,
histtype: HistType | None = None,
normalization: str | None = None,
variations: Mapping[str, tuple[Hist, Hist | None]]
| None = None,
per_object: bool | None = None,
poisson: bool | float = False,
_provenance: Provenance | None = None,
*,
stat_errors: tuple[ArrayLike, ArrayLike] | None = None,
)
A filled histogram together with its provenance and drawing hints.
The underlying :class:Hist (Weight storage, so bin variances
are the sums of squared weights) is available as :attr:hist; everything
else is metadata used for legends, ratios and statistics.
Attributes:
| Name | Type | Description |
|---|---|---|
hist |
Hist
|
The histogram itself (1D or 2D). |
label |
str
|
Legend label. |
sample |
Sample | None
|
The :class: |
stats |
Summary | None
|
Unbinned :class: |
is_data |
bool
|
Whether this represents observed data. |
color, histtype |
Drawing hints, |
|
normalization |
str | None
|
Description of the normalisation applied ( |
variations |
Mapping[str, tuple[Hist, Hist]]
|
Systematic variations, |
per_object |
bool
|
Whether an entry is an object rather than an event (a per-object
variable), which words the |
poisson |
bool | float
|
Whether the statistical uncertainty is the Poisson (Garwood) interval
of the counts rather than |
Statistical errors of any other origin (a fit, a bootstrap, an asymmetric |
|
|
graph) are given as ``stat_errors=(down, up)`` |
arrays with a value per bin,
|
|
or per cell with the flow bins. They go through scaling, normalising and |
|
|
moving cells with the contents; where bins are merged they add in |
|
|
quadrature side by side, an approximation for asymmetric errors. |
|
|
``errors()`` returns them, and ``replace(hist=...)`` drops them with the old |
|
|
contents. |
|
|
Whether the contents are counts is kept apart from the error model |
it
|
|
survives ``replace(poisson=False)`` and ``stat_errors``, so the Poisson |
|
|
interval can be asked for again later. |
|
underflow
property
¶
underflow: float
Content of the underflow bin (first axis, 1D only; 0 if the axis has none).
overflow
property
¶
overflow: float
Content of the overflow bin (first axis, 1D only; 0 if the axis has none).
underflow_variance
property
¶
underflow_variance: float
Variance of the underflow bin (first axis, 1D only; 0 if the axis has none).
overflow_variance
property
¶
overflow_variance: float
Variance of the overflow bin (first axis, 1D only; 0 if the axis has none).
entries
property
¶
entries: int | None
Number of filled entries from the unbinned statistics, or None if unknown.
A histogram that was not filled by rootfig, or that sums several (a
group), carries no entry count: its bin contents are sums of weights,
which only equal the number of fills for unweighted, unscaled
histograms (see :attr:sum_weights).
errors ¶
errors(
*, flow: bool = False
) -> tuple[FloatArray, FloatArray]
Statistical uncertainty below and above the contents, (down, up).
Both are sqrt(variances), the uncertainty of a sum of weights; the
errors given as stat_errors; or, with :attr:poisson, the distances
to the Garwood interval of the counts at its confidence level, scaled
like the contents (see
:func:~rootfig.histograms.intervals.poisson_errors). The factor of
each bin comes from a record of one count per bin that goes through
every scaling, normalisation and rebinning with the contents, never from
the contents themselves, so an empty histogram scaled by 3 gets
0 +5.52 and an empty bin divided by its width its own share. The
pair is matplotlib's yerr order; drawing, comparisons and
:func:~rootfig.histograms.uncertainty take it from here.
counts ¶
counts(
*, flow: bool = False
) -> tuple[FloatArray, FloatArray]
Return the whole counts behind the contents and each bin's factor: (counts, factor).
values = counts * factor. Known for unit-weight counts (whole numbers
equal to their variances, not filled with weights: factor 1) and for
what rootfig made of them since: scaled, normalised, rebinned or summed
with counts of the same factors, whatever the error model.
Raises:
| Type | Description |
|---|---|
ValueError
|
For anything else, since sums of weights cannot say whether they are counts, and for counts scaled by a negative factor. |
replace ¶
replace(**changes: Any) -> Histogram
Return a copy with the given fields changed, e.g. h.replace(label="B").
A new hist brings its own counts: whether they are counts (see
:meth:counts) is judged from it afresh, and errors given for the old
contents (stat_errors) are dropped; pass new ones as
stat_errors. Use :meth:map_hists to transform the contents and
keep both. Switching the error model (poisson) keeps what is known
about the counts.
map_hists ¶
map_hists(
transform: Callable[[Hist], Hist],
*,
linear: bool = False,
) -> Histogram
Return a copy with transform applied to the nominal histogram and every variation.
The result has sqrt(variances) errors and holds no known counts
(:attr:poisson off, errors given as stat_errors dropped) unless
linear=True says that transform acts on the cells linearly with
non-negative coefficients, as cropping, rebinning, moving flow cells and
scaling by a positive factor do (:meth:scaled handles a negative one,
which turns intervals over). The record of counts and the given errors
then go through it too, which holds only while every cell stays counts
times factors that do not depend on the contents: merging counts of
different factors, e.g. bins divided by unequal widths, drops the
counts (and raises with :attr:poisson). Errors correlated across bins
stay correlated through any transform.
scaled ¶
scaled(factor: float) -> Histogram
Return a copy multiplied by factor.
Variances scale with factor**2. The statistics' sum of weights (and
sum of squared weights) scale along; the moments, entry count and
effective entries are unchanged by a uniform rescaling. A negative
factor turns an interval over, so errors given as stat_errors swap
their sides.
rebinned ¶
rebinned(factor: int | Sequence[int]) -> Histogram
Return a copy with every factor adjacent bins merged (per axis for a sequence).
Contents and variances add up; the flow bins are kept. Variations are rebinned alike, so ratios and uncertainty bands stay consistent. Rebin before normalising: heights per unit width or per unit area do not add.
Raises:
| Type | Description |
|---|---|
BinningError
|
If the histogram is normalised, a factor is not a positive integer dividing the axis' bin count, an axis is categorical, or the sequence does not have one entry per axis. |
rebinned_to ¶
rebinned_to(
bins: Bins | Sequence[Bins | None] | None,
range: RangeSpec | Sequence[RangeSpec] = None,
) -> Histogram
Crop and merge axes using the same bins and range as a Variable.
For 1D, give one binning and range specification. For multiple axes,
give one of each per axis; a single None or integer bin count, and a
single range (None, a string or (low, high)), apply to every
axis. A range without bins keeps the existing bins between its ends.
Requested edges, range ends included, must coincide with existing
edges. Cropped values and variances
join the flow bins, and variations follow the same transformation.
Uniform merges preserve Regular axes and their transforms; uneven
merges produce Variable axes. Unbinned statistics describe the entries
and are kept. Asking for the existing bins returns self.
Raises:
| Type | Description |
|---|---|
BinningError
|
If edges do not coincide, a count does not divide the axis size, specifications do not match the dimensionality, a categorical axis is merged, a cropped side lacks its flow bin, or a histogram is normalised. Crop and rebin before normalising. |
rootfig.Comparison
dataclass
¶
Comparison(
kind: ComparisonKind,
label: str,
reference: str,
values: FloatArray,
errors: _Errors,
edges: FloatArray,
band: _Errors | None = None,
syst_errors: tuple[FloatArray, FloatArray]
| None = None,
syst_band: tuple[FloatArray, FloatArray] | None = None,
uncertainty: UncertaintyMode = "propagate",
reference_hist: Hist | None = None,
reference_points: Efficiency | Profile | None = None,
)
A bin-by-bin comparison of a numerator with a reference.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
ComparisonKind
|
The :data: |
label, reference |
The labels of the numerator and the reference; |
|
values |
FloatArray
|
The comparison per bin; |
errors |
_Errors
|
Statistical uncertainty on |
edges |
FloatArray
|
Bin edges shared by both histograms. |
band |
_Errors | None
|
The reference's statistical uncertainty as |
syst_errors |
tuple[FloatArray, FloatArray] | None
|
Systematic uncertainty on |
syst_band |
tuple[FloatArray, FloatArray] | None
|
The reference's systematic uncertainty, in the units of |
uncertainty |
UncertaintyMode
|
The :data: |
reference_hist |
Hist | None
|
The histogram compared with, which a panel drawing several comparisons
checks they share; |
reference_points |
Efficiency | Profile | None
|
The efficiency or profile compared with, checked like |
rootfig.GoodnessOfFit
dataclass
¶
GoodnessOfFit(
test: GoodnessOfFitTest,
method: str,
statistic: float,
ndf: int | None,
p_value: float,
label: str,
reference: str,
bins: ndarray,
notes: tuple[str, ...] = (),
systematics: tuple[str, ...] = (),
)
How well two histograms agree: the result of :func:goodness_of_fit.
Attributes:
| Name | Type | Description |
|---|---|---|
test |
GoodnessOfFitTest
|
The :data: |
method |
str
|
|
statistic |
float
|
The chi-square, or for |
ndf |
int | None
|
The degrees of freedom; |
p_value |
float
|
The probability of a statistic at least as large for compatible
histograms; |
label, reference |
The labels of the two histograms, in the order given. |
|
bins |
ndarray
|
Which bins entered the test (bins empty on both sides do not). |
notes |
tuple[str, ...]
|
Conditions that make the p-value unreliable, as ROOT reports them: bins with less than one event or fewer than ten effective entries. |
systematics |
tuple[str, ...]
|
The systematic sources in the covariance of |
chi2_ndf
property
¶
chi2_ndf: float | None
The chi-square per degree of freedom; None for "ks", nan without any.
rootfig.Uncertainty
dataclass
¶
Uncertainty(
edges: FloatArray,
nominal: FloatArray,
stat_down: FloatArray,
stat_up: FloatArray,
components: Mapping[
str, tuple[FloatArray, FloatArray]
] = dict(),
)
Per-bin statistical and systematic uncertainties of a one-dimensional histogram.
All arrays cover the visible bins. Uncertainties are non-negative
magnitudes below (down) and above (up) the nominal contents.
Attributes:
| Name | Type | Description |
|---|---|---|
edges |
FloatArray
|
Bin edges. |
nominal |
FloatArray
|
Nominal bin contents. |
stat_down, stat_up |
Statistical uncertainty below and above the contents
(:meth: |
|
components |
Mapping[str, tuple[FloatArray, FloatArray]]
|
Signed shifts of every source, |
total_up
property
¶
total_up: FloatArray
Statistical and systematic uncertainty above the nominal, in quadrature.
The usual convention for an uncertainty band: with a Poisson interval, which is no Gaussian standard deviation, the sum has no exact coverage.
total_down
property
¶
total_down: FloatArray
Statistical and systematic uncertainty below the nominal (see :attr:total_up).
has_systematics
property
¶
has_systematics: bool
True if at least one systematic source contributes.
rootfig.Summary
dataclass
¶
Summary(
entries: int,
sum_weights: float,
mean: float,
std: float,
sem: float,
skewness: float,
minimum: float,
maximum: float,
n_events: int = 0,
n_selected_events: int = 0,
n_missing: int = 0,
n_nonfinite: int = 0,
per_object: bool = False,
_sum_w2: float = 0.0,
)
Weighted summary statistics of one column of values.
All moments use the weights as frequency weights. sem is the standard
error of the mean, std / sqrt(n_eff) with the Kish effective sample size
n_eff = (sum w)^2 / sum w^2.
With negative weights (e.g. NLO simulation) the weighted second moment can
be negative; the standard deviation, its error and the skewness are then
nan (the histogram itself is unaffected).
effective_entries
property
¶
effective_entries: float
Kish effective sample size, equal to entries for unit weights.
format ¶
format(
precision: int = 4, *, include_entries: bool = True
) -> str
Return a compact multi-line text, e.g. for a statistics box on a plot.
Uses matplotlib math text for the symbols so it renders with the plot
fonts ($\mu$, $\sigma$).
rootfig.SummaryTable
dataclass
¶
SummaryTable(rows: tuple[tuple[str, str, Summary], ...])
Summary statistics for several variables and samples.
str(table) gives an aligned text table; :meth:get returns a single
:class:~rootfig.histograms.Summary.
rootfig.Cutflow
dataclass
¶
Cutflow(
sample: str,
steps: tuple[CutflowStep, ...],
interval: EfficiencyInterval = "auto",
cl: float = ONE_SIGMA,
)
The cut flow of one sample: a sequence of :class:CutflowStep.
interval is the confidence interval of the efficiency errors (see
:data:~rootfig.histograms.binomial.EfficiencyInterval). "auto" is
decided for each efficiency apart, as TEfficiency decides for the two
histograms of one: Clopper-Pearson when both steps' sums of weights equal
their sums of squared weights (unweighted events; weights of 0 and 1 are
unweighted), else the normal approximation. A cut that removes every
weighted event therefore makes the later relative efficiencies
Clopper-Pearson, while those measured against the first step stay normal.
cl is the confidence level of the intervals. Bayesian intervals are
refused: they report the posterior's mean or mode as the efficiency, where a
cut flow reports the ratio of its yields.
efficiencies
property
¶
efficiencies: ndarray
Weighted efficiency of each step relative to the previous one (1 for the first).
The plain ratio of the summed event weights, the yields without the
sample's scale and luminosity factor, which cancel: nan where the
previous sum is zero. With signed (NLO) weights a sum can be negative,
and the ratio may then lie outside [0, 1]; it is still reported.
absolute_efficiencies
property
¶
absolute_efficiencies: ndarray
Weighted efficiency of each step relative to the first (see :attr:efficiencies).
efficiency_errors
property
¶
efficiency_errors: tuple[ndarray, ndarray]
(down, up) statistical errors of :attr:efficiencies, 0 for the first step.
A step keeps a subset of the previous step's events, so its efficiency
is that of a pass fraction, never a ratio of two independent yields:
the :attr:interval at confidence level :attr:cl of the summed event
weights, as TEfficiency computes it from the histograms' contents
(for unweighted events the number of events of weight 1, so events of
weight 0 are no trials). nan where the
efficiency is undefined or lies outside [0, 1], and for a binomial
interval after a step holding a negative weight
(:attr:CutflowStep.negative_weights).
absolute_efficiency_errors
property
¶
absolute_efficiency_errors: tuple[ndarray, ndarray]
(down, up) errors of :attr:absolute_efficiencies, as :attr:efficiency_errors.
Every step keeps a subset of the first step's events, the denominator.
rootfig.CutflowTable
dataclass
¶
CutflowTable(rows: tuple[Cutflow, ...])
Cut flows of several samples with the same cuts; str(table) is an aligned text table.
rootfig.Efficiency
dataclass
¶
Efficiency(
values: FloatArray,
lower: FloatArray,
upper: FloatArray,
edges: FloatArray,
label: str = "",
)
Bin-by-bin efficiency passed / total with a confidence interval.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
The efficiency |
lower, upper |
Bounds of the confidence interval (see :func: |
|
edges |
FloatArray
|
Bin edges. |
label |
str
|
Legend label. |
errors
property
¶
errors: tuple[FloatArray, FloatArray]
(values - lower, upper - values), ready for yerr.
Never negative: a posterior's mode can lie outside its central interval, which then reaches only one way.
rootfig.Profile
dataclass
¶
Profile(
values: FloatArray,
errors: FloatArray,
counts: FloatArray,
edges: FloatArray,
statistic: ProfileStatistic = "mean",
label: str = "",
)
A statistic of y in bins of x.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
The weighted mean ( |
errors |
FloatArray
|
Standard error: |
counts |
FloatArray
|
Sum of weights per bin. |
edges |
FloatArray
|
Bin edges. |
statistic, label |
What is shown and the legend label. |
Errors and warnings¶
rootfig.errors ¶
Exception and warning types raised by rootfig.
All exceptions derive from :class:RootfigError so callers can catch
everything from the library with a single except clause. The more
specific subclasses also inherit from the matching built-in exception
(ValueError, KeyError-like LookupError) so generic handlers
keep working.
RootfigError ¶
Bases: Exception
Base class for all errors raised by rootfig.
ExpressionError ¶
Bases: RootfigError, ValueError
An expression string could not be parsed, is not allowed, or failed to evaluate.
MissingBranchError ¶
MissingBranchError(
name: str,
*,
available: Sequence[str] = (),
suggestions: Sequence[str] = (),
context: str | None = None,
)
Bases: ExpressionError, LookupError
An expression refers to a name that is not a branch, alias, function, or constant.
SelectionError ¶
Bases: RootfigError, ValueError
Variable, selection, and weight arrays have incompatible structures.
IncompatibleWeightError ¶
Bases: SelectionError
A weight array cannot be broadcast to the variable being histogrammed.
BinningError ¶
Bases: RootfigError, ValueError
A binning or range specification is invalid or cannot be inferred.
SourceError ¶
Bases: RootfigError
A file, tree, or in-memory data source cannot be opened or interpreted.
LuminosityError ¶
Bases: RootfigError, ValueError
A sample cannot be scaled to a luminosity (no luminosity, cross section or event count).
SystematicError ¶
Bases: RootfigError, ValueError
A systematic variation is malformed or cannot be applied to a sample or histogram.
RootfigWarning ¶
Bases: UserWarning
Base class for warnings emitted by rootfig (dropped values, empty selections, ...).
annotate ¶
annotate(note: str) -> Iterator[None]
Attach note to any :class:RootfigError raised inside the block.
The note is shown below the message (PEP 678). rootfig uses it to name the systematic variation an error was raised for.
Lower layers¶
These are the layers rf.plot is built from, usable on their own; names a module
does not list here are internal. The descriptions in rootfig.model are documented
above.
rootfig.io ¶
Reading branch arrays from ROOT files (uproot), NumPy .npz files or memory.
FileSource
dataclass
¶
FileSource(
files: FilesLike,
tree: str | None = None,
*,
entry_start: int | None = None,
entry_stop: int | None = None,
)
Branch arrays read from one or more ROOT files with uproot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
files
|
FilesLike
|
See :data: |
required |
tree
|
str | None
|
Name of the |
None
|
entry_start
|
int | None
|
Restrict reading to a range of entries counted across all files, in the
same way as :func: |
None
|
entry_stop
|
int | None
|
Restrict reading to a range of entries counted across all files, in the
same way as :func: |
None
|
resolved_tree ¶
resolved_tree() -> str
Return the tree name, auto-detecting it from the first file if needed.
branches ¶
branches() -> list[str]
Return the branch (or RNTuple field) names of the tree in the first file.
The leaves only, a nested field by its dotted path (Muon.pt): the
branches a TTree lists, the fields of an RNTuple's schema. The
result is cached on the instance; the file is opened only once.
branch_forms ¶
branch_forms() -> dict[str, Any]
Return the Awkward form of every branch of :meth:branches, as :meth:arrays reads it.
The forms come from the tree's metadata alone (the interpretation of each
TTree branch, the field schema of an RNTuple), so no entry is
read; they tell the type and structure of a branch, which
:mod:rootfig.io.schema classifies. A TTree branch uproot has no
interpretation for, or whose interpretation has no Awkward form, is left
out: :meth:arrays could not read it either. Cached on the instance like
:meth:branches.
num_entries ¶
num_entries() -> int
Return the number of entries (all files, honouring the entry range).
read_scalar ¶
read_scalar(key: str) -> float
Sum a number stored under key in every file.
Accepts histograms (their total content including flow bins, e.g. a
sum-of-weights histogram) and TParameter objects (their value).
Typical use: the number of generated events written by a framework. The
result is cached on the instance, like :meth:branches, so scaling many
histograms to a luminosity opens the files once.
objects ¶
objects() -> dict[str, str]
Return {name: class} for every object in the first file (cycle numbers stripped).
Objects inside directories are listed as "dir/name". The result is
cached on the instance.
histograms ¶
histograms(ndim: int | None = None) -> list[str]
Return the names of the histograms (TH1*, TH2*) in the first file, sorted.
ndim keeps the 1D or the 2D ones only; a histogram inside a directory
is listed as "dir/name".
read_histogram ¶
read_histogram(
name: str, *, variances_from_contents: bool = False
) -> Hist
Read the histogram stored as name in every file and return their sum.
The result has Weight storage; see :func:rootfig.io.objects.read_histograms
for how uncertainties are treated and what variances_from_contents accepts.
name may address an object inside a directory ("dir/name").
read_histograms ¶
read_histograms(
names: Sequence[str],
*,
variances_from_contents: bool = False,
) -> dict[str, Hist]
Read the histograms stored as names in every file and return their sums, by name.
Each file is opened once for all of them; otherwise as :meth:read_histogram.
arrays ¶
arrays(branches: Sequence[str]) -> dict[str, ak.Array]
Read branches from all files and concatenate them.
iterate ¶
iterate(
branches: Sequence[str], chunk_bytes: int | None = None
) -> Iterator[dict[str, ak.Array]]
Read branches a chunk of entries at a time, in order.
A chunk holds about chunk_bytes of arrays, :data:CHUNK_BYTES unless given;
an RNTuple cluster larger than that, which is read whole, makes a chunk alone.
The chunks, concatenated, are what :meth:arrays returns, entry range
included, while only one of them is held here at a time. Small pieces (under
an eighth of a chunk: the end of a file, small files, small RNTuple clusters)
are joined with what follows as long as together they fit in a chunk; a
larger piece makes a chunk of its own, since joining copies it. At least one
chunk is yielded, an empty one when no entry is in range, so the types of the
branches are always known.
NpzSource
dataclass
¶
NpzSource(
files: FilesLike,
*,
entry_start: int | None = None,
entry_stop: int | None = None,
)
Branch arrays read from one or more .npz files, one array per branch.
Every array with at least one dimension is a branch with one entry per event:
a flat array is per-event, an (events, n) array per-object, like any
fixed-size branch. Arrays of no dimension (a scalar saved beside the events)
are not branches. Object arrays (ragged lists saved by NumPy) are refused,
since NumPy can only load them by unpickling, which runs code from the file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
files
|
FilesLike
|
A local path, glob pattern, or a sequence of them; see :data: |
required |
entry_start
|
int | None
|
Keep only this range of entries, counted across all files (Python slice semantics). Only the files the range overlaps are read. |
None
|
entry_stop
|
int | None
|
Keep only this range of entries, counted across all files (Python slice semantics). Only the files the range overlaps are read. |
None
|
branches ¶
branches() -> list[str]
Return the names of the arrays in the first file, scalars left out.
branch_forms ¶
branch_forms() -> dict[str, Any]
Return the Awkward form of every branch, read from the array headers, not the values.
arrays ¶
arrays(branches: Sequence[str]) -> dict[str, ak.Array]
Read branches within the entry range, joined in file order.
Each file's arrays are cut to its part of the range before they are joined, so a short range of many large files holds only that part.
num_entries ¶
num_entries() -> int
Return the number of events within the entry range, from the array headers.
ArraySource
dataclass
¶
ArraySource(
data: Mapping[str, Any] | Array | ndarray,
*,
entry_start: int | None = None,
entry_stop: int | None = None,
)
Branch arrays already in memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Mapping[str, Any] | Array | ndarray
|
A mapping from branch name to array-like, an Awkward record array, or a NumPy structured array. All columns must have the same length. |
required |
entry_start
|
int | None
|
Keep only this range of entries (Python slice semantics). |
None
|
entry_stop
|
int | None
|
Keep only this range of entries (Python slice semantics). |
None
|
branch_forms ¶
branch_forms() -> dict[str, Any]
Return the Awkward form of every column: its type, read off the array, not its values.
Source ¶
Bases: Protocol
Anything that can list branch names and return branch arrays.
Implementations must be cheap to construct; opening files or reading data
should happen inside :meth:branches and :meth:arrays.
arrays ¶
arrays(branches: Sequence[str]) -> dict[str, ak.Array]
Read the given branches and return them as Awkward arrays of equal length.
ReadCache ¶
ReadCache()
Branch arrays and stored histograms read once, served to every histogram built from them.
Keyed by a :class:FileSource's value (files, tree, entry range), so two
samples over the same files, or the fresh source a systematic variation
builds over them, share one read. :meth:arrays reads only the names it
does not hold yet and keeps them, so the cache can be warmed with the union
of what a batch will need (:func:rootfig.histograms.prefetch) or simply
fill as histograms are built. :meth:source hands out one instance per
value, so what a source learns about its files is learnt once too.
In-memory and third-party sources are not cached; callers read those
directly. Drop the cache to release what it holds.
source ¶
source(source: FileSource) -> FileSource
Return the instance held for source's value, adopting source if it is the first.
A :class:FileSource keeps what it learns about its files (branches,
objects, tree, entry count, scalars) on the instance. Sources rebuilt
from the same paths for every histogram, such as raw paths given as
data or the files of a Systematic.samples variation, read through
the first instance and so learn it once. Reading through the cache
applies this (:func:~rootfig.histograms.sources.shared_source), so
callers only need it themselves for what they read from a source
directly.
arrays ¶
arrays(
source: FileSource, branches: Sequence[str]
) -> dict[str, ak.Array]
Return branches of source, reading the ones not held yet.
A fresh mapping holding exactly the requested names in their order, as
:meth:FileSource.arrays returns them.
histogram ¶
histogram(
source: FileSource,
name: str,
*,
variances_from_contents: bool = False,
) -> Hist
Return the stored histogram name of source, summed over its files, read once.
histograms ¶
histograms(
source: FileSource,
names: Sequence[str],
*,
variances_from_contents: bool = False,
) -> None
Read the stored histograms names of source not held yet, one open per file.
as_source ¶
as_source(
data: Any,
*,
tree: str | None = None,
entry_start: int | None = None,
entry_stop: int | None = None,
) -> Source
Turn user input into a :class:Source.
Strings, paths, and sequences of them become a :class:FileSource, or an
:class:~rootfig.io.NpzSource when every file they resolve to ends in
.npz (tree does not apply to those, as to in-memory arrays); mappings,
Awkward record arrays and NumPy structured arrays become an
:class:ArraySource; existing sources are returned unchanged. An entry
range (or, for a :class:FileSource, a different tree) cannot be
applied to an existing source afterwards and raises :class:SourceError;
give them to the source when constructing it.
rootfig.expressions ¶
Expression strings over branches.
rootfig expressions use ordinary Python syntax and are evaluated with NumPy
and Awkward Array semantics, so Muon_pt > 20 produces a boolean array
with the same (possibly jagged) structure as Muon_pt.
Supported syntax
- Names refer to branches (
Muon_pt). Branch names that are not valid Python identifiers are written in backticks:`jet1_b-tag` > 0.5. - Arithmetic
+ - * / // % **and unary-. - Comparisons
== != < <= > >=including chained comparisons (20 < Muon_pt < 100), which are rewritten element-wise. - Boolean combinations with
&,|,~or withand,or,not; the keyword forms are rewritten to the element-wise operators. - Indexing and slicing (
Muon_pt[:, 0]). - A fixed set of functions (see :data:
FUNCTIONS): element-wise NumPy functions such asabs,sqrt,log,where, and per-event reductions over jagged branches:count,sum,min,max,mean,any,all,first. - Functions of the caller's own, given as
functions={name: callable}to the rootfig call (or to :func:parse). - Constants
pi,e,inf,nan,True,False.
Everything else (attribute access, lambdas, comprehensions, string literals,
calls to unknown functions, names beginning with __rootfig_ outside
backticks) is rejected at parse time with :class:~rootfig.errors.ExpressionError.
The public entry points are :func:parse, :class:Expression, and
:func:evaluate.
FUNCTIONS
module-attribute
¶
FUNCTIONS: Final[Mapping[str, Callable[..., Any]]] = (
MappingProxyType(
{
"abs": np.abs,
"sqrt": np.sqrt,
"cbrt": np.cbrt,
"exp": np.exp,
"expm1": np.expm1,
"log": np.log,
"log10": np.log10,
"log2": np.log2,
"log1p": np.log1p,
"power": np.power,
"hypot": np.hypot,
"sin": np.sin,
"cos": np.cos,
"tan": np.tan,
"arcsin": np.arcsin,
"arccos": np.arccos,
"arctan": np.arctan,
"arctan2": np.arctan2,
"sinh": np.sinh,
"cosh": np.cosh,
"tanh": np.tanh,
"arcsinh": np.arcsinh,
"arccosh": np.arccosh,
"arctanh": np.arctanh,
"deg2rad": np.deg2rad,
"rad2deg": np.rad2deg,
"floor": np.floor,
"ceil": np.ceil,
"round": np.rint,
"trunc": np.trunc,
"sign": np.sign,
"minimum": np.minimum,
"maximum": np.maximum,
"clip": _clip,
"isnan": np.isnan,
"isinf": np.isinf,
"isfinite": np.isfinite,
"where": ak.where,
"count": _reduction("count", ak.num),
"len": _reduction("len", ak.num),
"sum": _reduction("sum", ak.sum),
"prod": _reduction("prod", ak.prod),
"min": _reduction("min", ak.min),
"max": _reduction("max", ak.max),
"mean": _reduction("mean", ak.mean),
"std": _reduction("std", ak.std),
"any": _reduction("any", ak.any),
"all": _reduction("all", ak.all),
"argmin": _reduction("argmin", ak.argmin),
"argmax": _reduction("argmax", ak.argmax),
"first": _first,
"pt": _pt,
"p": _p,
"theta": _theta,
"costheta": _costheta,
"eta": _eta,
"phi": _phi,
"mass": _mass,
}
)
)
Functions callable from expressions, keyed by the name used in the expression.
CONSTANTS
module-attribute
¶
CONSTANTS: Final[Mapping[str, float]] = MappingProxyType(
{
"pi": math.pi,
"e": math.e,
"inf": math.inf,
"nan": math.nan,
}
)
Named constants available in expressions when no branch of that name exists.
Expression
dataclass
¶
Expression(
text: str,
names: tuple[str, ...],
functions: tuple[str, ...],
_code: CodeType,
_backticks: Mapping[str, str] = dict(),
_callables: tuple[Callable[..., Any], ...] = (),
)
A parsed, validated expression ready to be evaluated on arrays.
Create instances with :func:parse (or pass strings anywhere rootfig
accepts an expression; they are parsed on the fly).
Attributes:
| Name | Type | Description |
|---|---|---|
text |
str
|
The original expression string. |
names |
tuple[str, ...]
|
Names referenced by the expression in order of first appearance. These
are candidate branch names; a name that is not a branch may still
resolve to a constant ( |
functions |
tuple[str, ...]
|
Names of the functions called by the expression, built-in or given with
|
is_trivial
property
¶
is_trivial: bool
True if the expression is a bare name (a single branch, no computation).
required_branches ¶
required_branches(available: Collection[str]) -> list[str]
Return the referenced names that must be read from available branches.
Names that are not available but match a constant are skipped. Any
other unknown name raises :class:~rootfig.errors.MissingBranchError
with close-match suggestions.
evaluate ¶
evaluate(
arrays: Mapping[str, Any] | Array,
*,
length: int | None = None,
) -> ak.Array
Evaluate the expression using arrays to resolve branch names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arrays
|
Mapping[str, Any] | Array
|
Either a mapping from branch name to array, or an Awkward record array whose fields are the branches. |
required |
length
|
int | None
|
Number of events a constant expression ( |
None
|
Returns:
| Type | Description |
|---|---|
Array
|
The result, converted to an Awkward array if the expression produced a NumPy array or a scalar. |
parse ¶
parse(
expression: ExpressionLike,
*,
functions: Mapping[str, Callable[..., Any]]
| None = None,
) -> Expression
Parse and validate an expression string.
Calls may name the built-in functions (:data:FUNCTIONS) and those in
functions, {name: callable}. Without functions, they are the ones
given to the rootfig call this runs in (none outside one). The expression
keeps the functions it was parsed with, so an :class:Expression is returned
as it is, and refused together with functions.
Raises:
| Type | Description |
|---|---|
ExpressionError
|
If the text is not a single Python expression or uses a disallowed
construct (unknown functions, string literals, lambdas, comprehensions,
conditional expressions, names beginning with |
rootfig.selection ¶
Selection semantics: combine variable, cut and weight arrays into flat columns.
Columns
dataclass
¶
Columns(
arrays: tuple[ndarray, ...],
weights: ndarray | None,
n_events: int,
n_selected_events: int,
n_missing: int = 0,
n_nonfinite: int = 0,
per_object: bool = False,
unit_weights: bool = False,
)
Flat, finite, aligned columns ready to fill a histogram.
Attributes:
| Name | Type | Description |
|---|---|---|
arrays |
tuple[ndarray, ...]
|
One flat |
weights |
ndarray | None
|
Weights aligned with |
n_events |
int
|
Number of events in the input arrays. |
n_selected_events |
int
|
Number of events contributing at least one entry after selection. |
n_missing |
int
|
Entries dropped because a value (or weight) was |
n_nonfinite |
int
|
Entries dropped because a value (or weight) was |
per_object |
bool
|
True if the variable was jagged (one entry per object rather than per event). |
unit_weights |
bool
|
True if the |
weighted
property
¶
weighted: bool
True unless every entry counts once or not at all (no weights, or 0 and 1 only).
A histogram filled from unweighted columns holds counts, as ROOT treats a
TH1 whose sum of weights equals its sum of squared weights.
effective_weights ¶
effective_weights() -> np.ndarray
Weights as an array, 1.0 everywhere when unweighted.
concatenate
classmethod
¶
concatenate(parts: Sequence[Columns]) -> Columns
Join the columns of consecutive chunks of events into those of all of them, in order.
What :func:prepare returns for the concatenated arrays: the values in the
same order, the event and entry counts added up.
prepare ¶
prepare(
arrays: Mapping[str, Any] | Array,
variables: ExpressionLike | Sequence[ExpressionLike],
*,
selection: ExpressionLike | None = None,
weight: ExpressionLike | None = None,
scale: float = 1.0,
nonfinite: NonFinitePolicy = "drop",
context: str = "",
n_events: int | None = None,
report: bool = True,
) -> Columns
Evaluate expressions and apply the selection/weight rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arrays
|
Mapping[str, Any] | Array
|
Branch arrays (mapping or record array) covering every name used. |
required |
variables
|
ExpressionLike | Sequence[ExpressionLike]
|
One expression, or several that must share the same structure (used for 2D histograms and correlations). |
required |
selection
|
ExpressionLike | None
|
Boolean expression, or |
None
|
weight
|
ExpressionLike | None
|
Weight expression, or |
None
|
scale
|
float
|
Constant factor multiplied into the weights. |
1.0
|
nonfinite
|
NonFinitePolicy
|
|
'drop'
|
context
|
str
|
Short text (e.g. the sample label) prepended to warnings. |
''
|
n_events
|
int | None
|
Number of events, needed only when every expression is a constant
( |
None
|
report
|
bool
|
|
True
|
Returns:
| Type | Description |
|---|---|
Columns
|
Flat aligned values and weights. |
depth_of ¶
depth_of(array: Array) -> int
Return the list depth of array: 1 for flat, 2 for jagged, and so on.
rootfig.histograms ¶
Histogram construction, normalisation, comparisons and statistics.
build_histograms ¶
build_histograms(
items: Sequence[Sample | Group],
variable: Variable | str,
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
systematics: Mapping[str, SystematicLike] | None = None,
variances_from_contents: bool = False,
cache: ReadCache | None = None,
) -> list[Histogram]
Fill one 1D histogram per sample or group, with a binning shared by all of them.
Systematic variations (the sample's own and systematics, which apply to
every non-data sample; a sample's own source of the same name wins) are filled into
:attr:~rootfig.histograms.Histogram.variations with the binning chosen
from the nominal values. A :class:~rootfig.model.Group is filled through
its samples, which share the binning like any other, and gets the sum of
their histograms (:func:~rootfig.histograms.groups.group_histogram).
A bare variable name that addresses a histogram stored in the samples'
files (see :func:~rootfig.histograms.stored_mode) is read instead of
filled; variances_from_contents then accepts stored histograms without a sum of
squared weights.
A cache (:class:~rootfig.io.ReadCache) serves the branch arrays and
stored histograms it holds and reads the rest, so several histograms built
from the same files read them once; the result does not depend on it.
fill ¶
fill(axes: Sequence[Axis], columns: Columns) -> Hist
Create a Hist with Weight storage and fill it from columns.
One axis is expected per column in columns.arrays. Unweighted data is
filled without weights, so bin variances equal bin counts.
sum_histograms ¶
sum_histograms(
histograms: Sequence[Histogram], *, label: str = "Total"
) -> Histogram
Add one-dimensional histograms bin by bin, keeping their systematic variations.
Variations are matched by name and added linearly (fully correlated); a
histogram without a source contributes its nominal contents to it. The sum
keeps the inputs' normalization when they all share it and has none
otherwise, so it never claims a scaling one of its parts lacks; it counts
objects (per_object) if any input does, and is observed data
(is_data) if every input is. Counts of one factor add up to counts of
it, so the sum holds known counts (:meth:~rootfig.histograms.Histogram.counts)
when every input does with the same factor per count in every bin, however
the inputs reached their binning, and keeps the Poisson interval
(:attr:~rootfig.histograms.Histogram.poisson) when every input also has
it at one confidence level, as TH1::Add keeps kPoisson for
unweighted histograms. Otherwise, if an input has errors of its own
(stat_errors), each input's (down, up) errors add in quadrature
side by side, an approximation for asymmetric errors; else the sum has
sqrt(sum w^2), as ROOT falls back to it.
Raises:
| Type | Description |
|---|---|
BinningError
|
If there are no histograms, an input is not one-dimensional, or the bin edges or flow-bin traits differ. Axis names and labels may differ. |
uncertainty ¶
uncertainty(histogram: Histogram) -> Uncertainty
Summarise the statistical and systematic uncertainties of a 1D histogram.
Raises:
| Type | Description |
|---|---|
BinningError
|
If the histogram is not one-dimensional. |
poisson_interval ¶
poisson_interval(
counts: ArrayLike, cl: float = ONE_SIGMA
) -> tuple[FloatArray, FloatArray]
Garwood's central interval of a Poisson mean for whole counts: (lower, upper).
The bounds cover the confidence level cl (68.27 % by default, ROOT's
TH1::kPoisson; 0.95 is TH1::kPoisson2), half the remainder on each
side: P(N >= n | lower) and P(N <= n | upper) are both
(1 - cl) / 2. The lower bound of n = 0 is 0, its upper bound
-log((1 - cl) / 2) (1.84 at one standard deviation). Both are gamma
quantiles (SciPy's inverse incomplete gamma functions), as in ROOT.
Raises:
| Type | Description |
|---|---|
ValueError
|
If a count is not a non-negative whole number or |
shape_covariance ¶
shape_covariance(
histogram: Histogram | Hist, spec: NormalizeSpec = True
) -> FloatArray
Return the covariance matrix of the visible bins of histogram normalised by spec.
The histogram's own total fluctuates with its bins, so they are
anti-correlated (see :data:NormalizeUncertainty): the first-order
covariance J V J^T of normalize(histogram, spec), with V the
diagonal of the sums of squared weights and the bins flattened in C order.
Its diagonal holds the variances that normalize(...,
uncertainty="shape") gives a histogram of summed weights; for counts with
a Poisson interval that gives the exact marginal interval of each bin
instead, which no covariance matrix holds.
Raises:
| Type | Description |
|---|---|
ValueError
|
Unless |
summarize ¶
summarize(columns: Columns, index: int = 0) -> Summary
Compute :class:Summary statistics for column index of columns.
read_stored ¶
read_stored(
samples: Sequence[Sample],
variables: Sequence[Variable],
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
systematics: Mapping[str, SystematicLike] | None = None,
variances_from_contents: bool = False,
include_systematics: bool = True,
cache: ReadCache | None = None,
) -> list[Histogram]
Read the histogram named by variables from every sample's files.
One :class:~rootfig.histograms.Histogram per sample, in Weight
storage, scaled by the sample's scale and luminosity factor. A TH1
saved with kPoisson or kPoisson2 brings its Poisson interval (see
:func:with_saved_errors) unless a factor other than 1 made it weighted. A
variable's bins and range crop and merge the stored bins. An integer
count merges the whole axis; explicit edges must coincide with stored ones,
and a range without bins keeps the stored bins between its ends, which must
be stored edges too. Cropped content joins the
flow bins (see :func:~rootfig.model.binning.merge_target). label and
unit replace the stored axis title. Systematics of kind "norm" scale the
histogram and Systematic.samples reads the same name from other files,
which are checked like the nominal ones; the other kinds need event data.
include_systematics=False reads the nominal histograms only and leaves
the samples' systematics unexamined, for callers that draw no variations
(2D plots); the samples themselves are kept on the result as given. A
cache (:class:~rootfig.io.ReadCache) serves the stored histograms it
holds and reads the rest.
Raises:
| Type | Description |
|---|---|
SelectionError
|
For a selection, weight or |
BinningError
|
If requested edges do not coincide with stored edges, a count does not divide the stored bin count, or a cropped side lacks a flow bin. |
SystematicError
|
For weight or branch-replacement systematics, or a variation whose sample addresses a tree or in-memory data instead of stored histograms. |
SourceError
|
If the stored histogram has another dimensionality than |
compatible_binning ¶
compatible_binning(a: Hist, b: Hist) -> bool
Return True if both histograms are one-dimensional and bin the same way.
Numeric axes must have the same edges up to round-off (a millionth of the smallest bin width, so bins shifted by a whole width at large coordinates are rejected); category axes the same categories in the same order. Flow bins and axis names or labels do not matter here: this decides whether two histograms can be stacked, summed or divided bin by bin.
normalize ¶
normalize(
histogram: Histogram,
spec: NormalizeSpec,
*,
uncertainty: NormalizeUncertainty = "scale",
) -> Histogram
Return a normalised copy of a :class:Histogram, recording the mode.
uncertainty says what normalising to the histogram's own total does to
its statistical uncertainty (see :data:NormalizeUncertainty): "scale"
keeps every bin's relative uncertainty, as TH1::Scale; "shape"
lets the total fluctuate with the bins, and raises ValueError for a
histogram with stat_errors.
normalization is set only when the normalisation was actually applied;
a histogram that could not be normalised (see :func:normalize_hist) keeps
None so labels do not claim a scaling that did not happen.
Systematic variations are normalised the same way as the nominal histogram,
each by its own total, so a normalised plot shows how a variation changes the
shape; a pure normalisation uncertainty drops out of the rescaling modes
(unity, density and numeric targets). width retains it.
If the nominal cannot be normalised, all variations stay unchanged too.
A variation that cannot be normalised when the nominal can raises
:class:~rootfig.errors.SystematicError, avoiding a mixture of raw and
normalised contents in the uncertainty.
rootfig.plotting ¶
Rendering histograms with matplotlib and mplhep.
Layout
dataclass
¶
Layout(
fig: Figure,
main: Axes,
panel: Axes | None = None,
main_right: Axes | None = None,
panel_right: Axes | None = None,
)
The axes of a figure: main panel, optional lower panel, optional right segments.
With a broken x axis every panel exists twice: main/main_right and
panel/panel_right show the left and right segments.
panel_axes
property
¶
panel_axes: tuple[Axes, ...]
The lower-panel axes, left to right (empty without a lower panel).
axes
property
¶
axes: tuple[Axes, ...]
Every axes of the layout: main (left, right), then the lower panel (left, right).
legend_axes
property
¶
legend_axes: Axes
Where the legend and statistics box go: the right-most main axes.
draw_histograms ¶
draw_histograms(
histograms: Sequence[Histogram],
ax: Axes,
*,
style: Style,
stack: StackSpec = False,
histtype: HistType | None = None,
errorbars: bool | None = None,
flow: FlowSpec = "hint",
stack_uncertainty: bool = True,
alpha: float | None = None,
view: Sequence[tuple[float, float]] | None = None,
) -> Drawn
Draw histograms on ax.
Histograms flagged is_data are always drawn as points with error
bars on top; the others are overlaid (default) or stacked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
histograms
|
Sequence[Histogram]
|
Histograms to draw, in input order. |
required |
ax
|
Axes
|
Target axes. |
required |
style
|
Style
|
Style providing the colour cycle. |
required |
stack
|
StackSpec
|
|
False
|
histtype
|
HistType | None
|
Default drawing type for overlaid histograms without their own
|
None
|
errorbars
|
bool | None
|
Draw statistical error bars on overlaid histograms. |
None
|
flow
|
FlowSpec
|
Under/overflow display, see :data: |
'hint'
|
stack_uncertainty
|
bool
|
Draw a hatched band for the uncertainty of the stack total: statistical, and systematic where the histograms carry variations. Overlaid histograms with variations get a light band in their own colour. |
True
|
alpha
|
float | None
|
Opacity for filled histograms (default 1 for stacks, 0.45 for overlays). |
None
|
view
|
Sequence[tuple[float, float]] | None
|
The x windows whose bins set |
None
|
draw_panel ¶
draw_panel(
comparisons: Sequence[Comparison],
ax: Axes,
*,
colors: Sequence[str] | None = None,
observed: Sequence[bool] | None = None,
ylim: tuple[float, float] | None = None,
ylabel: str | None = None,
band: bool | None = None,
view: Sequence[tuple[float, float]] | None = None,
) -> None
Draw comparisons of one kind into the lower panel ax.
Ratios, relative differences, differences and asymmetries are points with error bars around their baseline (1 or 0, a dashed line), over the reference's uncertainty band where there is one; pulls are filled bars from 0, without error bars; a significance is points with error bars and no baseline. A point beyond the y range is marked by a triangle at the edge it left through, in its colour; the markers follow later changes of the range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
comparisons
|
Sequence[Comparison]
|
What :func: |
required |
ax
|
Axes
|
The panel's axes. |
required |
colors
|
Sequence[str] | None
|
One colour per comparison; defaults to the foreground colour for observed data and the style's cycle for the rest. |
None
|
observed
|
Sequence[bool] | None
|
Which comparisons have observed data as numerator; their markers are drawn larger. Defaults to none. |
None
|
ylim
|
tuple[float, float] | None
|
Vertical range; defaults to :func: |
None
|
ylabel
|
str | None
|
Label; defaults to :func: |
None
|
band
|
bool | None
|
Draw the reference's uncertainty band of the first comparison that has
one. Defaults to drawing it when a comparison's error bars leave the
reference out ( |
None
|
view
|
Sequence[tuple[float, float]] | None
|
The x windows whose bins set the automatic y range. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
Without comparisons, or with comparisons of different kinds, references or binnings: one panel shows one kind against one reference, whose label and uncertainty band it draws, so references whose bands differ are refused too. |
draw_hist2d ¶
draw_hist2d(
histogram: Histogram,
ax: Axes,
*,
logz: bool = False,
cmap: str | Any = "viridis",
colorbar: bool = True,
zlabel: str | None = None,
flow: str = "none",
**kwargs: Any,
) -> Any
Draw a 2D histogram as a colour mesh with mplhep.
Empty bins (no content and no variance, i.e. nothing was ever filled there)
are left blank so the colour scale is driven by the populated region; bins
with negative content stay visible on a linear scale. With logz only
positive bins can be shown. Returns the mplhep artists.
draw_correlation ¶
draw_correlation(
matrix: FloatArray,
labels: Sequence[str],
ax: Axes,
*,
cmap: str | Any = "RdBu_r",
annotate: bool = True,
fmt: str | None = None,
colorbar: bool = True,
percent: bool = False,
) -> Any
Draw matrix (values in [-1, 1]) as an annotated heat map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
matrix
|
FloatArray
|
Square correlation matrix. |
required |
labels
|
Sequence[str]
|
Tick labels, one per row/column. |
required |
ax
|
Axes
|
Target axes. |
required |
cmap
|
str | Any
|
Diverging colour map. |
'RdBu_r'
|
annotate
|
bool
|
Write the coefficient into each cell. |
True
|
fmt
|
str | None
|
Format for the annotations. |
None
|
colorbar
|
bool
|
Add a colour bar. |
True
|
percent
|
bool
|
Show coefficients in percent instead of fractions. |
False
|
draw_efficiencies ¶
draw_efficiencies(
efficiencies: Sequence[Efficiency],
ax: Axes,
*,
style: Style,
colors: Sequence[str] | None = None,
) -> tuple[float, float]
Draw each efficiency as points with asymmetric error bars; return the y data range.
draw_profiles ¶
draw_profiles(
profiles: Sequence[Profile],
ax: Axes,
*,
style: Style,
colors: Sequence[str] | None = None,
) -> tuple[float, float]
Draw each profile as points with error bars; return the y data range.
split_stack ¶
split_stack(
histograms: Sequence[Histogram], stack: StackSpec
) -> tuple[
list[Histogram], list[Histogram], list[Histogram]
]
Return stacked, overlaid and observed histograms, each in input order.
Labels select every non-data histogram carrying them. Unknown labels and
labels belonging only to observed data raise ValueError; selectors other
than a bool, a string or a sequence of strings raise TypeError.
make_figure ¶
make_figure(
style: Style,
*,
panel: bool,
ax: AxesLike = None,
figsize: tuple[float, float] | None = None,
break_widths: tuple[float, float] | None = None,
cell: SubplotSpec | None = None,
) -> Layout
Create (or reuse) the figure and axes for a plot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
style
|
Style
|
Provides the default figure size. |
required |
panel
|
bool
|
Add a lower panel below the main panel, sharing the x axis. |
required |
ax
|
AxesLike
|
Existing axes to draw into: one |
None
|
figsize
|
tuple[float, float] | None
|
Figure size in inches; defaults to the style's, enlarged for a lower panel.
Not supported together with |
None
|
break_widths
|
tuple[float, float] | None
|
Relative widths of the left and right segments of a broken x axis.
|
None
|
cell
|
SubplotSpec | None
|
A cell of a grid on an existing figure to build the panels in, so that
several plots share one page (:class: |
None
|
figure_size ¶
figure_size(
style: Style,
*,
panel: bool,
figsize: tuple[float, float] | None = None,
) -> tuple[float, float]
Return the size in inches of a figure drawn on its own under style.
figsize wins, then the style's own size; otherwise the active
figure.figsize, made taller for a lower panel so the main panel keeps
its shape. Read inside the style context, since the rcParams are the style's.
add_legend ¶
add_legend(
ax: Axes, style: Style, *, loc: str | None = None
) -> Legend | None
Add a legend with data entries first, then the other histograms in drawing order.
mplhep already registers stacked histograms top-of-stack first, so the
legend order matches what the eye sees. Returns None when the style
disables legends or nothing is labelled.
add_stats_box ¶
add_stats_box(
ax: Axes,
histograms: Sequence[Histogram],
*,
loc: str = "auto",
precision: int = 4,
include_entries: bool = True,
colors: Sequence[str] | None = None,
legend: Legend | None = None,
) -> list[Text]
Add N, mean and standard deviation for each histogram with statistics.
With loc="auto" the box sits in the upper right corner, directly below
the legend if there is one there (the classic ROOT layout). Any matplotlib
legend location string places it in that corner instead. One block of text
is drawn per histogram. colors supplies one colour per input histogram;
a length mismatch raises ValueError.
add_text ¶
add_text(
ax: Axes,
text: str | Sequence[str],
*,
loc: str = "upper left",
**kwargs: Any,
) -> AnchoredText
Add free text (one string or several lines) anchored inside ax.
style_context ¶
style_context(style: StyleLike = None) -> Iterator[Style]
Temporarily apply style's rcParams; yields the resolved :class:Style.