API reference¶
Top level¶
rootfig.plot ¶
plot(
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,
observed: Any = None,
xlabel: str | None = None,
ylabel: str | None = None,
unit: str | None = None,
title: str | None = None,
normalize: NormalizeSpec = None,
stack: bool = False,
ratio: RatioSpec = False,
ratio_ylim: tuple[float, float] | None = None,
ratio_label: str | None = None,
ratio_uncertainty: RatioUncertainty | None = None,
logx: bool | None = None,
logy: bool = False,
flow: FlowSpec = "hint",
histtype: HistType | None = None,
errorbars: bool | 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,
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Any
|
What to plot: a file path or glob, |
required |
variable
|
str | Variable
|
Branch name or expression (see :mod: |
required |
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. |
None
|
observed
|
Any
|
A sample of observed data (or the file(s) for one) drawn as points, excluded from stacks and used as numerator of the ratio. |
None
|
xlabel
|
str | None
|
Axis labels; defaults come from the variable, the normalisation and the
bin width ( |
None
|
ylabel
|
str | None
|
Axis labels; defaults come from the variable, the normalisation and the
bin width ( |
None
|
unit
|
str | None
|
Axis labels; defaults come from the variable, the normalisation and the
bin width ( |
None
|
title
|
str | None
|
Axis labels; defaults come from the variable, the normalisation and the
bin width ( |
None
|
normalize
|
NormalizeSpec
|
|
None
|
stack
|
bool
|
Stack the non-data samples. |
False
|
ratio
|
RatioSpec
|
|
False
|
ratio_ylim
|
tuple[float, float] | None
|
Ratio panel range, y label, and uncertainty treatment
( |
None
|
ratio_label
|
tuple[float, float] | None
|
Ratio panel range, y label, and uncertainty treatment
( |
None
|
ratio_uncertainty
|
tuple[float, float] | None
|
Ratio panel range, y label, and uncertainty treatment
( |
None
|
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 non-data samples: |
None
|
errorbars
|
bool | None
|
Draw statistical error bars on non-data histograms. |
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
|
save
|
str | None
|
Path to save the figure to (also returned in the :class: |
None
|
Returns:
| Type | Description |
|---|---|
Plot
|
The figure, axes, histograms and ratios. |
rootfig.plot_histograms ¶
plot_histograms(
hists: Sequence[Histogram | Hist],
*,
variable: Variable | None = None,
labels: Sequence[str] | None = None,
xlabel: str | None = None,
ylabel: str | None = None,
title: str | None = None,
normalize: NormalizeSpec = None,
stack: bool = False,
ratio: RatioSpec = False,
ratio_ylim: tuple[float, float] | None = None,
ratio_label: str | None = None,
ratio_uncertainty: RatioUncertainty | None = None,
logx: bool | None = None,
logy: bool = False,
flow: FlowSpec = "hint",
histtype: HistType | None = None,
errorbars: bool | 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,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
assume_poisson: bool = False,
save: str | None = None,
) -> Plot
Draw already-filled histograms (hist.Hist or :class:~rootfig.histograms.Histogram).
Accepts the same drawing options as :func:plot. Plain hist.Hist
objects are labelled from labels (or numbered) and converted to
Weight storage if they have a plain count storage; mark data by passing
:class:~rootfig.histograms.Histogram objects with is_data=True.
Overlaid histograms may have different binnings; stacks, ratio panels and
flow="show" need identical bin edges.
A count-storage histogram that was filled with weights (or rescaled) has
lost its sum of squared weights and is rejected with a ValueError;
assume_poisson=True draws it anyway with the absolute bin contents as
variances (a warning is issued). Fill with hist.storage.Weight() to keep
the real uncertainties.
Systematic uncertainties come from the histograms' variations
(Histogram(h, label=..., variations={"jes": (h_up, h_down)})) and are
drawn as in :func:plot.
rootfig.plot2d ¶
plot2d(
data: Any,
x: str | Variable,
y: str | Variable,
*,
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",
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.
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,
nonfinite: NonFinitePolicy = "drop",
) -> Hist
Fill a single histogram and return it as a plain hist.Hist.
See :func:plot for the arguments. An integer bins without a range
infers one robustly (range="auto" for the full finite minimum and
maximum).
Examples:
>>> h = rf.histogram(
... "events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=(50, 0, 200)
... )
>>> h.values().sum()
rootfig.histograms ¶
Histogram construction, normalisation, ratios and statistics.
ProfileStatistic
module-attribute
¶
ProfileStatistic: TypeAlias = Literal['mean', 'std']
What a profile shows per bin: the weighted mean of y or its standard deviation.
NormalizeSpec
module-attribute
¶
NormalizeSpec: TypeAlias = (
bool
| Literal["unity", "density", "width"]
| float
| int
| None
)
How to normalise a histogram.
False/None- raw sums of weights.Trueor"unity"- scale so the visible bins sum to one."density"- scale so the integral over the visible range is one (contents divided by bin width and total)."width"- divide each bin by its width (Events / GeV), no rescaling.- a number - scale so the visible bins sum to that number.
SIGNIFICANCE_KINDS
module-attribute
¶
SIGNIFICANCE_KINDS: tuple[str, ...] = (
"significance",
"s/sqrt(b)",
"s/sqrt(s+b)",
)
Strings accepted by ratio= for a significance panel ("significance" means S/sqrt(B)).
SignificanceKind
module-attribute
¶
SignificanceKind: TypeAlias = Literal[
"s/sqrt(b)", "s/sqrt(s+b)"
]
Per-bin significance estimators: S / sqrt(B) or S / sqrt(S + B).
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,
)
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, |
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 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).
replace ¶
replace(**changes: Any) -> Histogram
Return a copy with the given fields changed, e.g. h.replace(label="B").
map_hists ¶
map_hists(transform: Callable[[Hist], Hist]) -> Histogram
Return a copy with transform applied to the nominal histogram and every variation.
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.
Cutflow
dataclass
¶
Cutflow(sample: str, steps: tuple[CutflowStep, ...])
The cut flow of one sample: a sequence of :class:CutflowStep.
efficiencies
property
¶
efficiencies: ndarray
Weighted efficiency of each step relative to the previous one (1 for the first).
The plain ratio of yields: nan where the previous yield is zero. With
signed (NLO) weights a yield 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).
CutflowStep
dataclass
¶
CutflowStep(
label: str,
expression: str,
events: int,
yield_: float,
error: float,
)
Yields after one more cut has been applied.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
The cut's label (or expression). For the first step: the label (or
expression) of the sample's own selection, |
expression |
str
|
The cut expression, empty for the first step. |
events |
int
|
Raw number of events passing all cuts so far. |
yield_ |
float
|
Weighted yield (sum of weights, including the sample scale and luminosity). |
error |
float
|
Statistical uncertainty on |
CutflowTable
dataclass
¶
CutflowTable(rows: tuple[Cutflow, ...])
Cut flows of several samples with the same cuts; str(table) is an aligned text table.
Efficiency
dataclass
¶
Efficiency(
values: FloatArray,
lower: FloatArray,
upper: FloatArray,
edges: FloatArray,
label: str = "",
)
Bin-by-bin efficiency passed / total with a binomial confidence interval.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
The efficiency |
lower, upper |
Bounds of the Wilson score interval ( |
|
edges |
FloatArray
|
Bin edges. |
label |
str
|
Legend label. |
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. |
Ratio
dataclass
¶
Ratio(
values: FloatArray,
errors: FloatArray,
band: FloatArray,
edges: FloatArray,
syst_errors: tuple[FloatArray, FloatArray]
| None = None,
syst_band: tuple[FloatArray, FloatArray] | None = None,
)
Bin-by-bin ratio of two histograms.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
|
errors |
FloatArray
|
Uncertainty on |
band |
FloatArray
|
Relative statistical uncertainty of the denominator, |
edges |
FloatArray
|
Bin edges shared by both histograms. |
syst_errors |
tuple[FloatArray, FloatArray] | None
|
Systematic uncertainty on |
syst_band |
tuple[FloatArray, FloatArray] | None
|
Relative systematic uncertainty of the denominator below and above one,
|
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$).
Uncertainty
dataclass
¶
Uncertainty(
edges: FloatArray,
nominal: FloatArray,
stat: 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 |
FloatArray
|
Statistical uncertainty, |
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.
total_down
property
¶
total_down: FloatArray
Statistical and systematic uncertainty below the nominal, in quadrature.
has_systematics
property
¶
has_systematics: bool
True if at least one systematic source contributes.
as_weight_storage ¶
as_weight_storage(
histogram: Hist, *, assume_poisson: bool = False
) -> Hist
Return histogram with Weight storage (a copy if it had another storage).
Plain count storages (Double, Int64, ...) carry no sum of squared
weights; their variances are taken as hist reports them, i.e. the
counts (Poisson) for unweighted fills. After a weighted fill or arithmetic
on such a storage hist reports no variances at all: the sum of squared
weights is lost and cannot be reconstructed. That is an error unless
assume_poisson=True, which uses the absolute bin contents as variances
(the Poisson guess; a :class:~rootfig.errors.RootfigWarning says so).
Raises:
| Type | Description |
|---|---|
TypeError
|
If the storage is not a count or |
ValueError
|
If the histogram reports no variances and |
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.
profile ¶
profile(
x: FloatArray,
y: FloatArray,
edges: FloatArray,
*,
weights: FloatArray | None = None,
statistic: ProfileStatistic = "mean",
label: str = "",
) -> Profile
Bin x with edges and compute the weighted mean or standard deviation of y.
The variance is computed from the deviations from the bin mean (two passes),
so a narrow spread at a large offset, e.g. std([1e9, 1e9 + 1]) = 0.5, is
not lost to cancellation.
normalization_label ¶
normalization_label(spec: NormalizeSpec) -> str | None
Short description of a normalisation, used as the y-axis label.
normalize_hist ¶
normalize_hist(
histogram: Hist, spec: NormalizeSpec
) -> Hist
Return a normalised copy of histogram with Weight storage.
Flow bins are scaled by the same factor as the visible bins for the
rescaling modes, and divided by the neighbouring visible bin size for the
per-width modes so they stay comparable when drawn. Histograms with a plain
count storage are converted (see :func:~rootfig.histograms.as_weight_storage).
The rescaling modes divide by the signed sum of the visible bins, so the bins sum to the target even when negative weights dominate (the shape then flips sign, with a warning). A histogram whose visible bins sum to zero, because it is empty or because positive and negative weights cancel, is returned unchanged with a warning.
build_histograms ¶
build_histograms(
samples: Sequence[Sample],
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,
) -> list[Histogram]
Fill one 1D histogram per sample 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.
build_histograms_2d ¶
build_histograms_2d(
samples: Sequence[Sample],
x: Variable | str,
y: Variable | str,
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
) -> list[Histogram]
Fill one 2D histogram per sample; x and y must share their structure.
combined_selection ¶
combined_selection(
sample: Sample, selection: CutLike | None
) -> Cut | None
Combine a sample's own selection with a plot-level selection using &.
combined_weight ¶
combined_weight(
sample: Sample, weight: str | None
) -> str | None
Combine a sample's own weight with a plot-level weight multiplicatively.
load_columns ¶
load_columns(
sample: Sample,
variables: Sequence[Variable | str],
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
) -> Columns
Read the required branches of sample and prepare flat columns.
The selection and weight given here are combined with those defined on the
sample itself (see :func:combined_selection and :func:combined_weight).
lumi scales samples that carry a cross section (see
:meth:~rootfig.model.Sample.lumi_scale).
load_columns_each ¶
load_columns_each(
sample: Sample,
variables: Sequence[Variable | str],
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
) -> list[Columns]
Like :func:load_columns once per variable, reading the source only once.
The union of the branches needed by all variables, the selection and the weight is read in a single pass; each variable is then prepared on its own, so variables of different structure (per-event and per-object) may be mixed and each keeps the semantics it would have alone.
read_arrays ¶
read_arrays(
sample: Sample, expressions: Sequence[Any]
) -> tuple[dict[str, Any], int]
Read the branches expressions need from sample and return them with the event count.
Only the union of the required branches is read. The number of events is
taken from the arrays, or from the source when nothing had to be read
(all expressions constant), so "1" or "True" still know how many
events there are.
source_length ¶
source_length(source: Any) -> int
Return the number of events in source (num_entries(), else a branch length).
compatible_binning ¶
compatible_binning(a: Hist, b: Hist) -> bool
Return True if both histograms are one-dimensional with identical edges.
Edges may differ by round-off only: the tolerance is a millionth of the smallest bin width, so bins shifted by a whole width at large coordinates (where NumPy's default relative tolerance would accept them) are rejected.
significance ¶
significance(
signal: Hist,
background: Hist,
*,
kind: SignificanceKind = "s/sqrt(b)",
) -> Ratio
Per-bin significance of signal over background with propagated uncertainties.
Returned as a :class:Ratio (values, errors, edges; band is
nan) so it can be drawn like a ratio panel. Bins with zero background
(or zero total for "s/sqrt(s+b)") are nan.
correlation_matrix ¶
correlation_matrix(columns: Columns) -> FloatArray
Return the (weighted) Pearson correlation matrix of all columns in columns.
Raises:
| Type | Description |
|---|---|
SelectionError
|
If fewer than two columns or fewer than two entries (with non-zero weight) are available, or weights are negative. |
describe_table ¶
describe_table(
summaries: Sequence[tuple[str, Summary]],
precision: int = 4,
) -> str
Format (label, summary) pairs as an aligned plain-text table.
summarize ¶
summarize(columns: Columns, index: int = 0) -> Summary
Compute :class:Summary statistics for column index of columns.
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.
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. |
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,
) -> 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
|
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",
) -> SummaryTable
Compute entries, mean, standard deviation, skewness, ... for variables and samples.
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",
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.
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",
) -> 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 and nonfinite work as in
:func:plot: events with a nan/inf weight are excluded from all
steps with a warning, or raise for nonfinite="error".
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
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,
legend: bool | str | None = None,
text: str | Sequence[str] | None = None,
style: StyleLike = None,
figsize: tuple[float, float] | None = None,
ax: AxesLike = None,
z: float = 1.0,
nonfinite: NonFinitePolicy = "drop",
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
Wilson score intervals (z standard deviations, effective entries for
weighted samples; see :func:rootfig.histograms.efficiency for the
treatment of negative weights). 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).
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,
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",
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.
Examples:
>>> rf.profile(
... "reco.root",
... "true_pt",
... "(reco_pt - true_pt) / true_pt",
... statistic="std",
... bins=(20, 0, 100),
... unit="GeV",
... )
rootfig.significance ¶
significance(
signal: Hist,
background: Hist,
*,
kind: SignificanceKind = "s/sqrt(b)",
) -> Ratio
Per-bin significance of signal over background with propagated uncertainties.
Returned as a :class:Ratio (values, errors, edges; band is
nan) so it can be drawn like a ratio panel. Bins with zero background
(or zero total for "s/sqrt(s+b)") are nan.
rootfig.evaluate ¶
evaluate(
expression: ExpressionLike,
arrays: Mapping[str, Any] | Array,
*,
length: int | 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.
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]
rootfig.ratio ¶
Ratios of histograms with uncertainty propagation.
SignificanceKind
module-attribute
¶
SignificanceKind: TypeAlias = Literal[
"s/sqrt(b)", "s/sqrt(s+b)"
]
Per-bin significance estimators: S / sqrt(B) or S / sqrt(S + B).
SIGNIFICANCE_KINDS
module-attribute
¶
SIGNIFICANCE_KINDS: tuple[str, ...] = (
"significance",
"s/sqrt(b)",
"s/sqrt(s+b)",
)
Strings accepted by ratio= for a significance panel ("significance" means S/sqrt(B)).
Ratio
dataclass
¶
Ratio(
values: FloatArray,
errors: FloatArray,
band: FloatArray,
edges: FloatArray,
syst_errors: tuple[FloatArray, FloatArray]
| None = None,
syst_band: tuple[FloatArray, FloatArray] | None = None,
)
Bin-by-bin ratio of two histograms.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
|
errors |
FloatArray
|
Uncertainty on |
band |
FloatArray
|
Relative statistical uncertainty of the denominator, |
edges |
FloatArray
|
Bin edges shared by both histograms. |
syst_errors |
tuple[FloatArray, FloatArray] | None
|
Systematic uncertainty on |
syst_band |
tuple[FloatArray, FloatArray] | None
|
Relative systematic uncertainty of the denominator below and above one,
|
compatible_binning ¶
compatible_binning(a: Hist, b: Hist) -> bool
Return True if both histograms are one-dimensional with identical edges.
Edges may differ by round-off only: the tolerance is a millionth of the smallest bin width, so bins shifted by a whole width at large coordinates (where NumPy's default relative tolerance would accept them) are rejected.
ratio ¶
ratio(
numerator: Hist | Histogram,
denominator: Hist | Histogram,
*,
uncertainty: RatioUncertainty = "propagate",
) -> Ratio
Compute numerator / denominator bin by bin with uncertainties.
Statistical uncertainties of numerator and denominator are uncorrelated.
Histogram inputs with systematic variations also give
:attr:Ratio.syst_errors and :attr:Ratio.syst_band, propagated source by
source through the varied ratio itself: a source present on both sides varies
numerator and denominator together (a shared luminosity uncertainty cancels),
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 "numerator"
only the numerator's sources enter the error bars; the denominator's are the
band. A variation that empties a denominator bin leaves that bin's
systematic uncertainty nan, with a :class:~rootfig.errors.RootfigWarning.
Raises:
| Type | Description |
|---|---|
BinningError
|
If the histograms do not share the same one-dimensional binning. |
significance ¶
significance(
signal: Hist,
background: Hist,
*,
kind: SignificanceKind = "s/sqrt(b)",
) -> Ratio
Per-bin significance of signal over background with propagated uncertainties.
Returned as a :class:Ratio (values, errors, edges; band is
nan) so it can be drawn like a ratio panel. Bins with zero background
(or zero total for "s/sqrt(s+b)") are nan.
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 used as the numerator of data/MC ratios. |
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.Variable
dataclass
¶
Variable(
expression: str,
bins: Bins = 50,
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
|
Binning specification, see :data: |
50
|
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
|
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.
Results¶
rootfig.Plot
dataclass
¶
Plot(
fig: Figure,
ax: Axes,
ratio_ax: Axes | None = None,
ax_right: Axes | None = None,
ratio_ax_right: Axes | None = None,
histograms: list[Histogram] = list(),
ratios: list[Ratio] = list(),
variable: Variable | None = None,
matrix: FloatArray | None = None,
efficiencies: list[Efficiency] = list(),
profiles: list[Profile] = 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: |
ratio_ax |
Axes | None
|
The ratio panel axes, or |
ax_right, ratio_ax_right |
The right-hand segments when the x axis is broken ( |
|
histograms |
list[Histogram]
|
The :class: |
ratios |
list[Ratio]
|
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 ratio (left, right).
uncertainty ¶
uncertainty(label: str | None = None) -> Uncertainty
Statistical and systematic uncertainties of a histogram of the plot, or of their sum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str | None
|
The label of one histogram. |
None
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If no histogram has |
ValueError
|
If |
BinningError
|
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,
)
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, |
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 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).
replace ¶
replace(**changes: Any) -> Histogram
Return a copy with the given fields changed, e.g. h.replace(label="B").
map_hists ¶
map_hists(transform: Callable[[Hist], Hist]) -> Histogram
Return a copy with transform applied to the nominal histogram and every variation.
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.
rootfig.Ratio
dataclass
¶
Ratio(
values: FloatArray,
errors: FloatArray,
band: FloatArray,
edges: FloatArray,
syst_errors: tuple[FloatArray, FloatArray]
| None = None,
syst_band: tuple[FloatArray, FloatArray] | None = None,
)
Bin-by-bin ratio of two histograms.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
|
errors |
FloatArray
|
Uncertainty on |
band |
FloatArray
|
Relative statistical uncertainty of the denominator, |
edges |
FloatArray
|
Bin edges shared by both histograms. |
syst_errors |
tuple[FloatArray, FloatArray] | None
|
Systematic uncertainty on |
syst_band |
tuple[FloatArray, FloatArray] | None
|
Relative systematic uncertainty of the denominator below and above one,
|
rootfig.Uncertainty
dataclass
¶
Uncertainty(
edges: FloatArray,
nominal: FloatArray,
stat: 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 |
FloatArray
|
Statistical uncertainty, |
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.
total_down
property
¶
total_down: FloatArray
Statistical and systematic uncertainty below the nominal, in quadrature.
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, ...])
The cut flow of one sample: a sequence of :class:CutflowStep.
efficiencies
property
¶
efficiencies: ndarray
Weighted efficiency of each step relative to the previous one (1 for the first).
The plain ratio of yields: nan where the previous yield is zero. With
signed (NLO) weights a yield 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).
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 binomial confidence interval.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
The efficiency |
lower, upper |
Bounds of the Wilson score interval ( |
|
edges |
FloatArray
|
Bin edges. |
label |
str
|
Legend label. |
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, ...).
Lower layers¶
rootfig.io ¶
Reading branch arrays from ROOT files (uproot) or from memory.
FilesLike
module-attribute
¶
FilesLike = (
str | PathLike[str] | Sequence[str | PathLike[str]]
)
A path, glob pattern, URL, "path:tree" string, or a sequence of those.
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
|
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 result is cached on the instance; the file is opened only once.
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.
arrays ¶
arrays(branches: Sequence[str]) -> dict[str, ak.Array]
Read branches from all files and concatenate them.
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.
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;
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.
resolve_files ¶
resolve_files(
files: FilesLike,
) -> tuple[tuple[str, ...], str | None]
Expand files into a sorted tuple of concrete paths and an optional tree name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
files
|
FilesLike
|
A path, glob pattern, remote URL, |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
|
Raises:
| Type | Description |
|---|---|
SourceError
|
If a pattern matches nothing, a local file does not exist, or the embedded tree names disagree. |
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. - Constants
pi,e,inf,nan,True,False.
Everything else (attribute access, lambdas, comprehensions, string literals,
calls to unknown functions) is rejected at parse time with
:class:~rootfig.errors.ExpressionError.
The public entry points are :func:parse, :class:Expression, and
:func:evaluate.
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.
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.
ExpressionLike
module-attribute
¶
ExpressionLike = str | Expression
Anything accepted where an expression is expected.
Expression
dataclass
¶
Expression(
text: str,
names: tuple[str, ...],
functions: tuple[str, ...],
_code: CodeType,
_backticks: Mapping[str, str] = dict(),
)
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. |
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. |
evaluate ¶
evaluate(
expression: ExpressionLike,
arrays: Mapping[str, Any] | Array,
*,
length: int | 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.
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]
parse ¶
parse(expression: ExpressionLike) -> Expression
Parse and validate an expression string.
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, ...). Dotted names such as
|
rootfig.selection ¶
Selection semantics: combine variable, cut and weight arrays into flat columns.
NonFinitePolicy
module-attribute
¶
NonFinitePolicy = Literal['drop', 'error']
What to do with nan/inf values: silently drop them (with a warning) or raise.
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,
)
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). |
effective_weights ¶
effective_weights() -> np.ndarray
Weights as an array, 1.0 everywhere when unweighted.
boolean_mask ¶
boolean_mask(
selection: ExpressionLike,
arrays: Mapping[str, Any] | Array,
*,
length: int | None = None,
) -> ak.Array
Evaluate selection and check that it is boolean; missing values become False.
Fixed-size dimensions are turned into lists (see :mod:rootfig.selection).
Raises:
| Type | Description |
|---|---|
SelectionError
|
If the selection evaluates to numbers rather than booleans, so an integer flag is never mistaken for an index array. |
depth_of ¶
depth_of(array: Array) -> int
Return the list depth of array: 1 for flat, 2 for jagged, and so on.
event_mask ¶
event_mask(
selection: ExpressionLike,
arrays: Mapping[str, Any] | Array,
*,
length: int | None = None,
) -> np.ndarray
Evaluate selection to one boolean per event.
Per-object selections count an event as passing when any of its objects
passes (the cut-flow convention); missing values count as False.
length sizes constant selections when arrays is empty.
event_weights ¶
event_weights(
weight: ExpressionLike | None,
arrays: Mapping[str, Any] | Array,
n_events: int,
*,
nonfinite: NonFinitePolicy = "drop",
context: str = "",
) -> np.ndarray
Evaluate a per-event weight expression (None gives unit weights).
Missing (None) and non-finite weights are returned as nan so the
caller can exclude those events; non-finite ones are reported following
nonfinite (a :class:~rootfig.errors.RootfigWarning, or a
:class:~rootfig.errors.SelectionError for "error").
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,
) -> 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
|
Returns:
| Type | Description |
|---|---|
Columns
|
Flat aligned values and weights. |
same_structure ¶
same_structure(a: Array, b: Array) -> bool
Return True if a and b have identical list lengths at every level.
rootfig.histograms ¶
Histogram construction, normalisation, ratios and statistics.
ProfileStatistic
module-attribute
¶
ProfileStatistic: TypeAlias = Literal['mean', 'std']
What a profile shows per bin: the weighted mean of y or its standard deviation.
NormalizeSpec
module-attribute
¶
NormalizeSpec: TypeAlias = (
bool
| Literal["unity", "density", "width"]
| float
| int
| None
)
How to normalise a histogram.
False/None- raw sums of weights.Trueor"unity"- scale so the visible bins sum to one."density"- scale so the integral over the visible range is one (contents divided by bin width and total)."width"- divide each bin by its width (Events / GeV), no rescaling.- a number - scale so the visible bins sum to that number.
SIGNIFICANCE_KINDS
module-attribute
¶
SIGNIFICANCE_KINDS: tuple[str, ...] = (
"significance",
"s/sqrt(b)",
"s/sqrt(s+b)",
)
Strings accepted by ratio= for a significance panel ("significance" means S/sqrt(B)).
SignificanceKind
module-attribute
¶
SignificanceKind: TypeAlias = Literal[
"s/sqrt(b)", "s/sqrt(s+b)"
]
Per-bin significance estimators: S / sqrt(B) or S / sqrt(S + B).
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,
)
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, |
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 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).
replace ¶
replace(**changes: Any) -> Histogram
Return a copy with the given fields changed, e.g. h.replace(label="B").
map_hists ¶
map_hists(transform: Callable[[Hist], Hist]) -> Histogram
Return a copy with transform applied to the nominal histogram and every variation.
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.
Cutflow
dataclass
¶
Cutflow(sample: str, steps: tuple[CutflowStep, ...])
The cut flow of one sample: a sequence of :class:CutflowStep.
efficiencies
property
¶
efficiencies: ndarray
Weighted efficiency of each step relative to the previous one (1 for the first).
The plain ratio of yields: nan where the previous yield is zero. With
signed (NLO) weights a yield 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).
CutflowStep
dataclass
¶
CutflowStep(
label: str,
expression: str,
events: int,
yield_: float,
error: float,
)
Yields after one more cut has been applied.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
The cut's label (or expression). For the first step: the label (or
expression) of the sample's own selection, |
expression |
str
|
The cut expression, empty for the first step. |
events |
int
|
Raw number of events passing all cuts so far. |
yield_ |
float
|
Weighted yield (sum of weights, including the sample scale and luminosity). |
error |
float
|
Statistical uncertainty on |
CutflowTable
dataclass
¶
CutflowTable(rows: tuple[Cutflow, ...])
Cut flows of several samples with the same cuts; str(table) is an aligned text table.
Efficiency
dataclass
¶
Efficiency(
values: FloatArray,
lower: FloatArray,
upper: FloatArray,
edges: FloatArray,
label: str = "",
)
Bin-by-bin efficiency passed / total with a binomial confidence interval.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
The efficiency |
lower, upper |
Bounds of the Wilson score interval ( |
|
edges |
FloatArray
|
Bin edges. |
label |
str
|
Legend label. |
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. |
Ratio
dataclass
¶
Ratio(
values: FloatArray,
errors: FloatArray,
band: FloatArray,
edges: FloatArray,
syst_errors: tuple[FloatArray, FloatArray]
| None = None,
syst_band: tuple[FloatArray, FloatArray] | None = None,
)
Bin-by-bin ratio of two histograms.
Attributes:
| Name | Type | Description |
|---|---|---|
values |
FloatArray
|
|
errors |
FloatArray
|
Uncertainty on |
band |
FloatArray
|
Relative statistical uncertainty of the denominator, |
edges |
FloatArray
|
Bin edges shared by both histograms. |
syst_errors |
tuple[FloatArray, FloatArray] | None
|
Systematic uncertainty on |
syst_band |
tuple[FloatArray, FloatArray] | None
|
Relative systematic uncertainty of the denominator below and above one,
|
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$).
Uncertainty
dataclass
¶
Uncertainty(
edges: FloatArray,
nominal: FloatArray,
stat: 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 |
FloatArray
|
Statistical uncertainty, |
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.
total_down
property
¶
total_down: FloatArray
Statistical and systematic uncertainty below the nominal, in quadrature.
has_systematics
property
¶
has_systematics: bool
True if at least one systematic source contributes.
as_weight_storage ¶
as_weight_storage(
histogram: Hist, *, assume_poisson: bool = False
) -> Hist
Return histogram with Weight storage (a copy if it had another storage).
Plain count storages (Double, Int64, ...) carry no sum of squared
weights; their variances are taken as hist reports them, i.e. the
counts (Poisson) for unweighted fills. After a weighted fill or arithmetic
on such a storage hist reports no variances at all: the sum of squared
weights is lost and cannot be reconstructed. That is an error unless
assume_poisson=True, which uses the absolute bin contents as variances
(the Poisson guess; a :class:~rootfig.errors.RootfigWarning says so).
Raises:
| Type | Description |
|---|---|
TypeError
|
If the storage is not a count or |
ValueError
|
If the histogram reports no variances and |
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.
profile ¶
profile(
x: FloatArray,
y: FloatArray,
edges: FloatArray,
*,
weights: FloatArray | None = None,
statistic: ProfileStatistic = "mean",
label: str = "",
) -> Profile
Bin x with edges and compute the weighted mean or standard deviation of y.
The variance is computed from the deviations from the bin mean (two passes),
so a narrow spread at a large offset, e.g. std([1e9, 1e9 + 1]) = 0.5, is
not lost to cancellation.
normalization_label ¶
normalization_label(spec: NormalizeSpec) -> str | None
Short description of a normalisation, used as the y-axis label.
normalize_hist ¶
normalize_hist(
histogram: Hist, spec: NormalizeSpec
) -> Hist
Return a normalised copy of histogram with Weight storage.
Flow bins are scaled by the same factor as the visible bins for the
rescaling modes, and divided by the neighbouring visible bin size for the
per-width modes so they stay comparable when drawn. Histograms with a plain
count storage are converted (see :func:~rootfig.histograms.as_weight_storage).
The rescaling modes divide by the signed sum of the visible bins, so the bins sum to the target even when negative weights dominate (the shape then flips sign, with a warning). A histogram whose visible bins sum to zero, because it is empty or because positive and negative weights cancel, is returned unchanged with a warning.
build_histograms ¶
build_histograms(
samples: Sequence[Sample],
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,
) -> list[Histogram]
Fill one 1D histogram per sample 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.
build_histograms_2d ¶
build_histograms_2d(
samples: Sequence[Sample],
x: Variable | str,
y: Variable | str,
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
) -> list[Histogram]
Fill one 2D histogram per sample; x and y must share their structure.
combined_selection ¶
combined_selection(
sample: Sample, selection: CutLike | None
) -> Cut | None
Combine a sample's own selection with a plot-level selection using &.
combined_weight ¶
combined_weight(
sample: Sample, weight: str | None
) -> str | None
Combine a sample's own weight with a plot-level weight multiplicatively.
load_columns ¶
load_columns(
sample: Sample,
variables: Sequence[Variable | str],
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
) -> Columns
Read the required branches of sample and prepare flat columns.
The selection and weight given here are combined with those defined on the
sample itself (see :func:combined_selection and :func:combined_weight).
lumi scales samples that carry a cross section (see
:meth:~rootfig.model.Sample.lumi_scale).
load_columns_each ¶
load_columns_each(
sample: Sample,
variables: Sequence[Variable | str],
*,
selection: CutLike | None = None,
weight: str | None = None,
lumi: float | str | None = None,
nonfinite: NonFinitePolicy = "drop",
) -> list[Columns]
Like :func:load_columns once per variable, reading the source only once.
The union of the branches needed by all variables, the selection and the weight is read in a single pass; each variable is then prepared on its own, so variables of different structure (per-event and per-object) may be mixed and each keeps the semantics it would have alone.
read_arrays ¶
read_arrays(
sample: Sample, expressions: Sequence[Any]
) -> tuple[dict[str, Any], int]
Read the branches expressions need from sample and return them with the event count.
Only the union of the required branches is read. The number of events is
taken from the arrays, or from the source when nothing had to be read
(all expressions constant), so "1" or "True" still know how many
events there are.
source_length ¶
source_length(source: Any) -> int
Return the number of events in source (num_entries(), else a branch length).
compatible_binning ¶
compatible_binning(a: Hist, b: Hist) -> bool
Return True if both histograms are one-dimensional with identical edges.
Edges may differ by round-off only: the tolerance is a millionth of the smallest bin width, so bins shifted by a whole width at large coordinates (where NumPy's default relative tolerance would accept them) are rejected.
significance ¶
significance(
signal: Hist,
background: Hist,
*,
kind: SignificanceKind = "s/sqrt(b)",
) -> Ratio
Per-bin significance of signal over background with propagated uncertainties.
Returned as a :class:Ratio (values, errors, edges; band is
nan) so it can be drawn like a ratio panel. Bins with zero background
(or zero total for "s/sqrt(s+b)") are nan.
correlation_matrix ¶
correlation_matrix(columns: Columns) -> FloatArray
Return the (weighted) Pearson correlation matrix of all columns in columns.
Raises:
| Type | Description |
|---|---|
SelectionError
|
If fewer than two columns or fewer than two entries (with non-zero weight) are available, or weights are negative. |
describe_table ¶
describe_table(
summaries: Sequence[tuple[str, Summary]],
precision: int = 4,
) -> str
Format (label, summary) pairs as an aligned plain-text table.
summarize ¶
summarize(columns: Columns, index: int = 0) -> Summary
Compute :class:Summary statistics for column index of columns.
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.
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. |
rootfig.plotting ¶
Rendering histograms with matplotlib and mplhep.
AxesLike
module-attribute
¶
AxesLike: TypeAlias = (
Axes | tuple[Axes, Axes] | Sequence[Axes] | None
)
Where to draw: nothing (new figure), one axes, or (main, ratio) axes.
DATA_STYLE
module-attribute
¶
DATA_STYLE: dict[str, Any] = {
"marker": "o",
"markersize": 5,
"capsize": 0,
}
Default appearance of data points, drawn in the style's ink colour unless a sample sets one.
FlowSpec
module-attribute
¶
FlowSpec: TypeAlias = Literal["hint", "show", "sum", "none"]
How under/overflow is shown (mplhep flow): small arrows hinting at flow content
("hint"), extra bins ("show"), added to the edge bins ("sum"), or ignored.
DARK_THEME
module-attribute
¶
DARK_THEME: Mapping[str, Any] = {
"figure.facecolor": "none",
"axes.facecolor": "none",
"text.color": _DARK_INK,
"axes.labelcolor": _DARK_INK,
"axes.edgecolor": _DARK_INK,
"xtick.color": _DARK_INK,
"ytick.color": _DARK_INK,
"patch.edgecolor": _DARK_INK,
"hatch.color": _DARK_INK,
"grid.color": "#30363d",
"savefig.facecolor": "auto",
"savefig.edgecolor": "auto",
}
Colours applied by :func:dark_theme: light ink on a transparent background.
DEFAULT_COLORS
module-attribute
¶
DEFAULT_COLORS: tuple[str, ...] = (
*_TAB10,
*_TAB20_LIGHT,
*_TAB20B,
*_TAB20C,
)
Default colour cycle: matplotlib's tab10, extended to 40 colours.
The first ten are the familiar matplotlib defaults, followed by their lighter
tab20 companions and ten colours each from tab20b and tab20c, so
plots with many samples get distinct colours instead of a repeating cycle
(the scheme used by k4Bench).
ROOTFIG_STYLE
module-attribute
¶
ROOTFIG_STYLE: Mapping[str, Any] = {
"figure.figsize": (7.0, 5.6),
"figure.dpi": 100,
"savefig.dpi": 200,
"figure.subplot.left": 0.13,
"figure.subplot.right": 0.97,
"figure.subplot.top": 0.95,
"figure.subplot.bottom": 0.12,
"font.size": 13,
"font.family": "sans-serif",
"font.sans-serif": [
"DejaVu Sans",
"Arial",
"Helvetica",
"Liberation Sans",
],
"mathtext.fontset": "dejavusans",
"axes.labelsize": 15,
"axes.titlesize": 14,
"axes.linewidth": 1.1,
"axes.labelpad": 6,
"axes.formatter.use_mathtext": True,
"axes.formatter.limits": (-3, 4),
"axes.prop_cycle": cycler(color=list(DEFAULT_COLORS)),
"xtick.direction": "in",
"ytick.direction": "in",
"xtick.top": True,
"ytick.right": True,
"xtick.minor.visible": True,
"ytick.minor.visible": True,
"xtick.major.size": 7,
"ytick.major.size": 7,
"xtick.minor.size": 3.5,
"ytick.minor.size": 3.5,
"xtick.major.width": 1.0,
"ytick.major.width": 1.0,
"xtick.labelsize": 13,
"ytick.labelsize": 13,
"legend.frameon": False,
"legend.fontsize": 12,
"legend.handlelength": 1.4,
"legend.borderaxespad": 0.8,
"lines.linewidth": 1.8,
"lines.markersize": 5,
"errorbar.capsize": 0,
"hatch.linewidth": 0.8,
}
Experiment-neutral defaults used when a :class:Style has no base.
Layout
dataclass
¶
Layout(
fig: Figure,
main: Axes,
ratio: Axes | None = None,
main_right: Axes | None = None,
ratio_right: Axes | None = None,
)
The axes of a figure: main panel, optional ratio panel, optional right segments.
With a broken x axis every panel exists twice: main/main_right and
ratio/ratio_right show the left and right segments.
Drawn
dataclass
¶
Drawn(
artists: list[Artist],
labels: list[str],
ymin: float,
ymax: float,
ymin_positive: float,
colors: dict[str, str],
histogram_colors: list[str],
)
What :func:draw_histograms produced.
colors maps legend labels to colours (for the statistics box);
histogram_colors holds the colour of every input histogram in order, so
callers can identify histograms with duplicate labels.
Plot
dataclass
¶
Plot(
fig: Figure,
ax: Axes,
ratio_ax: Axes | None = None,
ax_right: Axes | None = None,
ratio_ax_right: Axes | None = None,
histograms: list[Histogram] = list(),
ratios: list[Ratio] = list(),
variable: Variable | None = None,
matrix: FloatArray | None = None,
efficiencies: list[Efficiency] = list(),
profiles: list[Profile] = 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: |
ratio_ax |
Axes | None
|
The ratio panel axes, or |
ax_right, ratio_ax_right |
The right-hand segments when the x axis is broken ( |
|
histograms |
list[Histogram]
|
The :class: |
ratios |
list[Ratio]
|
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 ratio (left, right).
uncertainty ¶
uncertainty(label: str | None = None) -> Uncertainty
Statistical and systematic uncertainties of a histogram of the plot, or of their sum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str | None
|
The label of one histogram. |
None
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If no histogram has |
ValueError
|
If |
BinningError
|
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: |
{}
|
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: Mapping[str, 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, coloured like the histogram.
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.
correlation_figsize ¶
correlation_figsize(
n_variables: int,
) -> tuple[float, float]
Figure size for a matrix of n_variables, near-square cells included.
The matrix grows with the number of variables, in proportion to the active style's figure width: experiment styles pair larger fonts with larger figures, and the cell annotations and tick labels need that room too.
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
|
apply_xbreak ¶
apply_xbreak(
left: Axes,
right: Axes,
left_range: tuple[float, float],
right_range: tuple[float, float],
*,
mark_size: float = 0.012,
) -> None
Style a pair of axes as the two segments of one broken x axis.
Sets the x limits, removes the facing spines and ticks, and draws diagonal cut marks at the break. The right axes keeps no y tick labels (it shares y with the left one).
break_segments ¶
break_segments(
outer: tuple[float, float],
xbreak: tuple[float, float],
*,
logx: bool = False,
) -> tuple[
tuple[float, float],
tuple[float, float],
tuple[float, float],
]
Split outer at xbreak into two x ranges and their relative widths.
Returns (left_range, right_range, (left_width, right_width)). Widths
are proportional to the spans (in log space for logx).
Raises:
| Type | Description |
|---|---|
ValueError
|
If the break does not satisfy |
finish_axes ¶
finish_axes(
ax: Axes,
*,
data_range: tuple[float, float],
xlabel: str | None,
ylabel: str | None,
xlim: tuple[float, float] | None,
ylim: tuple[float | None, float | None] | None,
logx: bool,
logy: bool,
headroom: float = 1.2,
log_headroom: float = 12.0,
ymin_linear: Literal["zero", "auto"] = "zero",
) -> None
Apply labels, scales and limits, leaving headroom for legends and labels.
headroom is a small fixed margin above the tallest bin: enough to keep the
histogram off the top spine, not enough to reserve space for artists that may
not be there. Room for a legend, an experiment label, a statistics box or a
text line is made afterwards by :func:raise_ylim_above, which measures the
drawn artists and raises the limit only as far as they actually need.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data_range
|
tuple[float, float]
|
|
required |
make_figure ¶
make_figure(
style: Style,
*,
ratio: bool,
ax: AxesLike = None,
figsize: tuple[float, float] | None = None,
break_widths: tuple[float, float] | 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 |
ratio
|
bool
|
Add a ratio 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 ratio panel. |
None
|
break_widths
|
tuple[float, float] | None
|
Relative widths of the left and right segments of a broken x axis.
|
None
|
overlay_artists ¶
overlay_artists(
ax: Axes, legend: Artist | None
) -> list[Artist]
Return the legend, anchored text boxes and axes-relative texts drawn on ax.
These are the things that must not cover the histograms: the legend, the experiment label, the statistics box and free text lines.
raise_ylim_above ¶
raise_ylim_above(
axes: Sequence[Axes],
obstacles: Sequence[Artist],
*,
edges: ndarray,
heights: ndarray,
logy: bool,
floating: Sequence[Artist] = (),
margin: float = 1.08,
) -> None
Raise the upper y limit of axes until obstacles clear the histograms.
heights is the tallest drawn value per bin (see
:func:~rootfig.plotting.hist1d.envelope). obstacles are anchored in axes
coordinates (a legend with a fixed location, the experiment label, text boxes):
they keep their place while the data shrinks beneath them, so for each one the
histogram maximum under its horizontal extent is compared with its lower edge
and, if it would cover the histogram, the shared y range is stretched until
the edge sits margin above the histogram. floating artists (a legend
with loc="best") are relocated by matplotlib at draw time; for those only
the size matters and room is made for them in the upper left or upper right
corner, whichever needs less. Nothing happens if the backend cannot measure
artists.
ylabel_for ¶
ylabel_for(
*,
normalization: str | None,
unit: str | None,
widths: ndarray | None,
per_object: bool,
) -> str
Default y-axis label: Events, Entries / 2 GeV, Normalised to unity, ...
draw_histograms ¶
draw_histograms(
histograms: Sequence[Histogram],
ax: Axes,
*,
style: Style,
stack: bool = False,
histtype: HistType | None = None,
errorbars: bool | None = None,
flow: FlowSpec = "hint",
stack_uncertainty: bool = True,
alpha: 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 legend order. |
required |
ax
|
Axes
|
Target axes. |
required |
style
|
Style
|
Style providing the colour cycle. |
required |
stack
|
bool
|
Stack the non-data histograms (filled). |
False
|
histtype
|
HistType | None
|
Default drawing type for non-data histograms without their own
|
None
|
errorbars
|
bool | None
|
Draw statistical error bars on non-data 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.4 for overlays). |
None
|
envelope ¶
envelope(
histograms: Sequence[Histogram], *, stack: bool
) -> tuple[np.ndarray, np.ndarray]
Bin edges and the highest drawn value (content plus uncertainty) per bin.
Used to keep legends and labels clear of the histograms. The uncertainty includes systematic variations, which are drawn as bands. For stacks the stack total counts; otherwise the maximum over all histograms. Overlaid histograms may have different binnings: the envelope is then evaluated on the union of all edges.
fold_flow_bins ¶
fold_flow_bins(
histograms: Sequence[Histogram],
) -> list[Histogram]
Add the under/overflow of each histogram to its first/last visible bin.
This is mplhep's flow="sum" done once, up front, so that ratios,
significances, stack totals, statistical bands and axis limits are all
computed from the bins that are drawn. Systematic variations are folded the
same way. The flow bins of the returned histograms are empty (values and
variances).
label_flow_bins ¶
label_flow_bins(
ax: Axes, edges: ndarray, *, under: bool, over: bool
) -> None
Label the flow bins made by :func:show_flow_bins <low and >high on ax.
show_flow_bins ¶
show_flow_bins(
histograms: Sequence[Histogram],
) -> tuple[list[Histogram], tuple[bool, bool]]
Turn under/overflow into extra visible bins, consistently for all histograms.
mplhep's flow="show" adds a flow bin to each histogram only when that
histogram has content there, so a stack or overlay mixing histograms with and
without overflow ends up with mismatched bins (a crash for stacks, misaligned
axes otherwise). rootfig therefore materialises the flow bins itself: a bin is
added on a side when any histogram (or systematic variation) has content
there, with mplhep's width convention (the larger of 5 % of the range and the
mean bin width), and every histogram gets the same edges. The flow bins of the
returned histograms are empty.
Returns:
| Type | Description |
|---|---|
(histograms, (underflow_shown, overflow_shown))
|
|
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_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.
draw_ratio_panel ¶
draw_ratio_panel(
numerators: Sequence[Histogram],
reference: Histogram,
ax: Axes,
*,
style: Style,
uncertainty: RatioUncertainty
| Sequence[RatioUncertainty],
colors: Sequence[str] | None = None,
ylim: tuple[float, float] | None = None,
ylabel: str | None = None,
band: bool | None = None,
) -> list[Ratio]
Draw numerator / reference for every numerator and return the ratios.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
numerators
|
Sequence[Histogram]
|
Histograms to divide by |
required |
reference
|
Histogram
|
The denominator. |
required |
ax
|
Axes
|
The ratio axes. |
required |
style
|
Style
|
Style providing colours. |
required |
uncertainty
|
RatioUncertainty | Sequence[RatioUncertainty]
|
|
required |
colors
|
Sequence[str] | None
|
One colour per numerator; defaults to the numerator's own colour or the
style cycle ( |
None
|
ylim
|
tuple[float, float] | None
|
Vertical range; defaults to :data: |
None
|
ylabel
|
str | None
|
Label; defaults to |
None
|
band
|
bool | None
|
Draw the reference uncertainty band. Defaults to |
None
|
draw_significance_panel ¶
draw_significance_panel(
result: Ratio,
ax: Axes,
*,
kind: SignificanceKind = "s/sqrt(b)",
color: str | None = None,
ylim: tuple[float, float] | None = None,
ylabel: str | None = None,
) -> None
Draw a per-bin significance (from :func:~rootfig.histograms.significance) as points.
ratio_ylim ¶
ratio_ylim(
ratios: Sequence[Ratio],
*,
band: tuple[ndarray, ndarray] | None = None,
) -> tuple[float, float]
Choose a ratio range: at least (0.5, 1.5), widened to cover the bulk of what is drawn.
The bulk is the 5th to 95th percentile, padded by 10 percent, of the finite
ratio values, of the systematic extent of the points and of the edges of the
reference band when one is drawn. Each is judged on its own, so it can
widen the range but never narrow it. Statistical error bars do not count: a
few low-statistics bins with huge uncertainties would otherwise squash the
panel. The result is clipped to [0, 3], or to [-3, 3] with negative
ratios (signed weights), whose lowest value then stays in view.
add_experiment_label ¶
add_experiment_label(
ax: Axes,
style: Style,
*,
has_data: bool,
above: bool = False,
right: Axes | None = None,
) -> None
Draw the experiment label and/or free text described by style on ax.
above=True puts an experiment label above the frame unless
style.label_loc is set, for plots whose data fill the frame (2D
histograms, matrices) and so leave no room for a label inside it. right
is the right segment of a broken x axis: the luminosity text that belongs
above the right end of the frame is placed above that segment's end.
align_experiment_label ¶
align_experiment_label(
ax: Axes, *, right: Axes | None = None
) -> None
Anchor mplhep's experiment label to the finished figure.
mplhep positions the status word ("Simulation", "Internal", ...) as an axes
fraction computed when the label is drawn, and shifts a label above the axes
right by the width of the y axis' scientific-notation offset text measured at
that moment. Both go stale once the axes are resized (constrained layout) or
the y scale changes (a log axis has no offset text). This puts a label above
the axes flush with the frame unless an offset text is really shown, fits the
line above the frame to its width (see :func:_fit_label_line), lifts the
title above the label texts over the frame (see :func:_clear_title), and
places the status word a fixed gap after the experiment name with a point-based
offset, so it stays put at any axes size.
Call it on the finished figure once its style context has ended, as rootfig's
plotting functions do: tick labels and colour bars are laid out when the figure
is drawn, from the rcParams active then, so only that layout is the one the
figure is shown with. Fitting the label changes the layout in turn, so the
figure is drawn and measured again, up to :data:LABEL_FIT_PASSES times, until
nothing changes. Sizes follow from the texts' original sizes, so aligning again
leaves the label as it is. right is the right segment of a broken x axis,
which carries the luminosity text (see :func:add_experiment_label). Does
nothing when ax carries no mplhep label.
color_cycle ¶
color_cycle(
n: int, style: Style | None = None
) -> list[str]
Return n colours from the style's cycle (repeating if necessary).
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.
finalize_figure ¶
finalize_figure(
fig: Figure, *, panels: Sequence[Axes] = ()
) -> None
Fix what the figure's look depends on while its style context is still active.
Call as the last drawing step inside the style context: pins the fonts (see
:func:pin_fonts) and fits the y label of each lower panel in panels (see
:func:~rootfig.plotting.figure.fit_ylabel), which measures text and so must
follow the font pinning that decides which font is drawn. The experiment label
is anchored afterwards, outside the context (see :func:align_experiment_label).
foreground ¶
foreground() -> str
Return the ink colour of the active style (text.color) for points and outlines.
A colour name or hex string is returned as set; an RGB(A) tuple, which the rcParam also accepts, is converted to hex so it stays usable as a string.
legend_location ¶
legend_location(style: Style) -> str | None
Return the legend location string for style or None to skip the legend.
resolve_rc ¶
resolve_rc(style: Style) -> list[Mapping[str, Any] | str]
Return the list of style sheets to apply for style (later entries win).
style_context ¶
style_context(style: StyleLike = None) -> Iterator[Style]
Temporarily apply style's rcParams; yields the resolved :class:Style.
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.