Guide · writing the question down
Dimensions, units and scope
Physics students learn early that you cannot add a length to a mass, and that checking dimensions catches most algebra errors before they cost anything. Statistical software almost universally does not do this — a dataframe column is a column of floats, and floats add.
Three things, kept separate on purpose
They all look like a units problem. Only one is.
Dimension
What kind of thing this is: currency, time, an outcome count, a number of entities. There is no conversion factor between different dimensions, at any exchange rate, ever.
A mismatch always raises. There is no flag to disable it, because the operation has no meaning to permit.
declared bases on this build:
area, count, currency, entity, exposure_count, mass, outcome, pressure, time, volume
Unit
Which measure of that dimension: USD or EUR, days or weeks. Two units of the same dimension may be interconvertible — if somebody registered the factor.
A mismatch raises unless a conversion is registered, and when one is, the conversion writes a ledger line recording itself.
axiom ships no conversions; a domain declares the ones it needs
Scope
Which population, which window, which level of aggregation. The number is in the right units and means something else anyway.
Not an arithmetic problem at all. No factor fixes it; it is settled by a transfer plan and a named assumption, which is the subject of transportability.
handled by
Estimand.transfer_to, not by units
Why the third one is the dangerous one
A dimension error stops the program. A unit error stops the program. A scope error produces a number in perfectly correct units that answers a question nobody asked — and it is the only one of the three that reaches a slide.
What it looks like
Three refusals from this build
Each of these is a real exception, raised while this page was being generated.
estimand 'mismatched': declared dimension $ but a contrast of Y over N derives to Y raises
USD to
day:cannot convert 'USD' [currency] to 'day' [time]: different dimensions raises
day to
month — the same dimension, but
nobody said what a month is:unit 'month' is not declared in the unit system raises
The last one is the one that feels excessive and is not. Is a month thirty days, or a twelfth of a year, or the actual calendar month this row falls in? Those give different answers, the difference compounds over a long window, and the library has no business picking for you. Registering the factor takes one line and makes the choice visible to the next reader.
When it does convert
A conversion is a fact, and it still writes a line
Declared, then converted
unit_conversion —
“converted week to day by factor 7”
The ledger line carries no assumption:
assumption is None, because seven days in a week is a fact somebody
registered, not a claim that might fail. That distinction is load-bearing. A
ledger where facts and assumptions look alike is a ledger nobody reads, and the
whole point of the paper trail is that its
unverified lines stand out.
Contrast that with a scope correction — “the twelve-week window caught 81% of the
effect” — which does carry an assumption, names what would challenge it,
and stays unverified until something checks it.
Why this earns its keep
The bug class it removes
Cost quietly added to effect
A cost-per-unit and an effect-per-unit are both floats, and a helper four layers down adds them. The result is a number in no dimension at all, and it propagates to a decision. With dimensions declared, this raises at the boundary.
Weekly and daily series joined on index
Two panels, both indexed 0…N, one weekly and one daily. Nothing complains; the effect comes out seven times too small. A declared time unit makes the join a conversion that either has a registered factor or refuses.
A standard deviation treated as a variance
The reason dimension exponents are fractions rather than integers: a standard deviation is the square root of a variance, so it is genuinely a different dimension, and mixing them is catchable rather than a matter of naming discipline.