ump_spec.models.estimand
module ump-spec
Seven-field estimand (spec §5) and its scope (applies_to).
Every exchanged number carries one. Wire rules (spec §5, “Undeclared values”):
- A field the source did not declare is written as the string
"undeclared". In Python it reads asNone; every dump writes"undeclared"for the seven fields and for a required sub-field (unit.per,unit.currencywithper: spend,intervention.size_kindwith asize,baseline_xwithreduced_spend), so the absent and explicit forms hash the same. - JSON null is invalid anywhere in the estimand, on input. Optional members that are unset are left out of every dump.
- Nothing has a default: no
per,currency,schema_versionorsize_kindis filled in. - A member that is not one of the seven fields, their sub-fields or a registered optional member is
rejected with
ESTIMAND_UNKNOWN_FIELD(pydantic error typeestimand_unknown_field).
G-EST-01 fills an undeclared field from the source-type default
(ump_spec.rules.estimands.fill_undeclared), marks it assumed in field_status and widens
the interval. normalise_legacy_estimand reads docs written before this contract (<ccy>_spend,
nulls, weeks: null, the old defaults); it is read compatibility for one cycle and never a writer.
Classes
Section titled “Classes”Intervention
Section titled “Intervention”class
class Intervention(_EstimandPart)Fields
| Name | Type | Default | Description |
|---|---|---|---|
type | InterventionType | ||
size | float | None | None | e.g. -0.30 for a 30% cut, 0.10 for +10%. |
size_kind | SizeKind | None | None | How to read size: relative (a fraction of current spend) or absolute (spend in unit.currency). Required when size is present; no default (spec §5). |
arms | list[str] | list() | |
population | Population | None | None |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 163
Horizon
Section titled “Horizon”class
class Horizon(_EstimandPart)Time over which the effect is counted. {type: window, anchor: fit} is the model’s fit window:
a template written before any fit (an adapter’s metadata) leaves weeks absent, and every
quantity a fit produces states weeks (spec §5, “Fit-window horizon”). weeks is never null.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
type | HorizonType | ||
weeks | int | None | None | Weeks counted. Absent only on a fit-window template. |
threshold | float | None | None | Decay threshold for total_to_decay. |
anchor | Literal['fit'] | None | None | Spec §5: fit means the window is the model’s fit window. Absent means the window counts from the start of the intervention. |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 179
class
class Unit(_EstimandPart)per is spend, impression or absolute. currency (ISO 4217) is required with
per: spend and allowed with per: absolute (a money KPI); there its absence means the KPI is
not money. Build a spend unit with spend_unit(currency).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
kpi | str | ||
per | Per | None | None | Denominator: spend, impression or absolute (spec §5). |
cost_basis | CostBasis | None | None | |
currency | str | None | None | ISO 4217 alpha-3; required with per: spend. |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 195
PointRuleSpec
Section titled “PointRuleSpec”class
class PointRuleSpec(_EstimandPart){rule, scale} form of point_rule (spec §5): scale is identity or log.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
rule | PointRule | ||
scale | PointScale |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 221
IntervalSpec
Section titled “IntervalSpec”class
class IntervalSpec(_EstimandPart)Fields
| Name | Type | Default | Description |
|---|---|---|---|
method | IntervalMethod | ||
level | float |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 230
Period
Section titled “Period”class
class Period(_EstimandPart)Calendar dates, half-open [start, end) (spec §4).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
start | date | None | None | |
end | date | None | None |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 237
AppliesTo
Section titled “AppliesTo”class
class AppliesTo(_EstimandPart)Scope beside the estimand: compared by G-EST-02 like the seven fields.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
media_ids | list[str] | list() | |
geo_ids | list[str] | list() | |
kpi | str | None | None | |
period | Period | None | None | |
time_id | str | None | None | Spec §4 time registry entry ID. |
client_id | str | None | None |
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 246
Estimand
Section titled “Estimand”class
class Estimand(_EstimandPart)The seven fields plus applies_to and the registered optional members. None (written
"undeclared") means undeclared.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
intervention | Intervention | None | None | |
baseline | BaselineKind | None | None | |
baseline_x | float | None | None | x of reduced_spend(x): spend at x times the current plan. Required with baseline reduced_spend; ignored otherwise (spec §5). |
horizon | Horizon | None | None | |
unit | Unit | None | None | |
point_rule | PointRule | PointRuleSpec | None | None | |
interval | IntervalSpec | None | None | |
schema_version | str | None | None | |
applies_to | AppliesTo | AppliesTo() | |
registry_id | str | None | None | Registered estimand id, e.g. E-BM-AVG-ROI. |
metric_family | str | None | None | sales_return, brand_awareness, … |
field_status | dict[str, FieldStatus] | dict() |
Methods and properties (3)
missing_fields method
def missing_fields(self) -> list[str]Undeclared fields, then undeclared required sub-fields as dotted paths (unit.currency,
unit.per, intervention.size_kind, baseline.baseline_x), in spec order.
assumed_fields method
def assumed_fields(self) -> list[str]is_complete method
def is_complete(self) -> boolSource: packages/ump-spec/src/ump_spec/models/estimand.py, line 287
Functions
Section titled “Functions”omit_when_none
Section titled “omit_when_none”function
def omit_when_none(value: Any) -> boolexclude_if hook for members added after a shape was first published: an unset member is
left out of every dump, so existing outputs and content hashes stay byte-identical.
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 75
spend_unit
Section titled “spend_unit”function
def spend_unit( currency: str, kpi: str | None = None, *, cost_basis: CostBasis | None = None,) -> dict[str, Any]Members of a per-spend unit (spec §5): {"per": "spend", "currency": "USD", ...}. currency
is an ISO 4217 code; lower case is upper-cased. Splat it into a unit dict or Unit(**...).
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 366
normalise_legacy_unit
Section titled “normalise_legacy_unit”function
def normalise_legacy_unit(unit: dict[str, Any]) -> dict[str, Any]Read compatibility for one cycle: per: "<ccy>_spend" becomes per: spend plus
currency: "CCY", and null members are dropped. Other values pass through unchanged (a
per-thousand unit such as usd_k_spend is rescaled by its adapter, never here).
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 381
normalise_legacy_estimand
Section titled “normalise_legacy_estimand”function
def normalise_legacy_estimand(raw: dict[str, Any]) -> dict[str, Any]Read a stored estimand written before the spec §5 wire rules (fixtures, old docs; never a writer).
It applies the defaults the old contract filled in (schema_version est-registry/1.2.0,
size_kind: relative, per: usd_spend with currency: USD), rewrites <ccy>_spend,
turns weeks: null into the fit-window anchor and drops every other null.
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 393
without_unset
Section titled “without_unset”function
def without_unset(members: Any) -> AnyA producer’s own estimand dict with every None member dropped, at any depth, so optional
state that is not known (no client id, no period) is left out instead of written as null. Use it
only on dicts the producer builds; received input is validated as sent, where null is refused.
Source: packages/ump-spec/src/ump_spec/models/estimand.py, line 442
Constants
Section titled “Constants”| Name | Type | Value | Description |
|---|---|---|---|
ESTIMAND_FIELDS | tuple[str, ...] | ('intervention', 'baseline', 'horizon', 'unit', 'point_rule', 'interval', 'sc... | The seven estimand fields, in spec order. |
UNDECLARED | 'undeclared' | The wire word for a field the source did not declare (spec §5). | |
InterventionType | Literal['remove_all', 'scale', 'add', 'marginal'] | ||
Population | Literal['itt', 'exposed', 'geo_set'] | ||
BaselineKind | Literal['zero_spend', 'current_plan', 'reduced_spend', 'control_counterfactual'] | ||
HorizonType | Literal['immediate', 'window', 'total_to_decay', 'total'] | ||
PointRule | Literal['posterior_mean', 'posterior_median', 'map', 'estimator_point'] | ||
PointScale | Literal['identity', 'log'] | ||
IntervalMethod | Literal['hdi', 'eti', 'confidence', 'bootstrap_pct', 'placebo', 'envelope', '... | ||
CostBasis | Literal['net', 'gross'] | ||
FieldStatus | Literal['declared', 'undeclared', 'assumed'] | ||
Per | Literal['spend', 'impression', 'absolute'] | ||
SizeKind | Literal['absolute', 'relative'] | ||
PER_VALUES | tuple[str, ...] | ('spend', 'impression', 'absolute') | unit.per values (spec §5). |
CURRENCY_RE | '^[A-Z]{3}$' | ISO 4217 alpha-3 code (spec §5 unit.currency). |