Limitations and gotchas¶
What doesn’t work yet, and what to do instead.
Growth axes¶
Regular, Integer, IntCategory and StrCategory axes grow (Growth axes).
Two things are missing:
A growing
Variableaxis raisesTypeErrorwhen you construct the histogram: its new edges come from the data, so two partitions’ axes share no grid to merge on. Declare the edges up front.Several fills into one growth-category histogram (two
fillcalls, or an axis-mode fill whose values vary) list their categories partition by partition, not fill by fill as one eager fill per call would. Declare the categories up front if you need fill order.
No .compute(), persist, or to_delayed¶
Deferred histograms are not dask collections, so the dask collection protocols don’t apply.
You build a plan — h.plan(), or gh.plan({"pt": h1, "mass": h2}) to run several
histograms in one pass — and hand it to a runner, which returns concrete boost histograms.
See How graphed-histogram works for the full path from fill to result.
Float storages and reproducibility¶
On fixed axes, integer count storages (Int64) give bit-identical totals however the run is
split; on growth axes, see the edge and non-finite exceptions in Growth axes. Inexact
float sums in any storage, and every Mean and WeightedMean field, depend on order:
each runner fixes its combine tree from the runner family and the partition count, so
re-running reproduces your totals exactly, and so does changing the worker count — but
changing the runner family or the partitioning can change the last bits.
For bit-for-bit comparisons between runs, keep the runner configuration fixed, or compare an
Int64 count alongside the weighted result.
One partitioned source per plan¶
A plan reads one dataset: every fill in it must record into the same session, and that session must have exactly one partitioned source. Fills drawing on two different datasets cannot be planned together.
Run one plan per dataset and add the results
(gh.add_histograms(results_2017["pt"], results_2018["pt"])).
Backend behaviors are not carried to workers¶
Each worker builds its own array backend, and by default a bare one of the same class. Coffea-style behaviors attached to your record types live in a dictionary of Python functions that does not pickle, so nothing recovers them from the recording session automatically.
Pass backend="my_analysis:make_backend" to plan() — an importable zero-argument callable
resolved in the worker. A worker without the behaviors an analysis asks for raises rather than
filling the wrong thing.