Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8a10da3
Add HistRebinTask, limit plotting and meta-era support
aebid Aug 13, 2026
6c98607
Split law/tasks.py into one module per task
aebid Aug 27, 2026
96dd5c7
Name the binning knobs after the transformation, not the analysis
aebid Aug 27, 2026
18d7729
Take the 2D->1D binning out of the law chain
aebid Aug 27, 2026
1d79693
Remove defer_negative_bin_check by splitting getShape
aebid Aug 27, 2026
d57a034
Fix rebin_2d reading the sliced category names it writes
aebid Aug 28, 2026
8723cbf
Plot the rebinned shapes with FLAF's HistPlotter instead of our own
aebid Aug 28, 2026
7bb81a5
Require PlotResonantLimitsTask from ResonantLimitsAndHistPlotTask
aebid Aug 28, 2026
72ed428
Rebin each era of the configuration on its own statistics
aebid Aug 28, 2026
c6251fd
Lay the shape plots out as one grid per base category
aebid Aug 28, 2026
05bfdd7
Read the rebinned shapes from Hists_rebinned, not Hists_merged
aebid Aug 28, 2026
3c7d0af
Write binning.json inside the era it belongs to
aebid Aug 28, 2026
dbc9a1e
Make the binning the product, and apply it at datacard time
aebid Aug 28, 2026
24e9bf9
Free the 2D inputs after slicing, and follow the configuration's eras
aebid Aug 28, 2026
ad1d3be
Add PreprocessShapesTask, a configurable step before the datacards
aebid Aug 28, 2026
d3da325
Let a meta-era inherit its sub-eras' shape uncertainties
aebid Aug 28, 2026
7aae46c
Pass the datacard config to the preprocessing step
aebid Aug 28, 2026
9c39616
Depend on ResonantLimitsTask again, not PlotResonantLimitsTask
aebid Aug 28, 2026
ebcb813
Check the uncertainty class, not its type enum
aebid Aug 28, 2026
6521ffb
Drop call_rebin_2d.sh
aebid Aug 28, 2026
19369c1
Give a meta-era the shape treatment only for era-dependent lnN
aebid Aug 29, 2026
43a4131
Add PlotPullsAndImpactsTask, so the impacts land on EOS
aebid Aug 29, 2026
8990dc4
Say when a configured uncertainty reached no bin
aebid Aug 29, 2026
0829697
Format PlotPullsAndImpactsTask with black
aebid Aug 29, 2026
2414124
Let an impact_plots entry choose its datacard
aebid Aug 29, 2026
dd10fdd
Let an entry pass arguments through to the dhi task it depends on
aebid Aug 29, 2026
927348e
Build the impacts Asimov at the limit, not at r=1
aebid Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
# StatInference

## Two ways bins get decided

Two independent things in this repository choose the binning of the shapes that go into
the datacards. They do not share code, and an analysis uses one or the other. **Neither is
part of the datacard chain**: both are offline pre-steps that produce input the chain then
consumes, so `CreateDatacardsTask` reads whatever shapes it is pointed at and does no
binning of its own.

**1. Shape-driven 2D→1D binning — `bin_opt_2d/rebin_2d.py`.** Derives bin edges from the
shapes themselves (no fits, no limits): it cuts the x axis of a 2D input into slices that
become datacard categories, and rebins y into the bins of each slice. It writes the
rebinned shapes as `<output>/<source era>/<variable>/<variable>.root`, plus the
`binning.json` recording the edges it chose, beside them.

It runs as the datacard configuration's **preprocessing step**, not as part of this
repository's logic. `PreprocessShapesTask` runs whatever `preprocess:` names, supplying
`--input`, `--output`, `--era` and `--config` (the datacard configuration); nothing here
knows what the step does, and a
configuration that declares no `preprocess:` block skips the task entirely and reads the
merged histograms as they are:

```yaml
preprocess:
script: StatInference/bin_opt_2d/rebin_2d.py
args: [--binning-config, config/Datacards/binning_2d.yaml]
```

The knobs live with the analysis (`config/Datacards/binning_2d.yaml`) because they are
analysis configuration; the derived `binning.json` lives with the shapes it produced,
because it is a product of the run rather than an input to it.

Which era is being produced decides how it is derived. A plain era is binned on its own
statistics, for a standalone limit; an era that is a key of `era_groups:` is binned on its
members' summed statistics, and those edges are then applied to each member separately --
kept in their own files, because the datacard step sums them and a per-era lnN can only be
built by scaling a sub-era's own shape.

Each base category (`SR/res2b`) becomes per-slice categories named by the `category_pattern`
knob, e.g. `{base_category}_dnn{slice_idx}`. The pattern is the analysis's choice — nothing
here assumes the sliced axis is a DNN score. `common/tools.py:CategoryNaming` both writes
those names and parses them back from that one pattern.

**2. Limit-driven binning optimisation — `bin_opt/`.** A search harness, documented below:
it builds candidate binnings, runs limits with combine for each, and ranks them. Its product
is a `hist_bins` JSON, applied at datacard time by `dc_make/binner.py`. Every module is a
script driven by `bin_opt/bin_optimization.yaml`, and it exposes no importable API.

Which one an analysis is on is visible in its configuration: the `bin_opt` path sets
`hist_bins` (as `config/x_hh_bbtautau_run2.yaml` does), while an analysis reading rebinned
shapes leaves `hist_bins` unset and instead lists the sliced `categories:` and the
`category_pattern` that names them (as HH→bbWW's `config/Datacards/x_hh_bbww_DL_run3.yaml`
does). An analysis using neither simply lists the categories its input already has.

`categories:` is always taken verbatim — it is the set of directories that exist in the
input shapes, and nothing in `dc_make` derives or expands it.

## How to run binning optimisation on lxplus
Open two separate LXPLUS terminals: one for **server side** and the other for **worker side**.
Set up the environment and proxy in analysis area as usual on both terminals.
Expand Down
Loading
Loading