You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+9-1Lines changed: 9 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,14 @@
4
4
5
5
-**Breaking:** the previously-inert `predicted_outcome_columns` parameter (`Sample.from_frame` / `SampleFrame.from_frame` / `SampleFrame._create`) and the `SampleFrame.predicted_outcome_columns` property are renamed to `outcomes_hat_columns` (internal `_column_roles` key `"predicted"` → `"outcomes_hat"`). The old names are removed outright with no alias — the role was reserved scaffolding, never populated or consumed, so no migration is provided.
6
6
7
+
-**Breaking:**`BalanceFrame.aipw()` now requires responder weights produced by
8
+
`adjust()`, validates both weight vectors as non-empty, one-dimensional,
9
+
finite, and non-negative with positive finite totals, and verifies that the
10
+
responder total matches the target-weight total to a relative tolerance of
11
+
`1e-6`. This enforces the estimator's same-population-scale normalization
12
+
contract instead of accepting arbitrary, uncalibrated weights; call
13
+
`adjust(...)` before `aipw()` and do not rescale its output.
14
+
7
15
## New Features
8
16
9
17
-**Outcome-model input and replay validation is hardened.**`fit_outcome_model(..., sample_weight=...)` rejects incorrectly shaped arrays and zero, negative, NaN, infinite, non-real, or non-numeric weights with actionable `ValueError` messages; accepted weights must be one-dimensional, finite, strictly positive real numbers aligned to the covariates. Replaying categorical covariates now explicitly maps novel levels to missing before constructing the frozen categorical dtype, avoiding the pandas deprecation warning while preserving the existing novel-level behavior.
@@ -74,7 +82,7 @@
74
82
scored.outcomes_hat().mean() # μ̂_OM on the holdout target via train_bf's fitted model
75
83
```
76
84
77
-
- **`BalanceFrame.aipw()` — doubly-robust (AIPW) estimate `μ̂_DR`.** New `BalanceFrame.aipw()` (and, via the MRO, `Sample.aipw()`) returns the augmented / one-sample AIPW estimate of the target-population mean, per outcome column, combining the fitted outcome model `ĝ` with the balance weights `w`: `μ̂_DR = wmean(ĝ(X_T), w_T) + wmean(Y − ĝ(X_S), w)` (the augmentation runs over responders with an observed `Y`). It is **doubly robust** — consistent if *either* the outcome model *or* the weighting model is correct — and completes the estimator trio alongside `outcomes().mean()` (`μ̂_IPW`) and `outcomes_hat().mean()` (`μ̂_OM`); equivalently it is a GREG (model-assisted) estimator with the balance weights as the design weights. It requires a fitted outcome model (`fit_outcome_model(...)`) **and** a target (`set_target(...)`), accepts any balance weights, and **warns** when the responder weights are constant (no weighting fitted → `μ̂_DR` reduces to `μ̂_OM`). The point-estimate arithmetic lives in the pure `balance.outcome_models.aipw_point_estimate(...)`. **This is the point estimate only** — no confidence interval yet; an honest AIPW interval must jointly capture the weighting- and outcome-model uncertainty (see the TODOs in `balance/outcome_models/aipw.py`: cross-fitting, an analytic influence-function / sandwich SE, and an end-to-end joint bootstrap).
85
+
- **`BalanceFrame.aipw()` — doubly-robust (AIPW) estimate `μ̂_DR`.** New `BalanceFrame.aipw()` (and, via the MRO, `Sample.aipw()`) returns the augmented / one-sample AIPW estimate of the target-population mean, per outcome column, combining the fitted outcome model `ĝ` with the balance weights `w`: `μ̂_DR = wmean(ĝ(X_T), w_T) + wmean(Y − ĝ(X_S), w)` (the augmentation runs over responders with an observed `Y`). It is **doubly robust** — consistent if *either* the outcome model *or* the weighting model is correct — and completes the estimator trio alongside `outcomes().mean()` (`μ̂_IPW`) and `outcomes_hat().mean()` (`μ̂_OM`); equivalently it is a GREG (model-assisted) estimator with the balance weights as the design weights. It requires a fitted outcome model (`fit_outcome_model(...)`), a target (`set_target(...)`), and `adjust()`-calibrated responder weights whose total matches the target-weight total within relative tolerance `1e-6`; this same-population-scale requirement makes the doubly-robust claim asymptotic under the Hájek normalization used by balance (ratio bias is `O(1/n)`). It **warns** when the calibrated responder weights are constant (`μ̂_DR` reduces to `μ̂_OM`). The point-estimate arithmetic lives in the pure `balance.outcome_models.aipw_point_estimate(...)`. **This is the point estimate only** — no confidence interval yet; an honest AIPW interval must jointly capture the weighting- and outcome-model uncertainty (see the TODOs in `balance/outcome_models/aipw.py`: cross-fitting, an analytic influence-function / sandwich SE, and an end-to-end joint bootstrap).
78
86
79
87
```python
80
88
bf = sample.adjust(method="ipw").set_target(target) # or any balance weights
Copy file name to clipboardExpand all lines: docs/architecture/architecture_0_23_0.md
+6-5Lines changed: 6 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,6 +18,7 @@
18
18
19
19
| Date | Decision | Rationale |
20
20
|------|----------|-----------|
21
+
| 2026-08-01 |**AIPW normalization contract: implemented.** The augmentation is valid only when `w_R` and `w_T` are on the same population scale; Hájek self-normalization gives *asymptotic* DR (ratio bias `O(1/n)`). `aipw()` requires valid, non-negative `adjust()`-calibrated responder weights and asserts `|Σw_R−Σw_T|/Σw_T < 1e-6`. | Enforces the correctness precondition at the estimator boundary and reports an actionable error for malformed, arbitrary, or subsequently rescaled weights. |
21
22
| 2026-07-15 | **`outcomes_hat` is the canonical spelling everywhere — public and internal — as a clean rename (no alias, no `FutureWarning`, no migration).** Public: `outcomes_hat()`, `df_outcomes_hat`, `outcomes_hat_columns`. Internal: `_column_roles` key `"predicted"` → `"outcomes_hat"`, the `_create()`/`from_frame()` parameter `predicted_outcome_columns` → `outcomes_hat_columns`, internal locals (`predicted_list` → `outcomes_hat_list`, etc.), the overlap-validation dict key, and the protocol member `_outcomes_hat_columns`. The old `predicted_outcome_columns` param/property and the `"predicted"` role key are **removed outright**. | One vocabulary end-to-end; removes the overload with predicted-*weights* (`predict_weights`); distinct from `.outcomes()` (observed Y). No deprecation needed — the `predicted` role was reserved scaffolding **unused by any code or user** (only storage/validation/tests referenced it) and was never populated, so there is nothing to keep back-compatible and nothing to migrate. |
22
23
| 2026-07-15 |**Scope: outcome model now, AIPW later.** Ship IPW/Hájek (exists) + outcome-model / g-computation estimate (`μ̂_OM`) in 0.23.0; defer explicit AIPW/DR to a follow-up. Phase 1 delivers **no general DR** — only the linear-WLS special case (§1). | Smaller, reviewable diff stack that delivers the core ask (average `outcomes_hat` on the target). AIPW variance/CI + the normalization contract get their own design pass. (superseded 2026-07-18: explicit AIPW shipped as `bf.aipw()` — general for any learner, not only the linear-WLS special case; only AIPW variance/CI + cross-fitting remain deferred.) |
23
24
| 2026-07-15 |**Estimate lives on the `outcomes_hat()` view:**`μ̂_OM = bf.outcomes_hat().mean()` (target row). Not under `.outcomes()`, no separate `outcome_estimate(method=)` dispatcher for phase 1. | Keeps the estimate where the predicted outcomes live; reuses the existing weighted-mean/CI machinery for free. |
@@ -813,7 +814,7 @@ absolute GitHub URLs; the tutorial notebook (created in diff 7, extended in 8–
813
814
end-to-end inCI.
814
815
815
816
**Phase 2 (separate later stack):**
816
-
-**`[balance] Add doubly-robust AIPW estimate bf.aipw() + R oracles`** (D112679814) — **shipped** the AIPW point estimate (combine `outcomes_hat`+IPW weights). Still deferred on top of it: the same-scale `w_R`/`w_T` normalization **assert**, **cross-fitted** (out-of-fold) responder residuals,andAIPW variance/CI.
817
+
-**`[balance] Add doubly-robust AIPW estimate bf.aipw() + R oracles`** (D112679814) — **shipped** the AIPW point estimate (combine `outcomes_hat`+IPW weights)and its same-scale `w_R`/`w_T` normalization assertion. Still deferred on top of it: **cross-fitted** (out-of-fold) responder residuals andAIPW variance/CI.
0 commit comments