babelfin
Analysts import babelfin as fin
Babelfin is the front door to UMP for notebooks and scripts. Put a number through it and it reads the same in every tool.
It is a thin layer over ump-spec. Every result keeps the underlying contract object on .spec, so nothing about the standard changes underneath. Services never import it.
Quick path
Section titled “Quick path”- Install:
pip install -e 'packages/babelfin[recipe,plot]'from a checkout (recipeadds finprint,plotadds matplotlib). - Import:
import babelfin as fin. - Build an estimand, then a number. Every number needs an interval.
import babelfin as fin
roi = fin.estimand("roi", channels=["tv"], weeks=104)tv = fin.number(1.37, (1.26, 1.50), roi, label="TV ROI")tv# <Number TV ROI: 1.37 (90% 1.26 to 1.50)>tv.explain() spells out the question the number answers: revenue per dollar, net, from removing all TV spend versus zero spend, over 104 weeks, posterior median, 90% equal-tailed interval.
Main entry points
Section titled “Main entry points”| Call | What it does |
|---|---|
fin.estimand(name, **changes) | Starts from a registered question (roi, mroi, iroas, lift or any registry id) and changes only what differs. |
fin.number(value, (lo, hi), estimand) | A shown number. The interval is required, so a bare point cannot slip through. |
fin.compare(a, b) / fin.total({...}) | Walks the estimand alignment ladder (gate G-EST-02) and sums channels inside the dependence envelope (G-DEP-01). |
fin.check(kind, doc) | Runs the gates on a document offline and returns a report with its fallbacks as a table. |
fin.Workflow(...) | Fits your own model functions, asks named questions and saves the lot as a workflow bundle. |
fin.connect(as_=...) | A session against a running core: file, submit and approve artifacts, browse models, plan and prioritise tests. |
Compare and combine numbers
Section titled “Compare and combine numbers”Two numbers with different estimands answer different questions. Babelfin never averages them silently.
geo = fin.number(0.95, (0.61, 1.30), fin.estimand("iroas", channels=["tv"]), label="TV iROAS, geo test")fin.compare(tv, geo)# <Alignment assumed_alignment; differs on intervention, baseline, horizon, point_rule, interval>Here the two differ only on fields the ladder may assume, so the comparison lands on the assumed-alignment rung and widens the interval to the envelope across plausible alignments. Pass strict=True to raise fin.Stopped instead.
fin.total({"tv": tv, "search": (3.2, 2.7, 3.8)})# <Total 4.57 (3.96 to 5.30, per_channel_envelope)>Without joint draws, the total runs on the independence-to-comonotone envelope. Pass a DataFrame of draws and it uses them.
Rules babelfin keeps
- A vendor number with no interval goes through
fin.imputed_number(value, "roi"), which imputes a band and labels it as imputed. fin.ladder(available)picks the highest rung of the fallback ladder: full model, curve parameters, spend table, summary (warned), benchmarks (warned), not available.- Every result has the same small surface:
.spec,.to_frame()where there is a table,.to_dict(),.explain(), and a card in Jupyter with the headline first and the rigour behind a disclosure.
Check a document before you file it
Section titled “Check a document before you file it”report = fin.check("model_manifest", doc) # at="submit" by default, or "seal"report.okreport.fallbacks.to_frame()report.raise_for_stops()fin.validate(kind, doc) returns the typed model or raises fin.InvalidDocument, whose .to_frame() lists every issue with its pointer.
Save a model as a workflow
Section titled “Save a model as a workflow”c = fin.Contract.from_frame(train, time="week", target="sales", freq="W-SUN")wf = fin.Workflow("Weekly sales", contract=c, code="sales_model.py", seed=11, params={"draws": 200})wf.question("tv_roi", ask="What does a dollar of TV return?", estimand=fin.estimand("roi", channels=["tv"]))
fitted = wf.fit(train)fitted.answer("tv_roi") # a fin.Number with its estimandfitted.save("out/weekly_sales", include_data=False)
lw = fin.Workflow.load("out/weekly_sales") # hashes checked, nothing runlw.code # read it, then trust itlw.trust()run = lw.run(new_df) # scores by defaultrun.route, run.source, run.answers()sales_model.py holds plain functions: fit, to_state, from_state and one function per question. The ump-workflow guide explains the bundle format and when a run refits.
Talk to a running core
with fin.connect(as_="maya.chen") as s: s.check(model) # server dry run; a stop raises fin.Stopped art = s.file(model).submit(reason="Q1 refresh") with s.as_("sam.patel") as sam: # a second person approves, never the author sam.artifact(art.id).approve(accept=art.fallbacks) m = s.models.get(art.id) opt = s.planner.optimize(m, max_change=0.30) # opt.uplift is a fin.NumberApproving requires naming every fallback the artifact used, or the call is refused. Approving your own work raises fin.Forbidden (gate G-INT-02).