|
1196 | 1196 | "3. **Inspect the printed `CheckResult.text`** for the verdict and per-fold summaries; the full hierarchical-null draws are available in `metadata[\"null_samples\"]`.\n", |
1197 | 1197 | "4. **Open the HTML report** (iframe) for a consolidated view alongside the main estimates.\n", |
1198 | 1198 | "\n", |
1199 | | - "**Interpreting the result on this dataset.** On the example dataset above, the check reports `NOT SUPPORTED` with `P(actual outside null) ≈ 0.92` — *just* below the 0.95 default threshold. The actual cumulative impact (≈ −38) is clearly large, but the hierarchical null inferred from the placebo folds is wide enough to swallow it. Drilling into the per-fold summaries explains why: fold 1's pseudo-treatment time lands at index 12, leaving only 12 pre-treatment observations to fit a synthetic control on 7 donors. That model is poorly identified, so its posterior cumulative impact has `sd ≈ 30`, which inflates `tau` in the hierarchical status-quo model and broadens the null distribution. Fold 2, with a 41-row pre-period, behaves much better (`sd ≈ 9`).\n", |
| 1199 | + "**Interpreting the result on this dataset.** On the example dataset above the verdict sits *right* on the 0.95 threshold — the printed `P(actual outside null)` lands near 0.92, and small MCMC sampling noise can push it either side of the cutoff between runs (see the reproducibility note below). Read the verdict as borderline rather than as a clean pass or fail. The actual cumulative impact (≈ −38) is clearly large, but the hierarchical null inferred from the placebo folds is wide enough that the check cannot discriminate sharply on this data. Drilling into the per-fold summaries explains why: fold 1's pseudo-treatment time lands at index 12, leaving only 12 pre-treatment observations to fit a synthetic control on 7 donors. That model is poorly identified, so its posterior cumulative impact carries a large standard deviation, which inflates `tau` in the hierarchical status-quo model and broadens the null distribution. Fold 2, with a 41-row pre-period, behaves much better.\n", |
1200 | 1200 | "\n", |
1201 | | - "So this is *not* a clean rejection of the headline causal estimate; it is the placebo-in-time check telling you, correctly, that with this particular dataset and `n_folds=2` the placebo windows do not provide a tight enough null to discriminate. In general, a **pass** means the actual cumulative impact is unlikely under the status-quo null inferred from placebo folds — consistent with a real treatment effect. A **fail** means the placebo folds produced effects of similar magnitude, so the real effect is hard to distinguish from background variability. Like any single diagnostic, this is *necessary but not sufficient*: passing does not prove identification, and failing does not prove the absence of an effect {cite:p}`reichardt2019quasi`.\n", |
| 1201 | + "Treat this as an illustrative borderline case rather than a substantive verdict on the headline causal estimate — with only `n_folds=2` on a short series, this dataset does not give the placebo-in-time check enough independent evidence to call it either way. In general, a **pass** means the actual cumulative impact is unlikely under the status-quo null inferred from placebo folds — consistent with a real treatment effect. A **fail** means the placebo folds produced effects of similar magnitude, so the real effect is hard to distinguish from background variability. Like any single diagnostic, this is *necessary but not sufficient*: passing does not prove identification, and failing does not prove the absence of an effect {cite:p}`reichardt2019quasi`.\n", |
1202 | 1202 | "\n", |
1203 | 1203 | ":::{note}\n", |
1204 | | - "**Known limitation: placebo windows that land too early.** `PlaceboInTime` derives `intervention_length` from `data.index.max() - treatment_time` and shifts the placebo treatment time backward by multiples of that length. When the resulting earliest fold has a short pre-period (as fold 1 does here), the synthetic control fit on that fold is noisy and can dominate the hierarchical null. This is being tracked in [issue #875](https://github.com/pymc-labs/CausalPy/issues/875), which proposes letting users pass an explicit `intervention_length` and adding a configurable minimum pre-period per fold so weak folds can be skipped. As an interim workaround you can pass an `experiment_factory` to `PlaceboInTime` that fits placebo folds on a smaller donor pool or a shorter intervention window.\n", |
| 1204 | + "**Known limitations being tracked.** Two upstream issues affect how this check behaves on the example data above. Until both are resolved, treat borderline `P(actual outside null)` values on this dataset as exactly that — borderline — rather than as evidence for or against the headline estimate.\n", |
| 1205 | + "\n", |
| 1206 | + "- **Placebo windows that land too early ([#875](https://github.com/pymc-labs/CausalPy/issues/875)):** `PlaceboInTime` derives `intervention_length` from `data.index.max() - treatment_time` and shifts the placebo treatment time backward by multiples of that length. When the resulting earliest fold has a short pre-period (as fold 1 does here), the synthetic control fit on that fold is noisy and can dominate the hierarchical null. The proposed fix lets users pass an explicit `intervention_length` and adds a configurable minimum pre-period per fold so weak folds can be skipped. As an interim workaround you can pass an `experiment_factory` to `PlaceboInTime` that fits placebo folds on a smaller donor pool or a shorter intervention window.\n", |
| 1207 | + "- **Reproducibility gap in the internal posterior predictive ([#876](https://github.com/pymc-labs/CausalPy/issues/876)):** even with `random_seed` plumbed through both `sample_kwargs` and the constructor, the internal `pm.sample_posterior_predictive` call for `theta_new` is not currently seeded, so the printed `P(actual outside null)` can drift by ~0.01–0.02 between runs and may straddle the 0.95 threshold. The proposed fix unifies the seed surface so the constructor's `random_seed` deterministically governs every stochastic stage of the check.\n", |
1205 | 1208 | ":::\n", |
1206 | 1209 | "\n", |
1207 | 1210 | "#### If this check fails\n", |
|
0 commit comments