diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d4cdfa..a5d2dcea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- **HAD `trends_lin=True` linear-trend detrending mode** on `HeterogeneousAdoptionDiD.fit(aggregate="event_study")`, `joint_pretrends_test`, and `joint_homogeneity_test`. Mirrors R `DIDHAD::did_had(..., trends_lin=TRUE)` (paper Eq. 17 / Eq. 18 / page 32 joint-Stute homogeneity-with-trends). Per-group linear-trend slope estimated as `Y[g, F-1] - Y[g, F-2]` and applied as `(t - base) × slope` adjustment to per-event-time outcome evolutions. Requires F ≥ 3 (panel must contain F-2). The "consumed" placebo at our event-time `e=-2` is auto-dropped (R reduces max placebo lag by 1 with the same effect). Mutually exclusive with survey weighting (`survey_design` / `survey` / `weights`): raises `NotImplementedError` per `feedback_per_method_survey_element_contract` (weighted slope estimator not derived from paper; tracked in TODO.md as a follow-up). Bit-exact backcompat for `trends_lin=False` (default). Patch-level (additive keyword-only kwarg). +- **HAD R-package end-to-end parity test** vs `DIDHAD` v2.0.0 (`Credible-Answers/did_had`) on the **`design="continuous_at_zero"` (Design 1') surface**. New parity fixture `benchmarks/data/did_had_golden.json` generated by `benchmarks/R/generate_did_had_golden.R` covers 3 paper-derived synthetic DGPs (Uniform, Beta(2,2), Beta(0.5,1)) × 5 method combinations (overall, event-study, placebo, yatchew, trends_lin). The harness explicitly forces `HeterogeneousAdoptionDiD(design="continuous_at_zero")` because R `did_had` always evaluates the local-linear at `d=0` regardless of dose distribution; our default `design="auto"` may legitimately choose `continuous_near_d_lower` or `mass_point` on dose distributions with boundary density bounded away from zero (e.g., Beta(2,2)) and thereby diverge from R numerically — that divergence is methodologically defensible but out of scope for this parity test. Python parity test `tests/test_did_had_parity.py` asserts point estimate / SE / CI bounds at `atol=1e-8` and Yatchew T-stat at `atol=1e-10` after a documented `× G/(G-1)` finite-sample convention shift. Two intentional convention deviations from R, documented in `docs/methodology/REGISTRY.md`: (a) we report the bias-corrected point estimate (modern CCF 2018 convention; R's `Estimate` column reports the conventional estimate with the bias-corrected CI separately — our `att` matches R's CI midpoint); (b) Yatchew uses paper Appendix E's literal (1/G) variance-denominator convention while R uses base-R `var()`'s (1/(N-1)) sample-variance convention (parity is bit-exact after the `× G/(G-1)` shift). Yatchew on placebos with R's mean-independence null (`order=0`) is not yet exposed in our `yatchew_hr_test` (we currently only support the linearity null) and is skipped in the parity test; tracked as TODO follow-up. + ### Changed - **Rust dependency upgrades**: bumped `rand` 0.8 → 0.10 and `rand_xoshiro` 0.6 → 0.8 in the Rust backend (the two crates are coupled through `rand_core` and must move together). MSRV bumped from Rust 1.84 → 1.85 to satisfy the new dependency requirements. Three call sites in `rust/src/bootstrap.rs` updated for the `rand 0.9` API rename: `gen::()` → `random::()`, `gen::()` → `random::()`, `gen_range(0..6)` → `random_range(0..6)`. **Webb wild bootstrap byte stream shifted** as a side effect: `rand 0.9` reworked the internal algorithm for `random_range` (improved rejection sampling), so `Xoshiro256PlusPlus::seed_from_u64(seed)` followed by `random_range(0..6)` consumes RNG bytes differently than the old `gen_range(0..6)` did. Distributional properties of Webb weights are unchanged (still uniform over the 6-point support); aggregate inference (SE, p-values, CI) converges to the same values for any reasonable `n_bootstrap`. Rademacher and Mammen byte streams are bit-identical to the prior release. Anyone with a saved Rust+Webb baseline pinning specific seeded results will see different numbers; the regression test suite uses within-build seed-reproducibility (not cross-version baselines) so all internal tests pass unchanged. New regression guard `TestRustBackend::test_bootstrap_weights_bit_identity_snapshot` pins fixed-seed weights for all three weight types, so any future RNG drift fails loudly with a localized error message. diff --git a/TODO.md b/TODO.md index 8aeff2a2..1d6cd918 100644 --- a/TODO.md +++ b/TODO.md @@ -101,7 +101,10 @@ Deferred items from PR reviews that were not addressed before merge. | `HeterogeneousAdoptionDiD` mass-point: `vcov_type in {"hc2", "hc2_bm"}` raises `NotImplementedError` pending a 2SLS-specific leverage derivation. The OLS leverage `x_i' (X'X)^{-1} x_i` is wrong for 2SLS; the correct finite-sample correction uses `x_i' (Z'X)^{-1} (...) (X'Z)^{-1} x_i`. Needs derivation plus an R / Stata (`ivreg2 small robust`) parity anchor. | `diff_diff/had.py::_fit_mass_point_2sls` | Phase 2a | Medium | | `HeterogeneousAdoptionDiD` survey-design API consolidation, **next minor bump**: drop the deprecated `survey=` and `weights=` kwargs on all 8 HAD surfaces (`HeterogeneousAdoptionDiD.fit`, `did_had_pretest_workflow`, `qug_test`, `stute_test`, `yatchew_hr_test`, `stute_joint_pretest`, `joint_pretrends_test`, `joint_homogeneity_test`); only `survey_design=` remains. Also fold the legacy back-end `weights=` paths (e.g. `_aggregate_unit_weights` ad-hoc routing) into the unified `_resolve_survey_for_fit`-driven path. The `_make_trivial_resolved` underscore alias on `survey.py` stays (one-line, harmless). DeprecationWarning ships in this PR; the removal PR is ~50 LoC of cleanup. | `diff_diff/had.py`, `diff_diff/had_pretests.py` | next minor bump | Medium | | `HeterogeneousAdoptionDiD` continuous paths: thread `cluster=` through `bias_corrected_local_linear` (Phase 1c's wrapper already supports cluster; Phase 2a ignores it with a `UserWarning` on the continuous path to keep scope tight). | `diff_diff/had.py`, `diff_diff/local_linear.py` | Phase 2a | Low | -| `HeterogeneousAdoptionDiD` Eq 18 linear-trend detrending (Pierce-Schott style): the joint-Stute infrastructure shipped in the Phase 3 follow-up supports pre-trends (mean-indep) and post-homogeneity (linearity) nulls. The Pierce-Schott application (paper Section 5.2) uses a LINEAR-TREND detrending of pre-period outcomes before the joint CvM — `Y_{g,t} - Y_{g,t_anchor} - (t - t_anchor)*(Y_{g,t_anchor} - Y_{g,t_anchor-1})` — reaching p=0.51 on US-China tariff data. Extends `joint_pretrends_test` with a detrending mode or a separate Eq 18-specific helper. Deferred to Phase 4 replication harness (where the published p=0.51 serves as the parity anchor). | `diff_diff/had_pretests.py::joint_pretrends_test` | Phase 4 | Medium | +| `HeterogeneousAdoptionDiD` Eq 17 / Eq 18 linear-trend detrending: SHIPPED in PR #389 (Phase 4 R-parity, 2026-04). Exposed as `trends_lin: bool = False` keyword-only kwarg on `HeterogeneousAdoptionDiD.fit(aggregate="event_study")`, `joint_pretrends_test`, `joint_homogeneity_test`. Mirrors R `DIDHAD::did_had(..., trends_lin=TRUE)`. Pierce-Schott published-number parity (paper p=0.51 / p=0.40) deferred indefinitely (LBD-restricted analysis panel); replaced by end-to-end R-package parity at `tests/test_did_had_parity.py`. | `diff_diff/had_pretests.py::joint_pretrends_test`, `diff_diff/had.py` | Phase 4 (shipped) | Done | +| `HeterogeneousAdoptionDiD` `trends_lin × survey_design` follow-up: per-group linear-trend slope under survey weighting (weighted slope estimator? per-PSU slope?) is not derived from the paper. PR #389 raises `NotImplementedError` on the combination across all 3 trends_lin surfaces. If user demand emerges, derive the weighted variant and lift the gate. | `diff_diff/had.py::HeterogeneousAdoptionDiD.fit`, `diff_diff/had_pretests.py::joint_pretrends_test`, `diff_diff/had_pretests.py::joint_homogeneity_test` | follow-up | Low | +| `HeterogeneousAdoptionDiD` `yatchew_hr_test(null="mean_independence")` mode: R `YatchewTest::yatchew_test(order=0)` fits `Y ~ 1` (intercept-only baseline) and tests mean-independence of Y from D; R's `DIDHAD::did_had(yatchew=TRUE)` uses this on placebo rows ("non-parametric pre-trends test"). Our `yatchew_hr_test` always fits `Y ~ D` (linearity null) — no `null=` parameter exposed. Adding the mean-independence mode would (a) give practitioners a more conventional pre-trends test surface, and (b) close the PR #389 R-parity feature gap on the placebo-Yatchew rows (currently skipped in `tests/test_did_had_parity.py::TestYatchewParity` because the two tests are not the same statistic). | `diff_diff/had_pretests.py::yatchew_hr_test` | follow-up | Medium | +| `HeterogeneousAdoptionDiD` Stute family Stata-bridge parity: PR #389 R-parity covers the full HAD fit + Yatchew surfaces but skips Stute family (`stute_test`, `stute_joint_pretest`, `joint_pretrends_test`, `joint_homogeneity_test`) because no R `Stutetest` package exists publicly (chaisemartinPackages publishes only the Stata `stute_test` module; the paper cites a 2024c R Stutetest module that is not on GitHub or CRAN). Stata-bridge parity would add `benchmarks/stata/generate_stute_golden.do` + a Stata installation requirement. Low priority unless user demand emerges. | `benchmarks/stata/`, `tests/test_stute_test_parity.py` | follow-up | Low | | `HeterogeneousAdoptionDiD` Phase 3 Stute performance: Appendix D vectorized matrix form replaces the per-iteration OLS refit with a single precomputed `M = I - X(X'X)^{-1}X'` applied to `eps * eta`. Functionally identical, ~2x faster. Shipped literal-refit form in Phase 3 to match paper text and keep reviewer surface small. | `diff_diff/had_pretests.py::stute_test` | Phase 3 | Low | | `HeterogeneousAdoptionDiD` Phase 3 R-parity: Phase 3 ships coverage-rate validation on synthetic DGPs (not tight point parity against `chaisemartin::stute_test` / `yatchew_test`). Tight numerical parity requires aligning bootstrap seed semantics and `B` across numpy/R and is deferred. | `tests/test_had_pretests.py` | Phase 3 | Low | | `HeterogeneousAdoptionDiD` Phase 3 nprobust bandwidth for Stute: some Stute variants on continuous regressors use nprobust-style optimal bandwidth selection. Phase 3 uses OLS residuals from a 2-parameter linear fit (no bandwidth selection). nprobust integration is a future enhancement; not in paper scope. | `diff_diff/had_pretests.py::stute_test` | Phase 3 | Low | diff --git a/benchmarks/R/generate_did_had_golden.R b/benchmarks/R/generate_did_had_golden.R new file mode 100644 index 00000000..4240e886 --- /dev/null +++ b/benchmarks/R/generate_did_had_golden.R @@ -0,0 +1,282 @@ +# Generate cross-language end-to-end parity fixture for HAD Phase 4 +# (PR #389 R-parity vs `Credible-Answers/did_had`). +# +# Purpose: validate Python `HeterogeneousAdoptionDiD.fit()` (overall, +# event-study, placebo, yatchew, trends_lin) against R `DIDHAD::did_had()` +# bit-exactly on shared input. The R package is the methodology source +# of truth (the de Chaisemartin team wrote it); matching it within +# `atol=1e-8` on point/SE/CI and `atol=1e-10` on closed-form Yatchew +# T-stats is a strictly stronger correctness signal than reproducing the +# paper's published Pierce-Schott numbers (which depend on a +# LBD-restricted analysis panel). +# +# Usage: +# Rscript benchmarks/R/generate_did_had_golden.R +# +# Output: +# benchmarks/data/did_had_golden.json +# +# Phase 4 of HeterogeneousAdoptionDiD (de Chaisemartin et al. 2025). +# Python test loader: tests/test_did_had_parity.py. +# +# Pin: DIDHAD == 2.0.0 (CRAN current as of 2026-04). YatchewTest >= 1.1.0. + +library(jsonlite) +library(DIDHAD) +library(YatchewTest) + +# PR #392 R4 P3 / R5 P3: pin exact upstream versions so future +# regeneration does not silently re-anchor the goldens to a newer +# CRAN release while CHANGELOG / REGISTRY / parity test still cite +# v2.0.0 / SHA `edc09197`. The parity contract runs through +# `nprobust` numerical paths so we pin it too. Bump these pins +# (here AND in the parity test's `test_metadata_versions_match`) +# when intentionally re-anchoring. +stopifnot(packageVersion("DIDHAD") == "2.0.0") +stopifnot(packageVersion("YatchewTest") == "1.1.1") +stopifnot(packageVersion("nprobust") == "0.5.0") + +# ------------------------------------------------------------------------- +# Panel builder: 5-period panel with F=4 (treatment onset at t=4). +# Pre-periods: 1, 2, 3 (D=0). Post-periods: 4, 5 (D=fixed positive dose). +# Y[g, t] = unit_fe[g] + trend[g] * (t - 1) + (dose[g] + dose[g]^2) * (t >= F) + noise +# ------------------------------------------------------------------------- + +build_panel <- function(G, F_treat, T_periods, dose_draws, seed, + unit_trend_sd = 0.05, noise_sd = 0.5) { + set.seed(seed) + n <- G * T_periods + unit_fe <- rnorm(G, mean = 0, sd = 1.0) + unit_trend <- rnorm(G, mean = 0.1, sd = unit_trend_sd) + noise <- rnorm(n, mean = 0, sd = noise_sd) + + rows <- vector("list", n) + k <- 1 + for (g in seq_len(G)) { + for (t in seq_len(T_periods)) { + treated <- as.numeric(t >= F_treat) + y <- unit_fe[g] + unit_trend[g] * (t - 1) + + (dose_draws[g] + dose_draws[g]^2) * treated + + noise[k] + d_obs <- if (treated == 1) dose_draws[g] else 0.0 + # Use short column names (g, t, d, y) matching DIDHAD's tutorial + # convention. The package has a data-masking issue when column + # names alias the formal parameter names (e.g., column "time" with + # `time = "time"` resolves to the column values inside dplyr's + # `.data[[get("time")]]` lookup), so avoid that overlap upstream. + rows[[k]] <- data.frame( + g = g, + t = t, + y = y, + d = d_obs, + stringsAsFactors = FALSE + ) + k <- k + 1 + } + } + do.call(rbind, rows) +} + +# DGP 1: D ~ Uniform(0, 1). +dgp_uniform <- function(G = 200, F_treat = 4, T_periods = 5, seed = 20260426) { + set.seed(seed * 2L + 1L) + d <- runif(G, min = 0.0, max = 1.0) + list( + name = "uniform_G200_F4_T5", + panel = build_panel(G, F_treat, T_periods, d, seed = seed), + G = G, F_treat = F_treat, T_periods = T_periods, + dose_distribution = "Uniform(0, 1)", + seed = seed + ) +} + +# DGP 2: D ~ Beta(2, 2). Symmetric, bell-shaped on [0, 1]. +dgp_beta22 <- function(G = 200, F_treat = 4, T_periods = 5, seed = 20260426) { + set.seed(seed * 2L + 2L) + d <- rbeta(G, shape1 = 2, shape2 = 2) + list( + name = "beta22_G200_F4_T5", + panel = build_panel(G, F_treat, T_periods, d, seed = seed), + G = G, F_treat = F_treat, T_periods = T_periods, + dose_distribution = "Beta(2, 2)", + seed = seed + ) +} + +# DGP 3: D ~ Beta(0.5, 1). Heavy left tail (mass near 0); approximates +# the empirical Pierce-Schott NTR-gap distribution where many industries +# have small tariff gaps (boundary density vanishes property). +dgp_boundary <- function(G = 200, F_treat = 4, T_periods = 5, seed = 20260426) { + set.seed(seed * 2L + 3L) + d <- rbeta(G, shape1 = 0.5, shape2 = 1.0) + list( + name = "boundary_G200_F4_T5", + panel = build_panel(G, F_treat, T_periods, d, seed = seed), + G = G, F_treat = F_treat, T_periods = T_periods, + dose_distribution = "Beta(0.5, 1)", + seed = seed + ) +} + +# ------------------------------------------------------------------------- +# Run did_had with given options and extract the standardized result +# matrix. The R package returns a `did_had` S3 object whose `results` +# slot has `resmat` (effects + placebos) and optionally `yatchew_test`. +# ------------------------------------------------------------------------- + +run_did_had <- function(panel, effects = 1, placebo = 0, + trends_lin = FALSE, yatchew = FALSE) { + # graph_off=TRUE suppresses the auto-print of the event-study plot. + fit <- did_had( + df = panel, + outcome = "y", + group = "g", + time = "t", + treatment = "d", + effects = effects, + placebo = placebo, + trends_lin = trends_lin, + yatchew = yatchew, + graph_off = TRUE + ) + res <- fit$results + resmat <- res$resmat + out <- list( + n_effects_actual = res$res.effects, + n_placebo_actual = res$res.placebo, + rownames = rownames(resmat), + estimate = unname(resmat[, "Estimate"]), + se = unname(resmat[, "SE"]), + ci_lo = unname(resmat[, "LB.CI"]), + ci_hi = unname(resmat[, "UB.CI"]), + n_per_horizon = unname(as.integer(resmat[, "N"])), + bw_per_horizon = unname(resmat[, "BW"]), + n_within_bw = unname(as.integer(resmat[, "N.BW"])), + qug_t = unname(resmat[, "T"]), + qug_p = unname(resmat[, "p.val"]), + event_id = unname(as.integer(resmat[, "ID"])) + ) + if (yatchew) { + yt <- res$yatchew_test + out$yatchew_t <- unname(yt[, "T_hr"]) + out$yatchew_p <- unname(yt[, "p-value"]) + out$yatchew_n <- unname(as.integer(yt[, "N"])) + # Capture sigma2 components for diagnostic comparison; the column + # names contain unicode (sigma², σ²). Use positional indexing. + out$yatchew_sigma2_lin <- unname(yt[, 1]) + out$yatchew_sigma2_diff <- unname(yt[, 2]) + } + out +} + +# ------------------------------------------------------------------------- +# Build the DGP × method-combo fixture grid. +# ------------------------------------------------------------------------- + +dgp_builders <- list( + uniform = dgp_uniform, + beta22 = dgp_beta22, + boundary = dgp_boundary +) + +# Per-DGP method matrix. Each combo runs did_had with the named flags +# and stores the resulting standardized resmat dict alongside the input +# panel arrays. Python parity test loops over combos and asserts. +# +# Why effects=2/placebo=2: F=4 with T=5 leaves 2 post-period horizons +# (t=4, 5) and 2 pre-period placebos (t=2, 1) without trends_lin. R +# auto-truncates if requested > feasible. Under trends_lin, the +# F-2 -> F-1 evolution is consumed by the slope estimator and R reduces +# max placebo by 1 (so only placebo at t=1 survives). +combos <- list( + list(name = "overall_e1", effects = 1, placebo = 0, + trends_lin = FALSE, yatchew = FALSE), + list(name = "event_e2_p2", effects = 2, placebo = 2, + trends_lin = FALSE, yatchew = FALSE), + list(name = "event_e2_p2_yatchew", effects = 2, placebo = 2, + trends_lin = FALSE, yatchew = TRUE), + list(name = "event_e2_p2_trendslin", effects = 2, placebo = 2, + trends_lin = TRUE, yatchew = FALSE), + list(name = "event_e2_p2_yatchew_trendslin", effects = 2, placebo = 2, + trends_lin = TRUE, yatchew = TRUE) +) + +fixtures <- list() +for (dgp_name in names(dgp_builders)) { + dgp <- dgp_builders[[dgp_name]]() + panel <- dgp$panel + combo_results <- list() + for (combo in combos) { + res <- run_did_had( + panel = panel, + effects = combo$effects, + placebo = combo$placebo, + trends_lin = combo$trends_lin, + yatchew = combo$yatchew + ) + combo_results[[combo$name]] <- list( + effects = combo$effects, + placebo = combo$placebo, + trends_lin = combo$trends_lin, + yatchew = combo$yatchew, + result = res + ) + } + fixtures[[dgp$name]] <- list( + name = dgp$name, + G = dgp$G, + F = dgp$F_treat, + T = dgp$T_periods, + dose_distribution = dgp$dose_distribution, + seed = dgp$seed, + panel = list( + g = panel$g, + t = panel$t, + y = panel$y, + d = panel$d + ), + combos = combo_results + ) +} + +# ------------------------------------------------------------------------- +# Serialize +# ------------------------------------------------------------------------- + +out <- list( + metadata = list( + description = paste( + "DIDHAD::did_had end-to-end parity fixture for HAD Phase 4", + "(PR #389 R-parity).", + sep = " " + ), + didhad_version = as.character(packageVersion("DIDHAD")), + yatchewtest_version = as.character(packageVersion("YatchewTest")), + nprobust_version = as.character(packageVersion("nprobust")), + r_version = as.character(getRversion()), + n_dgps = length(fixtures), + n_combos_per_dgp = length(combos), + point_atol = 1e-8, + se_atol = 1e-8, + ci_atol = 1e-8, + yatchew_atol = 1e-10, + qug_atol = 1e-12, + notes = paste( + "Three synthetic DGPs (Uniform, Beta(2,2), Beta(0.5,1) approximation", + "of the empirical Pierce-Schott NTR-gap distribution). Each DGP runs", + "5 method combos covering overall, event-study, placebo, yatchew,", + "and trends_lin variants. Tolerances per the Phase 4 plan.", + sep = " " + ) + ), + fixtures = fixtures +) + +out_dir <- "benchmarks/data" +if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE) +out_path <- file.path(out_dir, "did_had_golden.json") +write_json(out, path = out_path, digits = 17, auto_unbox = TRUE, null = "null") +message(sprintf( + "Wrote %d DGP fixtures (each with %d combos) to %s", + length(fixtures), length(combos), out_path +)) diff --git a/benchmarks/R/requirements.R b/benchmarks/R/requirements.R index 1de743cf..24d672c1 100644 --- a/benchmarks/R/requirements.R +++ b/benchmarks/R/requirements.R @@ -13,6 +13,9 @@ required_packages <- c( "triplediff", # Ortiz-Villavicencio & Sant'Anna (2025) triple difference "survey", # Lumley (2004) complex survey analysis "estimatr", # Blair et al. (2019) weighted robust / IV SE (HAD mass-point parity) + "DIDHAD", # de Chaisemartin et al. (2025) HAD estimator (HAD Phase 4 R-parity) + "YatchewTest", # Yatchew (1997) linearity test (HAD yatchew R-parity) + "nprobust", # Calonico-Cattaneo-Farrell local-linear (DIDHAD dependency) # Utilities "jsonlite", # JSON output for Python interop @@ -33,6 +36,42 @@ install_if_missing <- function(pkg) { } } +# PR #392 R6 P3: pinned-version installer for upstream packages whose +# version is part of the parity contract. The HAD R-parity test +# (`tests/test_did_had_parity.py`) and the generator +# (`benchmarks/R/generate_did_had_golden.R`) hard-pin DIDHAD, +# YatchewTest, and nprobust to specific versions; without a +# version-aware installer here, a fresh R environment would silently +# install whatever CRAN currently serves and the generator's +# `stopifnot(packageVersion(...) == "X.Y.Z")` would abort. +install_pinned_version <- function(pkg, version) { + if (requireNamespace(pkg, quietly = TRUE) && + as.character(packageVersion(pkg)) == version) { + message(sprintf("%s is already at pinned version %s.", pkg, version)) + return(invisible(NULL)) + } + message(sprintf("Installing %s == %s (pinned for HAD R-parity)...", pkg, version)) + if (!requireNamespace("remotes", quietly = TRUE)) { + install.packages("remotes", repos = "https://cloud.r-project.org/", quiet = TRUE) + } + remotes::install_version( + pkg, + version = version, + repos = "https://cloud.r-project.org/", + quiet = TRUE, + upgrade = "never" + ) +} + +# HAD R-parity (PR #392) version pins. Bump these in lockstep with +# the generator's `stopifnot(packageVersion(...) == "X.Y.Z")` and the +# parity test's `test_metadata_versions_match` when re-anchoring. +pinned_versions <- list( + DIDHAD = "2.0.0", + YatchewTest = "1.1.1", + nprobust = "0.5.0" +) + install_github_if_missing <- function(pkg, repo) { if (!requireNamespace(pkg, quietly = TRUE)) { message(sprintf("Installing %s from GitHub...", pkg)) @@ -49,6 +88,15 @@ install_github_if_missing <- function(pkg, repo) { message("Installing CRAN packages...") lapply(required_packages, install_if_missing) +# Reinforce the HAD R-parity pinned versions AFTER the bulk install +# above (which may have installed any-CRAN-version of e.g. nprobust +# as a transitive dep). install_pinned_version is idempotent if the +# correct version is already installed. +message("\nEnforcing HAD R-parity version pins...") +for (pkg in names(pinned_versions)) { + install_pinned_version(pkg, pinned_versions[[pkg]]) +} + # Install GitHub packages message("\nInstalling GitHub packages...") for (pkg in names(github_packages)) { diff --git a/benchmarks/data/did_had_golden.json b/benchmarks/data/did_had_golden.json new file mode 100644 index 00000000..46e87122 --- /dev/null +++ b/benchmarks/data/did_had_golden.json @@ -0,0 +1 @@ +{"metadata":{"description":"DIDHAD::did_had end-to-end parity fixture for HAD Phase 4 (PR #389 R-parity).","didhad_version":"2.0.0","yatchewtest_version":"1.1.1","nprobust_version":"0.5.0","r_version":"4.5.2","n_dgps":3,"n_combos_per_dgp":5,"point_atol":1e-08,"se_atol":1e-08,"ci_atol":1e-08,"yatchew_atol":1e-10,"qug_atol":9.9999999999999998e-13,"notes":"Three synthetic DGPs (Uniform, Beta(2,2), Beta(0.5,1) approximation of the empirical Pierce-Schott NTR-gap distribution). Each DGP runs 5 method combos covering overall, event-study, placebo, yatchew, and trends_lin variants. Tolerances per the Phase 4 plan."},"fixtures":{"uniform_G200_F4_T5":{"name":"uniform_G200_F4_T5","G":200,"F":4,"T":5,"dose_distribution":"Uniform(0, 1)","seed":20260426,"panel":{"g":[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,20,20,20,20,20,21,21,21,21,21,22,22,22,22,22,23,23,23,23,23,24,24,24,24,24,25,25,25,25,25,26,26,26,26,26,27,27,27,27,27,28,28,28,28,28,29,29,29,29,29,30,30,30,30,30,31,31,31,31,31,32,32,32,32,32,33,33,33,33,33,34,34,34,34,34,35,35,35,35,35,36,36,36,36,36,37,37,37,37,37,38,38,38,38,38,39,39,39,39,39,40,40,40,40,40,41,41,41,41,41,42,42,42,42,42,43,43,43,43,43,44,44,44,44,44,45,45,45,45,45,46,46,46,46,46,47,47,47,47,47,48,48,48,48,48,49,49,49,49,49,50,50,50,50,50,51,51,51,51,51,52,52,52,52,52,53,53,53,53,53,54,54,54,54,54,55,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58,58,58,58,58,59,59,59,59,59,60,60,60,60,60,61,61,61,61,61,62,62,62,62,62,63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,67,68,68,68,68,68,69,69,69,69,69,70,70,70,70,70,71,71,71,71,71,72,72,72,72,72,73,73,73,73,73,74,74,74,74,74,75,75,75,75,75,76,76,76,76,76,77,77,77,77,77,78,78,78,78,78,79,79,79,79,79,80,80,80,80,80,81,81,81,81,81,82,82,82,82,82,83,83,83,83,83,84,84,84,84,84,85,85,85,85,85,86,86,86,86,86,87,87,87,87,87,88,88,88,88,88,89,89,89,89,89,90,90,90,90,90,91,91,91,91,91,92,92,92,92,92,93,93,93,93,93,94,94,94,94,94,95,95,95,95,95,96,96,96,96,96,97,97,97,97,97,98,98,98,98,98,99,99,99,99,99,100,100,100,100,100,101,101,101,101,101,102,102,102,102,102,103,103,103,103,103,104,104,104,104,104,105,105,105,105,105,106,106,106,106,106,107,107,107,107,107,108,108,108,108,108,109,109,109,109,109,110,110,110,110,110,111,111,111,111,111,112,112,112,112,112,113,113,113,113,113,114,114,114,114,114,115,115,115,115,115,116,116,116,116,116,117,117,117,117,117,118,118,118,118,118,119,119,119,119,119,120,120,120,120,120,121,121,121,121,121,122,122,122,122,122,123,123,123,123,123,124,124,124,124,124,125,125,125,125,125,126,126,126,126,126,127,127,127,127,127,128,128,128,128,128,129,129,129,129,129,130,130,130,130,130,131,131,131,131,131,132,132,132,132,132,133,133,133,133,133,134,134,134,134,134,135,135,135,135,135,136,136,136,136,136,137,137,137,137,137,138,138,138,138,138,139,139,139,139,139,140,140,140,140,140,141,141,141,141,141,142,142,142,142,142,143,143,143,143,143,144,144,144,144,144,145,145,145,145,145,146,146,146,146,146,147,147,147,147,147,148,148,148,148,148,149,149,149,149,149,150,150,150,150,150,151,151,151,151,151,152,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,155,156,156,156,156,156,157,157,157,157,157,158,158,158,158,158,159,159,159,159,159,160,160,160,160,160,161,161,161,161,161,162,162,162,162,162,163,163,163,163,163,164,164,164,164,164,165,165,165,165,165,166,166,166,166,166,167,167,167,167,167,168,168,168,168,168,169,169,169,169,169,170,170,170,170,170,171,171,171,171,171,172,172,172,172,172,173,173,173,173,173,174,174,174,174,174,175,175,175,175,175,176,176,176,176,176,177,177,177,177,177,178,178,178,178,178,179,179,179,179,179,180,180,180,180,180,181,181,181,181,181,182,182,182,182,182,183,183,183,183,183,184,184,184,184,184,185,185,185,185,185,186,186,186,186,186,187,187,187,187,187,188,188,188,188,188,189,189,189,189,189,190,190,190,190,190,191,191,191,191,191,192,192,192,192,192,193,193,193,193,193,194,194,194,194,194,195,195,195,195,195,196,196,196,196,196,197,197,197,197,197,198,198,198,198,198,199,199,199,199,199,200,200,200,200,200],"t":[1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5],"y":[-1.2481976087462026,0.33516584772394142,-0.69652385632476399,1.0565852939635534,1.3703691245060239,-1.0004767215467067,-0.65773183371464561,-0.4315030051462877,0.57190958511231937,0.50361457696687018,-0.8376848656398912,-0.82292030328775334,-0.28230423676302774,0.78401422603818915,1.2673283680588057,-1.3502556720219618,-0.47835914308532312,-0.37463307000886414,-0.2253293773927274,-0.34936351908984342,0.028564281625720667,-0.88771009250153943,-0.20390652055351802,0.33260105241470023,0.46999685101274108,-0.78405095207953124,-0.94481072249866949,-0.85774650964545018,-0.28255429204252419,0.37278115461740602,-1.055080381556512,-1.0410501417555875,-1.7885232449922268,-2.714147648306076,-1.6900925381962795,0.018178826348799526,0.78263229846998406,0.68434859123845748,3.1919499688930122,3.7172465807525885,0.45557229061822152,0.55710780061696152,0.50019518770008498,1.5495833041626996,1.5323718022078161,-1.2683095304528753,-0.62260368239352559,-1.1276085654404513,-0.072657875236362413,-0.13037487190994684,0.10441246827896644,1.0726580754030139,0.71493404859853915,2.0913885931335932,1.84217884377387,-0.24034275410595177,0.60845095334667154,0.30448286162443072,0.070349655878582718,-0.47704451363118799,-0.6455934884985588,-0.53743505521753243,0.066315540321114863,1.2503402513996298,1.8399461764436096,0.096150240463075298,0.37485581479640329,1.0951452368408456,0.43943538809788885,0.6377111837564392,1.1190297127267588,1.4434429001925579,-0.088660852996109707,1.9076526535353977,1.4093658815558663,0.5085813008275446,0.95221726886404601,0.17410374176644855,1.1008320635850959,2.021149739741416,2.0028153164321139,1.6326643654005797,2.2835342572523967,2.3896647485628293,2.694673318055083,-0.70292159302829493,-0.14311746900120936,-0.63308429161201563,-0.73959359974951544,0.53247806242484419,-0.48366458294384529,-0.79882159903028249,-1.0285938933054779,-0.95085224478257757,0.11559719279514846,-0.8851144049251054,-0.9024119262896737,0.026866495585643912,1.4512871542655637,0.62177707933472903,1.4719572466536572,0.5515278851769041,1.6792613525207043,1.2738559912435192,1.2866328747828615,-0.15238359521482803,-0.29506468654208273,-0.60387260052811298,1.41033894110469,0.72023979906127711,-0.66318094919300075,-0.64515823715742859,0.014444696178840456,1.5922226786166134,2.4590953183001396,-1.2639262803387092,-1.1835270578404933,-0.38533568941590096,1.4524535041031428,1.922056022741387,-1.1673564651151553,-1.2629550170641282,-0.6429883142754762,-1.0864090507372044,-0.76535271449042219,-1.5254309508163653,-1.4258139830118861,-1.5121360951500606,-1.6578018442986719,-0.73522856032461958,-0.87935392708567661,-0.38451746797533626,0.2802679163799397,0.75493647517454487,0.51791685654125186,-2.2044387457976078,-1.5902724152012642,-1.2392130258801251,-0.89725312302453308,-0.418006164588764,1.1556871702279972,1.5559811987842804,1.7356153110963368,2.4406636167262548,2.5326842509415366,0.51344990766209686,0.63378545842404399,1.0179610387767823,1.1180600062251955,1.9892480946264082,-0.64593276633740082,0.27021022898658781,-0.12959156162073385,0.29581664105234617,1.1525932844988265,-0.25721026043614276,-0.0027665278429818696,0.35739226466389351,-0.10598001832426335,0.31890400636367888,0.5815556176967529,0.051256914787265462,0.28792708586127852,2.2007642133974845,2.3994131029620136,1.191989864627049,2.098844901228754,1.7983221007861776,2.5355782651612611,1.4227734012889162,-0.17660193347076514,0.12645375382791091,-1.1696045450552746,0.3019440467226574,0.14009767960894992,-1.5720107653850905,-1.253076447703745,-1.6035084306997962,-0.30991230001181058,-0.3040014810314573,-0.99172763891795135,-1.1616092623557612,-0.30170617241914544,-0.51717425024350017,-0.32188468962658451,-1.397064855932898,-1.8333627581029046,-1.1341108608339916,-1.2476249558820711,-0.96600932818690888,1.8606948369300516,0.70745583448155402,1.689898661431632,1.4071074489005595,2.456309915284216,-0.38104491110105698,-0.19458545814007269,-0.2838234951212053,0.067323096324223952,0.3383922465970432,0.056665459499193771,-0.67715242221320571,0.1999580968188126,1.8684690279133114,2.3241147292569577,0.99012105023405561,1.7173799128274494,1.5511713537636727,5.0025582901746244,4.627075192157621,0.83355800668779234,0.66766952459316753,-0.062393515415196998,1.2439370232395028,1.5676883056815873,0.85863528176647919,0.81463113735936332,0.82877997978309037,1.6722719183545798,2.110869854643485,0.54471046770607567,1.0814469930703536,1.2929979911574008,0.55508260447596425,0.58397306741233124,-2.0118095335669599,0.18601080155299154,-1.1085017465513451,0.13302068216623519,-1.1794546151342011,-1.850534748102437,-1.3754303723655816,-1.4479026590895108,-0.19707444964588611,0.68076822621419741,-0.6858809615273942,0.1813768921787921,1.0455448576319657,0.86954935045338266,0.056757473869507358,0.084855938609760456,0.16655334847406494,-0.20098228442447785,0.27779448434634924,0.69538472670804463,-1.3954730378394178,-0.012837190898052131,-0.5257320356502212,1.8173626345574609,1.1839272699693151,-0.45760869361396567,0.14564516083015899,0.20269362835951343,1.0995064291839851,1.0199550193035596,0.6333722886806642,0.26581495085304097,0.7914197039257711,1.3344450847216183,0.78145711050213451,0.81891694774334134,1.4785405574192825,0.33540938725035563,0.27670118511677666,1.6506469868021278,-0.13201085785916133,0.49096012354139301,0.80094213619371035,2.1142600758191996,2.366335255437134,-0.49245871773978167,-1.1113472106530158,-0.53263816503133998,-0.15455591889795633,-0.30458696248083261,0.86955011477678135,0.14439821517716223,-0.17144083586836256,0.75685844960240423,1.7583061996981675,1.2725462170617887,1.404058001479332,1.3836854961629701,1.7679964607618157,2.2261687717564014,0.85528424453809271,0.79930272828428295,1.0051824094098012,2.5208922288742324,2.3943907709803649,0.26060055843434327,0.87027136534179805,-0.28806290043680605,2.2798499189842198,1.6650276924542919,2.1520691295681904,2.2913647332895661,1.6085328404182881,2.2658376101991342,2.2706544269026727,-0.088357991012390447,0.044575070775824677,1.5853198399567283,0.4737601441634896,0.83812824167247912,0.61855251924503207,0.78543994824635566,0.60757089823806376,0.22917551977912298,1.4744984209211365,-1.5934198868257219,-0.52874298267771747,-0.9989058314188497,-0.29663704736253654,-0.057837206559179906,3.0086321313483277,2.4899020246544383,2.1935078549366844,3.8170920222917832,3.2775027552108975,-2.4336434497477382,-2.2761897656209191,-1.7389434320135764,-0.23093300014508089,-0.72650484052447173,-1.9730852823962448,-1.7005688275691169,-1.2824533799159654,-0.19399812621991658,0.76980700634038501,-2.2733963370132528,-0.48718262816479374,-0.81241864789718732,0.60223332832791809,1.9381723343007886,-0.59289045858785883,-0.16688579429424005,0.47547455137687733,1.529548129558773,0.97624628168656991,-1.5216915267905122,-2.439870058991469,-1.4004214553093799,0.14487413884096045,0.25786742053046363,-0.67730338336495155,0.55922345382229333,-0.82547662059432703,0.7825150122409954,1.0922830355781592,0.79966485677075783,0.78167347729280134,0.81333938686238549,2.4140027087572107,1.4581098345638366,-0.25147321922443222,0.54026528428936482,1.1159509902194826,1.9811850341393253,1.3747771384948932,-0.27691375493956033,0.3864993117091724,0.43200515983271959,0.92243342638845993,0.013697964175351629,0.71464840828685028,-0.16861515118192708,0.36639612454336379,2.5330768233197385,2.1513702375331043,0.036061354530013412,0.62624106779751854,0.72195930267385888,1.6326133005247057,1.9085183787017681,-0.059301327510256054,0.39133510872254501,1.2142866597818749,1.5103144505777268,2.4761829860464326,-0.45026539137776472,0.44240187099163208,1.0247655939597928,1.7926128437835342,1.1698575775330147,0.11947689088530847,0.61062231915730913,-0.46975615084180572,1.7653670793338641,0.49545922351543742,-0.66398350181665688,0.27096103627078699,-0.094155791413704365,0.67085838069603365,0.6119120088687805,-1.6975901909232816,-2.1934008831032177,-2.7543507490457331,-0.57624576915362624,-0.61219998960042721,0.59369102548196495,1.1425148239644107,-0.27664132447310785,0.15444476615678793,1.5460682198018241,-0.25207511934079668,-1.0921119967534119,-0.68923973019036167,1.273187796612576,0.23161301733131201,-0.583831739107046,-0.26256617306279123,-0.72963312511088674,0.42607583673909227,0.53059425898293422,-0.47002302829063258,-1.1914598742823181,-0.19488737921408747,0.31039733704108086,0.21897766794453749,2.0649857282926258,1.4895858347377244,0.31282366175466847,4.0491677397425363,2.6198051731159397,-1.8736729105612615,-0.8050931912842817,-1.865255547767271,0.34197421138211981,0.94742842635411118,-0.34249604952935203,0.20291779245900834,-0.88581143620954617,1.736727443784789,2.0815736725097338,1.3530657464626896,1.2528698391829958,2.217085314892445,3.2922204319557089,3.0035389787280442,0.96406300840570069,-0.39969764368903515,1.533864061298591,2.8539701809862201,3.6255901105108994,1.5772564416944184,1.1016399601183142,1.8711572519750046,1.7533192947210814,2.3675439057304182,1.5663253116513769,1.5070162017273478,1.3306908005699865,2.1028253692782943,2.7338740379683895,1.743658936172696,0.76333961168977238,0.68668803160840697,2.1350495362435069,1.7449703184875447,1.7601728162220449,2.4520809690248035,2.0601506150732134,3.6276373133278108,4.8593350632344512,1.7321246267762405,2.292385561552674,2.4309657004227243,3.7365430885619775,3.5841613401872001,0.8881711901625492,0.58099202937506389,0.72875823146273189,1.1071989431881115,1.5992134654706616,-1.0863816007510803,0.17325377077186388,-0.89707912000465351,1.1538318547198663,3.486992450554697,1.2568182623530482,1.250840674800513,0.90810133096008927,2.237433362685552,1.8696676302975359,0.35186948296101789,1.4697771129936501,1.2418220432009219,0.92232047114352667,0.66453006956056937,-0.91846261667407381,-1.279407072475272,-1.2524460289323094,0.67482024754979264,-0.18078186837909063,0.74840441873161911,0.60200647306480704,1.385079522765795,1.9208590914901507,2.965799744281981,-0.71115208452455825,-1.1608257400969801,-1.5289056219692869,-0.34732872504069556,0.31453659071792184,-0.237940621123922,-0.3727075168973259,0.28556022785151192,1.3521468345172165,0.63091638351918744,0.66829263614986023,0.27760230229839233,1.6893196530720385,1.2899132623079288,1.7217561843665066,0.24117614411548333,0.71079437481623264,0.71528329155413095,2.9740602721007341,3.1958692930146588,0.050692772224158078,0.29404796193604865,-0.15279830022379237,1.9747675840456709,0.95062670627256263,-0.79452472152624321,-0.29470283734244423,-0.16573023912301763,2.2263524722281924,3.0313674520563438,-0.20352842932273285,-1.0262299634971981,-1.132846927646423,2.0660121459514622,1.2387203518468162,1.2531137862751569,2.2999110902912365,1.5722966468464741,2.7751414450508287,2.9666671889553187,-0.63610662182459099,-0.67043529532994439,-0.34971725104818363,-1.4660268319974505,0.5804979033009805,0.65707225030358218,0.26050857928371079,0.78486373716247426,1.3825280163565514,2.0160721925575849,0.25807407419333905,-0.069074345702453188,-0.036382374836353301,-0.71901959250879977,0.38227907185095883,0.45257097313350225,0.93448356848648051,0.42688005194625783,1.6790475040818076,1.3092615885419669,-0.90358407346735725,-0.8049009636272787,-0.88445361639369424,-1.2509273053550338,-1.3922127522213825,-1.256337202988175,-0.86647411391015861,-0.87299606967883703,-1.4800249495665887,-0.53061524446453912,-1.2984337773074808,-0.91477231929239433,-0.26368643560102623,1.2423923772969647,0.35626334572644036,0.63579151922199462,-0.15796488613324261,0.46914641319149325,1.2400812966901185,0.98828593557009514,0.020089354184026798,-1.2698054586157439,-1.2948293144841745,2.0030779664683971,1.3580551959373706,-0.052238539157843968,0.67980060409033249,-0.58019698792930841,0.6158050220730571,-0.19747845004850428,0.84139405633626729,1.6079490418207574,1.6766957804523321,1.7208818930944592,1.2930550602374775,-0.11065740999238963,0.56375401586464746,0.54794908864074898,2.2972576144557886,2.7837080048341605,-1.558165015996646,-0.95590714552714184,-1.1241176692657437,0.08211477495428568,0.67768532681729077,-0.38583919996460037,-1.6642629825538426,-1.0003388034215617,0.33482549374135123,-0.052523236947032204,-1.1124077333370228,-1.0219008688389666,-0.69216829792602441,-0.34120392693853607,-0.093863983247188754,0.027335240478659158,-0.12595014359066317,-0.73750258962417892,1.7462581363102256,2.6347782743657882,0.72938590076809229,0.34783549773903744,0.49450336004966577,1.9283922033002641,2.0550697475440396,1.1082414259799611,0.58063547064338528,0.33765583485871259,1.3014805048854583,1.373114285774649,-0.69390740180366128,0.3783022733157555,1.241591870549887,1.683017746706567,2.8285408787694513,-0.30581531376165921,0.018974594663268035,1.3714461021503519,0.91227738478599596,1.5867133731792151,-0.31128324860010148,-0.75152442018096255,-0.53051125483126471,1.7519382911155466,1.6014635164788134,-1.3861900394307523,-0.79615840716968422,0.16343573158786984,2.0507789980605073,2.0404588841954374,0.81785495012133325,0.32361085569433601,0.14382632669462619,2.5183814589590003,3.1882885674677062,-1.8857471798431993,-1.7289767541039072,-1.724031889335675,-1.2836991928648103,0.11182094327137315,0.76055403572738811,-0.31164444523453672,1.0892943447657502,2.9931530062440528,1.9823052181659342,1.4851140222134291,1.3996868773436038,1.8926797599701637,2.2587373547347527,2.4459297014191073,-0.053884105590754683,0.91215512365181262,0.77942341981835761,1.1530345884060795,1.2693687525603299,-0.41482919988521033,-0.57473014870117589,-0.013741987960638536,0.76939221603768559,0.95801300606707418,0.25256249835800709,0.45772620490953836,0.65022894417318078,1.465723973083495,1.1218898410083527,-0.51251954179099324,-1.1442087679922124,0.5623477752147813,0.32727343975184264,0.95633688223941993,-1.0098852833490941,-0.88826385169033495,-0.89886968509236465,-0.11447382986955607,0.64478404273138068,-0.79657091580369566,0.079762050484129765,-0.49613229077185189,-0.77803231405619733,-0.049808979085802334,0.086131226013897028,0.58859163434457396,0.12022050781125446,1.3239488492379525,0.93642107702162558,-0.83668450051128918,-1.8366912845731826,-0.04176436332279071,0.91664786217938055,0.67323662371033155,-1.0687793055305099,-0.16377930614451786,-0.32421454692389873,1.2807930068003968,-0.45614362881394177,-1.1199571669646948,0.33670949526572463,-0.24890888403455635,0.72843126346895504,0.43172912685910436,0.54631062826616872,0.088130176363804075,1.3053148117038953,1.0154781493166884,1.0460652880863563,1.3643141509806356,0.71460326330250712,1.096421389015785,2.3007991621249744,2.2732655721841262,0.22575274376357346,1.174890215104915,-0.4977747178692562,2.1590720055100303,1.5201933521636617,0.4226819254333572,0.77689408354182876,0.59158229359482339,2.3565555369672913,2.8480736237520916,-0.13412823529455825,-0.44384676375720677,0.43409123354743778,1.6699309570449201,0.29679771053010351,-2.0612967322929663,-0.70258713364277148,-0.95172730827486995,0.16757334867991258,0.44755134097611504,0.51391744981154752,0.75904353313058159,0.77129208868264032,1.5169332793606451,2.2849607350490619,0.52425162806585679,0.40333035150970198,0.51896591565285743,0.86316054221867922,1.7112036668190622,1.0644323324767546,2.6551022650194951,2.1014939712284928,1.1127076627794226,2.5762524336490689,1.0619239284868212,0.81752018057352593,0.90875879733858156,1.2207316491220845,1.0356194341212555,-1.0645662185741833,-0.0080850871212820863,-0.71211084893844689,1.0474794727686811,0.1664129391303818,-0.71477424230635378,-0.21421583127575125,-0.39404418187009888,0.067655443066739041,-0.014025263414243816,-0.89240231501570544,-1.8590800221969936,-1.228847762154252,-0.99565601920913871,-0.91071965899443064,-1.1339395231089875,-1.1109385627766235,-1.0596755690905426,1.3578972969846161,0.76658825161476929,-0.90362244067578312,0.13012161823134427,-0.16201105285358922,0.70115030830513758,1.551365251649824,-0.2430149596229946,-0.46990600868988858,-0.39483335363482386,0.21147872414668561,1.4590747276577918,-2.6138627768413256,-2.2925144620440117,-1.6114827149624802,-1.472340787620557,-1.6492004433577609,0.1993860056784037,1.6386614677708162,0.015289623449104561,1.0844999212090012,1.5989170489174507,0.29397308510538045,0.30843133361873076,0.94953480861035799,2.5328230005928805,2.5730123327870107,1.6296313470661528,1.6213365011382022,1.6477307312347591,3.7411240129042258,3.6419748616317711,0.95443819096590521,-0.062488692828586323,1.1639844348527748,1.1823904650669517,0.75501115397577934,0.91137265014016333,1.3454295478199438,0.86711120187423507,3.1703153197233482,2.9464968391837827,0.6632787541254368,1.1090750975390387,0.75904530397378223,2.8202957332858407,2.4976709645251356,0.41144886609561426,0.17190690274420117,0.38511500298196422,0.59033807475274802,1.0520673888360736,1.1404502045008691,0.60356153217516495,0.95186328864210568,0.67762870363278394,0.95169396112168847,1.1406813127101239,0.66856158479569094,-0.21180316573365388,1.8218899517524025,2.6826902893539462,-0.97840388508320775,-1.0133686812905278,-0.59872564914944404,0.69275458675764834,1.2842662085391927,-0.87642229351117906,-1.0131314107134222,-0.71364402653740866,1.2787913175383729,1.0596342762894795,1.953773957216373,1.8104150992831904,2.1604489474344342,3.8072924362730705,3.8339243224354806,-1.2002144379609423,-1.8232480317293354,-1.7382592536786716,-0.26588752867165671,-0.37280437362489743,1.5411254367536111,0.72893536550964722,0.84554966511548824,0.98947478644218845,1.305747288853595,0.27908720959562128,0.27861999438396068,0.32265831885826518,0.45770211598291777,0.73941615672149186,-2.8095539198611674,-1.0753757798822003,-1.1808033075579927,-0.9582077981540299,-0.74838118643030216,0.47516189340835302,0.17361724603430811,0.35736518182179966,0.98276486836046772,2.1133073163628278,0.084705260915975855,-0.78889487132225278,-0.24101215862291697,-0.71576491538343667,-0.60013870961721683,-1.1333204035257338,-0.96780574899574145,-0.63257252379064721,0.39379590830050143,-0.037353277883964253,-0.27616048371959495,-0.2891995458635197,-0.59524373567006705,-0.44059551308598033,0.64725467120380331,0.43432304838334379,1.6735083806429378,2.4702308013061867,2.8299912774783667,2.6044298263520842,-1.1266087123965263,-1.1950223223570473,-0.58573689518592542,0.22795946337663697,-0.79657526381226784,0.36920482845698305,-0.33108897746365751,1.4057858908172136,1.0059433919471792,1.0720628022678862,0.3904362512248335,1.0936356199373507,1.0684611550321248,3.2641537263192455,1.9848426904150536,0.50585428796135234,1.3510818043986026,0.92101302322478884,2.4692235495966322,1.8840984310789315,-0.93722093962854314,0.48304595881472945,-0.098262558713369835,0.94278583972894492,0.60626440084826572,-0.72114403465326449,-0.66492929653054955,-1.2494259612997998,0.64806581098907046,-0.43973671058698827,-1.8422731379209563,-1.6793582335225232,-1.695628927765557,-0.79143870256383764,-0.94577841501811599,-1.4065741941768097,0.046266194331139543,-1.2357305859211771,0.42712020320087307,-0.32815405205772386,-2.7845709928205991,-2.7481892838321631,-2.8123363614964974,-2.6996936424255464,-2.6873792125772358,1.8969356493980545,1.8362949085100533,2.6618696766671874,4.077333175550911,3.5232333196969243,0.44187667965561089,0.19227599536229262,0.29372098338371622,0.91397809046177592,1.398200637122377,1.9204086321170943,2.7143946848726337,1.8295816515592458,4.3240039843051692,3.7022022966949479,1.1823146804141853,0.92591622124172779,0.31488077776595086,1.0024104509532146,1.2301926627950697,0.49794454256171528,1.0789497470804525,0.45613060520636628,2.1565772918346506,1.8721007692885749,-2.0765911475142951,-1.2279788742726507,-2.057462417269639,-0.98601009214097746,0.65750356945476551,-3.3447081475340923,-2.9094782633837362,-2.1122307886116047,-2.019707490124814,-1.3387605338630189,-1.4587535060589185,-0.60424953548934945,-1.06462103623018,1.2490431378086044,0.97857703446345268,1.6398519238144524,1.369551128214791,0.68173825117349918,1.7520444377769475,1.7887150236869647],"d":[0,0,0,0.70766306319274008,0.70766306319274008,0,0,0,0.4699667003005743,0.4699667003005743,0,0,0,0.76951491646468639,0.76951491646468639,0,0,0,0.45619883108884096,0.45619883108884096,0,0,0,0.44303508708253503,0.44303508708253503,0,0,0,0.60905376449227333,0.60905376449227333,0,0,0,0.048973524942994118,0.048973524942994118,0,0,0,0.91151479724794626,0.91151479724794626,0,0,0,0.32233460457064211,0.32233460457064211,0,0,0,0.2467325811740011,0.2467325811740011,0,0,0,0.24932343186810613,0.24932343186810613,0,0,0,0.14611148624680936,0.14611148624680936,0,0,0,0.87751495791599154,0.87751495791599154,0,0,0,0.10868556308560073,0.10868556308560073,0,0,0,0.2655844958499074,0.2655844958499074,0,0,0,0.49627235159277916,0.49627235159277916,0,0,0,0.4468473915476352,0.4468473915476352,0,0,0,0.3644522160757333,0.3644522160757333,0,0,0,0.26194958831183612,0.26194958831183612,0,0,0,0.52015627268701792,0.52015627268701792,0,0,0,0.35737017588689923,0.35737017588689923,0,0,0,0.87987225851975381,0.87987225851975381,0,0,0,0.72526492667384446,0.72526492667384446,0,0,0,0.99373082187958062,0.99373082187958062,0,0,0,0.2152189495973289,0.2152189495973289,0,0,0,0.072123299585655332,0.072123299585655332,0,0,0,0.46745894686318934,0.46745894686318934,0,0,0,0.60397999593988061,0.60397999593988061,0,0,0,0.25295831542462111,0.25295831542462111,0,0,0,0.4943066427949816,0.4943066427949816,0,0,0,0.10647498187609017,0.10647498187609017,0,0,0,0.050009065540507436,0.050009065540507436,0,0,0,0.59324093908071518,0.59324093908071518,0,0,0,0.2510823649354279,0.2510823649354279,0,0,0,0.56122757284902036,0.56122757284902036,0,0,0,0.49023537384346128,0.49023537384346128,0,0,0,0.30258725420571864,0.30258725420571864,0,0,0,0.50560651416890323,0.50560651416890323,0,0,0,0.66282571433112025,0.66282571433112025,0,0,0,0.36301490594632924,0.36301490594632924,0,0,0,0.68097514822147787,0.68097514822147787,0,0,0,0.86502076499164104,0.86502076499164104,0,0,0,0.4428986688144505,0.4428986688144505,0,0,0,0.31527837156318128,0.31527837156318128,0,0,0,0.014910911675542593,0.014910911675542593,0,0,0,0.2699725239071995,0.2699725239071995,0,0,0,0.290028479648754,0.290028479648754,0,0,0,0.38525049528107047,0.38525049528107047,0,0,0,0.35926528600975871,0.35926528600975871,0,0,0,0.67485666181892157,0.67485666181892157,0,0,0,0.60550327715463936,0.60550327715463936,0,0,0,0.52808593166992068,0.52808593166992068,0,0,0,0.22645642329007387,0.22645642329007387,0,0,0,0.83932338538579643,0.83932338538579643,0,0,0,0.42481219535693526,0.42481219535693526,0,0,0,0.36965767666697502,0.36965767666697502,0,0,0,0.40907194837927818,0.40907194837927818,0,0,0,0.88652421906590462,0.88652421906590462,0,0,0,0.76679766806773841,0.76679766806773841,0,0,0,0.2285860066767782,0.2285860066767782,0,0,0,0.0091554280370473862,0.0091554280370473862,0,0,0,0.065243080956861377,0.065243080956861377,0,0,0,0.63577379542402923,0.63577379542402923,0,0,0,0.35131184780038893,0.35131184780038893,0,0,0,0.7301272451877594,0.7301272451877594,0,0,0,0.80871217465028167,0.80871217465028167,0,0,0,0.94492009049281478,0.94492009049281478,0,0,0,0.46113105397671461,0.46113105397671461,0,0,0,0.55214959871955216,0.55214959871955216,0,0,0,0.47647994011640549,0.47647994011640549,0,0,0,0.34710263786837459,0.34710263786837459,0,0,0,0.33088995213620365,0.33088995213620365,0,0,0,0.0076954287942498922,0.0076954287942498922,0,0,0,0.95800900855101645,0.95800900855101645,0,0,0,0.68383462587371469,0.68383462587371469,0,0,0,0.65949610923416913,0.65949610923416913,0,0,0,0.19033391098491848,0.19033391098491848,0,0,0,0.052337746601551771,0.052337746601551771,0,0,0,0.42285842169076204,0.42285842169076204,0,0,0,0.34453681879676878,0.34453681879676878,0,0,0,0.20546834613196552,0.20546834613196552,0,0,0,0.72218945156782866,0.72218945156782866,0,0,0,0.32346735754981637,0.32346735754981637,0,0,0,0.17549914564006031,0.17549914564006031,0,0,0,0.97608277969993651,0.97608277969993651,0,0,0,0.82922990643419325,0.82922990643419325,0,0,0,0.70357962581329048,0.70357962581329048,0,0,0,0.41099856817163527,0.41099856817163527,0,0,0,0.99700105492956936,0.99700105492956936,0,0,0,0.5615431098267436,0.5615431098267436,0,0,0,0.35381392133422196,0.35381392133422196,0,0,0,0.80225399322807789,0.80225399322807789,0,0,0,0.9124788602348417,0.9124788602348417,0,0,0,0.6986458576284349,0.6986458576284349,0,0,0,0.08899487997405231,0.08899487997405231,0,0,0,0.92402160284109414,0.92402160284109414,0,0,0,0.34113700920715928,0.34113700920715928,0,0,0,0.34040822181850672,0.34040822181850672,0,0,0,0.70273002446629107,0.70273002446629107,0,0,0,0.71090310090221465,0.71090310090221465,0,0,0,0.5073543933685869,0.5073543933685869,0,0,0,0.40452427649870515,0.40452427649870515,0,0,0,0.38923664973117411,0.38923664973117411,0,0,0,0.96245388127863407,0.96245388127863407,0,0,0,0.66367745585739613,0.66367745585739613,0,0,0,0.81989997928030789,0.81989997928030789,0,0,0,0.84974780352786183,0.84974780352786183,0,0,0,0.060015706578269601,0.060015706578269601,0,0,0,0.27523981733247638,0.27523981733247638,0,0,0,0.68000774551182985,0.68000774551182985,0,0,0,0.064318167744204402,0.064318167744204402,0,0,0,0.89151551434770226,0.89151551434770226,0,0,0,0.14880290837027133,0.14880290837027133,0,0,0,0.11849718936719,0.11849718936719,0,0,0,0.87006954033859074,0.87006954033859074,0,0,0,0.39510595146566629,0.39510595146566629,0,0,0,0.89489022037014365,0.89489022037014365,0,0,0,0.30350301484577358,0.30350301484577358,0,0,0,0.31264528888277709,0.31264528888277709,0,0,0,0.7940497079398483,0.7940497079398483,0,0,0,0.63892868394032121,0.63892868394032121,0,0,0,0.6406236351467669,0.6406236351467669,0,0,0,0.59127854276448488,0.59127854276448488,0,0,0,0.64396128780208528,0.64396128780208528,0,0,0,0.56184128182940185,0.56184128182940185,0,0,0,0.0042087731417268515,0.0042087731417268515,0,0,0,0.84536602254956961,0.84536602254956961,0,0,0,0.58256440726108849,0.58256440726108849,0,0,0,0.93653872376307845,0.93653872376307845,0,0,0,0.81528985546901822,0.81528985546901822,0,0,0,0.88000081083737314,0.88000081083737314,0,0,0,0.28095462964847684,0.28095462964847684,0,0,0,0.88322725705802441,0.88322725705802441,0,0,0,0.27093278616666794,0.27093278616666794,0,0,0,0.18264273856766522,0.18264273856766522,0,0,0,0.29692412703298032,0.29692412703298032,0,0,0,0.3418467310257256,0.3418467310257256,0,0,0,0.4536029954906553,0.4536029954906553,0,0,0,0.67398465401493013,0.67398465401493013,0,0,0,0.052605430595576763,0.052605430595576763,0,0,0,0.47517940355464816,0.47517940355464816,0,0,0,0.68724934570491314,0.68724934570491314,0,0,0,0.33995116245932877,0.33995116245932877,0,0,0,0.64324260875582695,0.64324260875582695,0,0,0,0.074367279186844826,0.074367279186844826,0,0,0,0.72185749071650207,0.72185749071650207,0,0,0,0.21699393633753061,0.21699393633753061,0,0,0,0.89831688092090189,0.89831688092090189,0,0,0,0.064317746087908745,0.064317746087908745,0,0,0,0.32604884682223201,0.32604884682223201,0,0,0,0.44478787528350949,0.44478787528350949,0,0,0,0.3598820639308542,0.3598820639308542,0,0,0,0.44062275299802423,0.44062275299802423,0,0,0,0.1463774056173861,0.1463774056173861,0,0,0,0.55819621728733182,0.55819621728733182,0,0,0,0.3269325268920511,0.3269325268920511,0,0,0,0.38293791702017188,0.38293791702017188,0,0,0,0.93523805565200746,0.93523805565200746,0,0,0,0.6194994580000639,0.6194994580000639,0,0,0,0.41256729303859174,0.41256729303859174,0,0,0,0.28128216206096113,0.28128216206096113,0,0,0,0.35554746375419199,0.35554746375419199,0,0,0,0.98425883450545371,0.98425883450545371,0,0,0,0.98753017093986273,0.98753017093986273,0,0,0,0.32574963266961277,0.32574963266961277,0,0,0,0.84996243612840772,0.84996243612840772,0,0,0,0.71448695892468095,0.71448695892468095,0,0,0,0.83717783610336483,0.83717783610336483,0,0,0,0.13265573745593429,0.13265573745593429,0,0,0,0.59764645877294242,0.59764645877294242,0,0,0,0.58612006762996316,0.58612006762996316,0,0,0,0.98932622163556516,0.98932622163556516,0,0,0,0.99582664831541479,0.99582664831541479,0,0,0,0.28915034187957644,0.28915034187957644,0,0,0,0.37861383124254644,0.37861383124254644,0,0,0,0.083830688614398241,0.083830688614398241,0,0,0,0.68069487088359892,0.68069487088359892,0,0,0,0.81762564880773425,0.81762564880773425,0,0,0,0.25156953302212059,0.25156953302212059,0,0,0,0.31937584187835455,0.31937584187835455,0,0,0,0.023829359328374267,0.023829359328374267,0,0,0,0.31884078169241548,0.31884078169241548,0,0,0,0.26989340479485691,0.26989340479485691,0,0,0,0.38073810539208353,0.38073810539208353,0,0,0,0.94108616560697556,0.94108616560697556,0,0,0,0.66607572534121573,0.66607572534121573,0,0,0,0.77652183570899069,0.77652183570899069,0,0,0,0.48009983100928366,0.48009983100928366,0,0,0,0.47512594377622008,0.47512594377622008,0,0,0,0.29835009109228849,0.29835009109228849,0,0,0,0.070141894510015845,0.070141894510015845,0,0,0,0.70230912184342742,0.70230912184342742,0,0,0,0.46119750523939729,0.46119750523939729,0,0,0,0.77122196811251342,0.77122196811251342,0,0,0,0.24266755115240812,0.24266755115240812,0,0,0,0.49770484212785959,0.49770484212785959,0,0,0,0.78018786199390888,0.78018786199390888,0,0,0,0.52403736137785017,0.52403736137785017,0,0,0,0.93604217446409166,0.93604217446409166,0,0,0,0.10790254827588797,0.10790254827588797]},"combos":{"overall_e1":{"effects":1,"placebo":0,"trends_lin":false,"yatchew":false,"result":{"n_effects_actual":1,"n_placebo_actual":0,"rownames":"Effect_1","estimate":2.1269141067154136,"se":0.79076780340031538,"ci_lo":0.37168139867287264,"ci_hi":3.4714342282698079,"n_per_horizon":200,"bw_per_horizon":0.3479360049102313,"n_within_bw":70,"qug_t":1.2071089207450889,"qug_p":0.45308140010707498,"event_id":1}},"event_e2_p2":{"effects":2,"placebo":2,"trends_lin":false,"yatchew":false,"result":{"n_effects_actual":2,"n_placebo_actual":2,"rownames":["Effect_1","Effect_2","Placebo_1","Placebo_2"],"estimate":[2.1269141067154136,1.8064721619204898,0.14782554804321557,-0.12464821038665075],"se":[0.79076780340031538,0.81914070416617213,0.76809594013652327,0.62616309196737818],"ci_lo":[0.37168139867287264,0.13505829375509903,-1.4668882276026507,-1.1555601162388842],"ci_hi":[3.4714342282698079,3.3460308506280505,1.5439925310753864,1.2989541011697208],"n_per_horizon":[200,200,200,200],"bw_per_horizon":[0.3479360049102313,0.31075802680500864,0.25168447849572612,0.44777421770361608],"n_within_bw":[70,54,39,96],"qug_t":[1.2071089207450889,1.2071089207450889,"NA","NA"],"qug_p":[0.45308140010707498,0.45308140010707498,"NA","NA"],"event_id":[1,2,-1,-2]}},"event_e2_p2_yatchew":{"effects":2,"placebo":2,"trends_lin":false,"yatchew":true,"result":{"n_effects_actual":2,"n_placebo_actual":2,"rownames":["Effect_1","Effect_2","Placebo_1","Placebo_2"],"estimate":[2.1269141067154136,1.8064721619204898,0.14782554804321557,-0.12464821038665075],"se":[0.79076780340031538,0.81914070416617213,0.76809594013652327,0.62616309196737818],"ci_lo":[0.37168139867287264,0.13505829375509903,-1.4668882276026507,-1.1555601162388842],"ci_hi":[3.4714342282698079,3.3460308506280505,1.5439925310753864,1.2989541011697208],"n_per_horizon":[200,200,200,200],"bw_per_horizon":[0.3479360049102313,0.31075802680500864,0.25168447849572612,0.44777421770361608],"n_within_bw":[70,54,39,96],"qug_t":[1.2071089207450889,1.2071089207450889,"NA","NA"],"qug_p":[0.45308140010707498,0.45308140010707498,"NA","NA"],"event_id":[1,2,-1,-2],"yatchew_t":[-0.3860467240352603,-0.14076057173257514,0.59031090522968133,-0.95304161844462998],"yatchew_p":[0.65026896744264939,0.5559704539677186,0.27749111477648825,0.82971550784324333],"yatchew_n":[200,200,200,200],"yatchew_sigma2_lin":[0.53182096502061016,0.514253029990139,0.4717377015571827,0.44339821445724054],"yatchew_sigma2_diff":[0.54519362231757829,0.51916819398923952,0.45381972295000866,0.47145576018102153]}},"event_e2_p2_trendslin":{"effects":2,"placebo":2,"trends_lin":true,"yatchew":false,"result":{"n_effects_actual":2,"n_placebo_actual":1,"rownames":["Effect_1","Effect_2","Placebo_1"],"estimate":[2.2646671869090471,2.083684968393162,-0.11479553641777721],"se":[1.5138131549655991,2.2230144313212046,0.85830727653827543],"ci_lo":[-1.1365816544759328,-2.4852201194371224,-1.6983907344925793],"ci_hi":[4.7974568716351174,6.2288363255675749,1.6661119648747802],"n_per_horizon":[200,200,200],"bw_per_horizon":[0.28079836038524775,0.2604643566318115,0.23090036781225204],"n_within_bw":[46,40,34],"qug_t":[1.2071089207450889,1.2071089207450889,"NA"],"qug_p":[0.45308140010707498,0.45308140010707498,"NA"],"event_id":[1,2,-1]}},"event_e2_p2_yatchew_trendslin":{"effects":2,"placebo":2,"trends_lin":true,"yatchew":true,"result":{"n_effects_actual":2,"n_placebo_actual":1,"rownames":["Effect_1","Effect_2","Placebo_1"],"estimate":[2.2646671869090471,2.083684968393162,-0.11479553641777721],"se":[1.5138131549655991,2.2230144313212046,0.85830727653827543],"ci_lo":[-1.1365816544759328,-2.4852201194371224,-1.6983907344925793],"ci_hi":[4.7974568716351174,6.2288363255675749,1.6661119648747802],"n_per_horizon":[200,200,200],"bw_per_horizon":[0.28079836038524775,0.2604643566318115,0.23090036781225204],"n_within_bw":[46,40,34],"qug_t":[1.2071089207450889,1.2071089207450889,"NA"],"qug_p":[0.45308140010707498,0.45308140010707498,"NA"],"event_id":[1,2,-1],"yatchew_t":[0.28369014930529912,0.5341593625896125,-0.35106469531645984],"yatchew_p":[0.38832392222027801,0.29661564095268655,0.63723009331070279],"yatchew_n":[200,200,200],"yatchew_sigma2_lin":[1.5161518182216591,3.4154932715084674,1.3999333548858246],"yatchew_sigma2_diff":[1.4879167201537093,3.2907284926232125,1.4335147695614252]}}}},"beta22_G200_F4_T5":{"name":"beta22_G200_F4_T5","G":200,"F":4,"T":5,"dose_distribution":"Beta(2, 2)","seed":20260426,"panel":{"g":[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,20,20,20,20,20,21,21,21,21,21,22,22,22,22,22,23,23,23,23,23,24,24,24,24,24,25,25,25,25,25,26,26,26,26,26,27,27,27,27,27,28,28,28,28,28,29,29,29,29,29,30,30,30,30,30,31,31,31,31,31,32,32,32,32,32,33,33,33,33,33,34,34,34,34,34,35,35,35,35,35,36,36,36,36,36,37,37,37,37,37,38,38,38,38,38,39,39,39,39,39,40,40,40,40,40,41,41,41,41,41,42,42,42,42,42,43,43,43,43,43,44,44,44,44,44,45,45,45,45,45,46,46,46,46,46,47,47,47,47,47,48,48,48,48,48,49,49,49,49,49,50,50,50,50,50,51,51,51,51,51,52,52,52,52,52,53,53,53,53,53,54,54,54,54,54,55,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58,58,58,58,58,59,59,59,59,59,60,60,60,60,60,61,61,61,61,61,62,62,62,62,62,63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,67,68,68,68,68,68,69,69,69,69,69,70,70,70,70,70,71,71,71,71,71,72,72,72,72,72,73,73,73,73,73,74,74,74,74,74,75,75,75,75,75,76,76,76,76,76,77,77,77,77,77,78,78,78,78,78,79,79,79,79,79,80,80,80,80,80,81,81,81,81,81,82,82,82,82,82,83,83,83,83,83,84,84,84,84,84,85,85,85,85,85,86,86,86,86,86,87,87,87,87,87,88,88,88,88,88,89,89,89,89,89,90,90,90,90,90,91,91,91,91,91,92,92,92,92,92,93,93,93,93,93,94,94,94,94,94,95,95,95,95,95,96,96,96,96,96,97,97,97,97,97,98,98,98,98,98,99,99,99,99,99,100,100,100,100,100,101,101,101,101,101,102,102,102,102,102,103,103,103,103,103,104,104,104,104,104,105,105,105,105,105,106,106,106,106,106,107,107,107,107,107,108,108,108,108,108,109,109,109,109,109,110,110,110,110,110,111,111,111,111,111,112,112,112,112,112,113,113,113,113,113,114,114,114,114,114,115,115,115,115,115,116,116,116,116,116,117,117,117,117,117,118,118,118,118,118,119,119,119,119,119,120,120,120,120,120,121,121,121,121,121,122,122,122,122,122,123,123,123,123,123,124,124,124,124,124,125,125,125,125,125,126,126,126,126,126,127,127,127,127,127,128,128,128,128,128,129,129,129,129,129,130,130,130,130,130,131,131,131,131,131,132,132,132,132,132,133,133,133,133,133,134,134,134,134,134,135,135,135,135,135,136,136,136,136,136,137,137,137,137,137,138,138,138,138,138,139,139,139,139,139,140,140,140,140,140,141,141,141,141,141,142,142,142,142,142,143,143,143,143,143,144,144,144,144,144,145,145,145,145,145,146,146,146,146,146,147,147,147,147,147,148,148,148,148,148,149,149,149,149,149,150,150,150,150,150,151,151,151,151,151,152,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,155,156,156,156,156,156,157,157,157,157,157,158,158,158,158,158,159,159,159,159,159,160,160,160,160,160,161,161,161,161,161,162,162,162,162,162,163,163,163,163,163,164,164,164,164,164,165,165,165,165,165,166,166,166,166,166,167,167,167,167,167,168,168,168,168,168,169,169,169,169,169,170,170,170,170,170,171,171,171,171,171,172,172,172,172,172,173,173,173,173,173,174,174,174,174,174,175,175,175,175,175,176,176,176,176,176,177,177,177,177,177,178,178,178,178,178,179,179,179,179,179,180,180,180,180,180,181,181,181,181,181,182,182,182,182,182,183,183,183,183,183,184,184,184,184,184,185,185,185,185,185,186,186,186,186,186,187,187,187,187,187,188,188,188,188,188,189,189,189,189,189,190,190,190,190,190,191,191,191,191,191,192,192,192,192,192,193,193,193,193,193,194,194,194,194,194,195,195,195,195,195,196,196,196,196,196,197,197,197,197,197,198,198,198,198,198,199,199,199,199,199,200,200,200,200,200],"t":[1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5],"y":[-1.2481976087462026,0.33516584772394142,-0.69652385632476399,0.99481955049519266,1.3086033810376629,-1.0004767215467067,-0.65773183371464561,-0.4315030051462877,0.3001880706575516,0.23189306251210234,-0.8376848656398912,-0.82292030328775334,-0.28230423676302774,0.81728189946473384,1.3005960414853504,-1.3502556720219618,-0.47835914308532312,-0.37463307000886414,-0.46602161224042171,-0.59005575393753773,0.028564281625720667,-0.88771009250153943,-0.20390652055351802,-0.071423250519627682,0.065972548078413198,-0.78405095207953124,-0.94481072249866949,-0.85774650964545018,-0.70253032746035804,-0.047194880800427774,-1.055080381556512,-1.0410501417555875,-1.7885232449922268,-2.2132131009363274,-1.1891579908265313,0.018178826348799526,0.78263229846998406,0.68434859123845748,2.2177275997525516,2.7430242116121279,0.45557229061822152,0.55710780061696152,0.50019518770008498,2.5450702287524081,2.5278587267975245,-1.2683095304528753,-0.62260368239352559,-1.1276085654404513,0.023810053334439196,-0.033906943339145235,0.10441246827896644,1.0726580754030139,0.71493404859853915,3.0928713760761513,2.8436616267164281,-0.24034275410595177,0.60845095334667154,0.30448286162443072,0.20373526294868688,-0.34365890656108378,-0.6455934884985588,-0.53743505521753243,0.066315540321114863,0.52065053281021534,1.1102564578541951,0.096150240463075298,0.37485581479640329,1.0951452368408456,1.9874044335393166,2.185680229197867,1.1190297127267588,1.4434429001925579,-0.088660852996109707,1.8363785749684152,1.3380918029888837,0.5085813008275446,0.95221726886404601,0.17410374176644855,1.543942418097167,2.4642600942534871,2.0028153164321139,1.6326643654005797,2.2835342572523967,2.4458689543045917,2.7508775237968455,-0.70292159302829493,-0.14311746900120936,-0.63308429161201563,0.52829262100451624,1.800364283178876,-0.48366458294384529,-0.79882159903028249,-1.0285938933054779,-1.0198932929577933,0.046556144619932627,-0.8851144049251054,-0.9024119262896737,0.026866495585643912,0.8237796937369376,-0.0057303811938971272,1.4719572466536572,0.5515278851769041,1.6792613525207043,2.0354893395482843,2.0482662230876265,-0.15238359521482803,-0.29506468654208273,-0.60387260052811298,1.048580097335539,0.35848095529212609,-0.66318094919300075,-0.64515823715742859,0.014444696178840456,0.79626880141984835,1.6631414411033743,-1.2639262803387092,-1.1835270578404933,-0.38533568941590096,1.3159445818259305,1.7855471004641748,-1.1673564651151553,-1.2629550170641282,-0.6429883142754762,0.29514996457248521,0.61620630081926719,-1.5254309508163653,-1.4258139830118861,-1.5121360951500606,-1.3059896611888886,-0.38341637721483612,-0.87935392708567661,-0.38451746797533626,0.2802679163799397,1.5745945268749755,1.3375749082416823,-2.2044387457976078,-1.5902724152012642,-1.2392130258801251,-1.1486943542271513,-0.66944739579138224,1.1556871702279972,1.5559811987842804,1.7356153110963368,2.8691228941067246,2.961143528322006,0.51344990766209686,0.63378545842404399,1.0179610387767823,1.2054179064227619,2.0766059948239746,-0.64593276633740082,0.27021022898658781,-0.12959156162073385,2.0088359655611048,2.8656126090075849,-0.25721026043614276,-0.0027665278429818696,0.35739226466389351,0.90565201378479554,1.3305360384727376,0.5815556176967529,0.051256914787265462,0.28792708586127852,1.7176882441987082,1.9163371337632373,1.191989864627049,2.098844901228754,1.7983221007861776,2.751686365422537,1.6388815015501921,-0.17660193347076514,0.12645375382791091,-1.1696045450552746,0.67572287511082929,0.51387650799712181,-1.5720107653850905,-1.253076447703745,-1.6035084306997962,-0.45928593655124794,-0.45337511757089466,-0.99172763891795135,-1.1616092623557612,-0.30170617241914544,0.030643368286223105,0.22593292890313876,-1.397064855932898,-1.8333627581029046,-1.1341108608339916,-1.4721463347535537,-1.1905307070583913,1.8606948369300516,0.70745583448155402,1.689898661431632,1.599530376390456,2.6487328427741126,-0.38104491110105698,-0.19458545814007269,-0.2838234951212053,1.295467568852009,1.5665367191248283,0.056665459499193771,-0.67715242221320571,0.1999580968188126,1.9310020963530106,2.3866477976966571,0.99012105023405561,1.7173799128274494,1.5511713537636727,3.8741187763217377,3.4986356783047334,0.83355800668779234,0.66766952459316753,-0.062393515415196998,1.7568451905682712,2.0805964730103561,0.85863528176647919,0.81463113735936332,0.82877997978309037,2.974857817652532,3.4134557539414376,0.54471046770607567,1.0814469930703536,1.2929979911574008,1.6025105654523446,1.6314010283887113,-2.0118095335669599,0.18601080155299154,-1.1085017465513451,1.001269534472444,-0.31120576282799234,-1.850534748102437,-1.3754303723655816,-1.4479026590895108,-0.33978024145481073,0.53806243440527279,-0.6858809615273942,0.1813768921787921,1.0455448576319657,1.7040507740198476,0.8912588974359722,0.084855938609760456,0.16655334847406494,-0.20098228442447785,0.89297939925607217,1.3105696416177675,-1.3954730378394178,-0.012837190898052131,-0.5257320356502212,1.6521333121819344,1.0186979475937883,-0.45760869361396567,0.14564516083015899,0.20269362835951343,0.68348682521232529,0.60393541533189976,0.6333722886806642,0.26581495085304097,0.7914197039257711,0.90512246074798852,0.35213448652850476,0.81891694774334134,1.4785405574192825,0.33540938725035563,0.32414020881383232,1.6980860104991835,-0.13201085785916133,0.49096012354139301,0.80094213619371035,1.6731291405837734,1.9252043202017082,-0.49245871773978167,-1.1113472106530158,-0.53263816503133998,-0.52171082756533094,-0.67174187114820716,0.86955011477678135,0.14439821517716223,-0.17144083586836256,0.7320635545462697,1.733511304642033,1.2725462170617887,1.404058001479332,1.3836854961629701,2.4422009723602685,2.9003732833548543,0.85528424453809271,0.79930272828428295,1.0051824094098012,2.0398008824835046,1.9132994245896371,0.26060055843434327,0.87027136534179805,-0.28806290043680605,1.2533242617933662,0.63850203526343796,2.1520691295681904,2.2913647332895661,1.6085328404182881,2.903327383984402,2.9081442006879406,-0.088357991012390447,0.044575070775824677,1.5853198399567283,1.5361550030724807,1.90052310058147,0.61855251924503207,0.78543994824635566,0.60757089823806376,0.80035267101685004,2.0456755721588635,-1.5934198868257219,-0.52874298267771747,-0.9989058314188497,-0.41595080080510688,-0.17715096000175024,3.0086321313483277,2.4899020246544383,2.1935078549366844,4.7252145079533108,4.185625240872425,-2.4336434497477382,-2.2761897656209191,-1.7389434320135764,0.043222635151202407,-0.45234920522818844,-1.9730852823962448,-1.7005688275691169,-1.2824533799159654,-1.549328495193238,-0.58552336263293636,-2.2733963370132528,-0.48718262816479374,-0.81241864789718732,0.17617986638810412,1.5121188723609746,-0.59289045858785883,-0.16688579429424005,0.47547455137687733,1.4971749503770426,0.94387310250483947,-1.5216915267905122,-2.439870058991469,-1.4004214553093799,0.17052992790508759,0.28352320959459076,-0.67730338336495155,0.55922345382229333,-0.82547662059432703,1.2430433682089799,1.5528113915461437,0.79966485677075783,0.78167347729280134,0.81333938686238549,3.3325529847150905,2.3766601105217164,-0.25147321922443222,0.54026528428936482,1.1159509902194826,2.3054192439871608,1.6990113483427287,-0.27691375493956033,0.3864993117091724,0.43200515983271959,1.0172052469181101,0.10846978470500179,0.71464840828685028,-0.16861515118192708,0.36639612454336379,1.9265106835178747,1.5448040977312405,0.036061354530013412,0.62624106779751854,0.72195930267385888,0.63441430960103529,0.91031938777809751,-0.059301327510256054,0.39133510872254501,1.2142866597818749,1.4061460660228604,2.372014601491566,-0.45026539137776472,0.44240187099163208,1.0247655939597928,1.9188968787588574,1.296141612508338,0.11947689088530847,0.61062231915730913,-0.46975615084180572,2.2944591546281767,1.0245512988097498,-0.66398350181665688,0.27096103627078699,-0.094155791413704365,0.57146759578052098,0.51252122395326793,-1.6975901909232816,-2.1934008831032177,-2.7543507490457331,0.096249174864772691,0.060294954417971725,0.59369102548196495,1.1425148239644107,-0.27664132447310785,0.68785842918259732,2.0794818828276336,-0.25207511934079668,-1.0921119967534119,-0.68923973019036167,0.67366191153410548,-0.36791286774715837,-0.583831739107046,-0.26256617306279123,-0.72963312511088674,0.39308276948394133,0.49760119172778333,-0.47002302829063258,-1.1914598742823181,-0.19488737921408747,0.28826726978624106,0.19684760068969775,2.0649857282926258,1.4895858347377244,0.31282366175466847,2.8968538105663941,1.4674912439397978,-1.8736729105612615,-0.8050931912842817,-1.865255547767271,-1.0386457891598155,-0.43319157418782417,-0.34249604952935203,0.20291779245900834,-0.88581143620954617,1.356930225919589,1.7017764546445333,1.3530657464626896,1.2528698391829958,2.217085314892445,3.1135217959725447,2.8248403427448801,0.96406300840570069,-0.39969764368903515,1.533864061298591,2.0598398361902954,2.8314597657149747,1.5772564416944184,1.1016399601183142,1.8711572519750046,1.4332046762259936,2.0474292872353304,1.5663253116513769,1.5070162017273478,1.3306908005699865,2.1412556453858924,2.7723043140759875,1.743658936172696,0.76333961168977238,0.68668803160840697,1.5370869992770846,1.1470077815211224,1.7601728162220449,2.4520809690248035,2.0601506150732134,3.0019120280526295,4.233609777959269,1.7321246267762405,2.292385561552674,2.4309657004227243,3.6534572022087173,3.5010754538339395,0.8881711901625492,0.58099202937506389,0.72875823146273189,1.9050278132594007,2.397042335541951,-1.0863816007510803,0.17325377077186388,-0.89707912000465351,-0.37414814614686709,1.9590124496879637,1.2568182623530482,1.250840674800513,0.90810133096008927,2.7996386323958706,2.4318729000078543,0.35186948296101789,1.4697771129936501,1.2418220432009219,1.3649052646408537,1.1071148630578964,-0.91846261667407381,-1.279407072475272,-1.2524460289323094,-0.11936073104543676,-0.97496284697432001,0.74840441873161911,0.60200647306480704,1.385079522765795,1.924029481381619,2.9689701341734493,-0.71115208452455825,-1.1608257400969801,-1.5289056219692869,0.35726838688084772,1.0191337026394651,-0.237940621123922,-0.3727075168973259,0.28556022785151192,1.8806507993854584,1.1594203483874297,0.66829263614986023,0.27760230229839233,1.6893196530720385,1.9819751380347164,2.4138180600932944,0.24117614411548333,0.71079437481623264,0.71528329155413095,2.9314625109010883,3.1532715318150135,0.050692772224158078,0.29404796193604865,-0.15279830022379237,1.8452176310253461,0.82107675325223783,-0.79452472152624321,-0.29470283734244423,-0.16573023912301763,1.243853825880052,2.0488688057082034,-0.20352842932273285,-1.0262299634971981,-1.132846927646423,1.1445185753725908,0.31722678126794435,1.2531137862751569,2.2999110902912365,1.5722966468464741,3.6472468883687101,3.8387726322732001,-0.63610662182459099,-0.67043529532994439,-0.34971725104818363,-1.0863248432721666,0.96019989202626443,0.65707225030358218,0.26050857928371079,0.78486373716247426,1.1792195908819139,1.8127637670829475,0.25807407419333905,-0.069074345702453188,-0.036382374836353301,-0.46948361540101835,0.63181504895874019,0.45257097313350225,0.93448356848648051,0.42688005194625783,0.41382513326525405,0.044039217725413415,-0.90358407346735725,-0.8049009636272787,-0.88445361639369424,-1.3978977253486644,-1.5391831722150131,-1.256337202988175,-0.86647411391015861,-0.87299606967883703,-1.0225361258748058,-0.07312642077275644,-1.2984337773074808,-0.91477231929239433,-0.26368643560102623,0.44573676744159851,-0.44039226412892574,0.63579151922199462,-0.15796488613324261,0.46914641319149325,2.271979516733559,2.0201841556135358,0.020089354184026798,-1.2698054586157439,-1.2948293144841745,0.66781032952596342,0.022787558994936952,-0.052238539157843968,0.67980060409033249,-0.58019698792930841,0.54318995234014389,-0.27009351978141738,0.84139405633626729,1.6079490418207574,1.6766957804523321,1.6519810502008343,1.2241542173438524,-0.11065740999238963,0.56375401586464746,0.54794908864074898,2.2395300122630153,2.7259804026413872,-1.558165015996646,-0.95590714552714184,-1.1241176692657437,-0.67993499530824897,-0.084364443445243853,-0.38583919996460037,-1.6642629825538426,-1.0003388034215617,-0.18282111241662163,-0.57016984310500507,-1.1124077333370228,-1.0219008688389666,-0.69216829792602441,-0.77499345088966753,-0.52765350719832016,0.027335240478659158,-0.12595014359066317,-0.73750258962417892,1.1316611935249221,2.0201813315804844,0.72938590076809229,0.34783549773903744,0.49450336004966577,2.5902528936855815,2.7169304379293573,1.1082414259799611,0.58063547064338528,0.33765583485871259,2.9649071626568841,3.0365409435460746,-0.69390740180366128,0.3783022733157555,1.241591870549887,1.1236443763177779,2.2691675083806628,-0.30581531376165921,0.018974594663268035,1.3714461021503519,1.6483655766914003,2.3228015650846197,-0.31128324860010148,-0.75152442018096255,-0.53051125483126471,1.2270365191070793,1.0765617444703461,-1.3861900394307523,-0.79615840716968422,0.16343573158786984,2.3656947078268447,2.3553745939617752,0.81785495012133325,0.32361085569433601,0.14382632669462619,2.1907688028893531,2.8606759113980589,-1.8857471798431993,-1.7289767541039072,-1.724031889335675,-0.60369081000779645,0.79182932612838697,0.76055403572738811,-0.31164444523453672,1.0892943447657502,2.223903981600369,1.2130561935222504,1.4851140222134291,1.3996868773436038,1.8926797599701637,2.2371831882421955,2.4243755349265497,-0.053884105590754683,0.91215512365181262,0.77942341981835761,1.6583145751990254,1.7746487393532757,-0.41482919988521033,-0.57473014870117589,-0.013741987960638536,1.4218052015207623,1.6104259915501506,0.25256249835800709,0.45772620490953836,0.65022894417318078,1.49557758007084,1.1517434479956976,-0.51251954179099324,-1.1442087679922124,0.5623477752147813,0.52824922354305393,1.1573126660306312,-1.0098852833490941,-0.88826385169033495,-0.89886968509236465,0.23521106421110954,0.99446893681204629,-0.79657091580369566,0.079762050484129765,-0.49613229077185189,0.78345297330005803,1.511676308270453,0.086131226013897028,0.58859163434457396,0.12022050781125446,0.85977643778571966,0.4722486655693926,-0.83668450051128918,-1.8366912845731826,-0.04176436332279071,0.74418085683442192,0.5007696183653727,-1.0687793055305099,-0.16377930614451786,-0.32421454692389873,1.2211974471756524,-0.51573918843868605,-1.1199571669646948,0.33670949526572463,-0.24890888403455635,0.43233465448435815,0.13563251787450759,0.54631062826616872,0.088130176363804075,1.3053148117038953,1.0518088766860052,1.0823960154556733,1.3643141509806356,0.71460326330250712,1.096421389015785,2.3747468330942034,2.3472132431533548,0.22575274376357346,1.174890215104915,-0.4977747178692562,2.5291028024242923,1.8902241490779237,0.4226819254333572,0.77689408354182876,0.59158229359482339,1.441108361062946,1.932626447847746,-0.13412823529455825,-0.44384676375720677,0.43409123354743778,1.9456302838065875,0.57249703729177093,-2.0612967322929663,-0.70258713364277148,-0.95172730827486995,0.56078266579535885,0.84076065809156131,0.51391744981154752,0.75904353313058159,0.77129208868264032,1.6622510133753274,2.4302784690637442,0.52425162806585679,0.40333035150970198,0.51896591565285743,0.42777897913620133,1.2758221037365844,1.0644323324767546,2.6551022650194951,2.1014939712284928,1.3175290508753494,2.7810738217449957,1.0619239284868212,0.81752018057352593,0.90875879733858156,1.9820195197315567,1.7969073047307278,-1.0645662185741833,-0.0080850871212820863,-0.71211084893844689,0.48602976224860556,-0.39503677138969384,-0.71477424230635378,-0.21421583127575125,-0.39404418187009888,0.172437290035868,0.090756583554885251,-0.89240231501570544,-1.8590800221969936,-1.228847762154252,-1.286214236844561,-1.2012778766298531,-1.1339395231089875,-1.1109385627766235,-1.0596755690905426,1.3973240530936211,0.80601500772377421,-0.90362244067578312,0.13012161823134427,-0.16201105285358922,1.1271661338677399,1.9773810772124261,-0.2430149596229946,-0.46990600868988858,-0.39483335363482386,0.51521489547301536,1.7628108989841214,-2.6138627768413256,-2.2925144620440117,-1.6114827149624802,-0.14854169196674633,-0.32540134770395046,0.1993860056784037,1.6386614677708162,0.015289623449104561,0.84135676472840959,1.3557738924368592,0.29397308510538045,0.30843133361873076,0.94953480861035799,0.80060575770706432,0.84079508990119456,1.6296313470661528,1.6213365011382022,1.6477307312347591,2.1839016617438372,2.0847525104713824,0.95443819096590521,-0.062488692828586323,1.1639844348527748,1.5642176659782099,1.1368383548870373,0.91137265014016333,1.3454295478199438,0.86711120187423507,2.3398397782596345,2.116021297720069,0.6632787541254368,1.1090750975390387,0.75904530397378223,3.0069035252614436,2.6842787565007384,0.41144886609561426,0.17190690274420117,0.38511500298196422,0.20978971741721741,0.67151903150054315,1.1404502045008691,0.60356153217516495,0.95186328864210568,0.67075458147829581,0.94481983896720034,1.1406813127101239,0.66856158479569094,-0.21180316573365388,1.4787127942334937,2.3395131318350373,-0.97840388508320775,-1.0133686812905278,-0.59872564914944404,0.08922795625694252,0.68073957803848706,-0.87642229351117906,-1.0131314107134222,-0.71364402653740866,0.28360871077198135,0.064451669523087923,1.953773957216373,1.8104150992831904,2.1604489474344342,2.2361049740909427,2.2627368602533533,-1.2002144379609423,-1.8232480317293354,-1.7382592536786716,0.031093914406408241,-0.075822930546832465,1.5411254367536111,0.72893536550964722,0.84554966511548824,0.91348966526838571,1.2297621676797927,0.27908720959562128,0.27861999438396068,0.32265831885826518,0.49914574231361264,0.78085978305218673,-2.8095539198611674,-1.0753757798822003,-1.1808033075579927,-1.5657563315987839,-1.3559297198750562,0.47516189340835302,0.17361724603430811,0.35736518182179966,1.0602708669839915,2.1908133149863516,0.084705260915975855,-0.78889487132225278,-0.24101215862291697,-0.86774554319558872,-0.75211933742936887,-1.1333204035257338,-0.96780574899574145,-0.63257252379064721,1.4168781503987036,0.98572896421423783,-0.27616048371959495,-0.2891995458635197,-0.59524373567006705,0.18600496699353111,1.2738551512833147,0.43432304838334379,1.6735083806429378,2.4702308013061867,2.8691084423613296,2.6435469912350471,-1.1266087123965263,-1.1950223223570473,-0.58573689518592542,1.0695167578939189,0.044982030705013998,0.36920482845698305,-0.33108897746365751,1.4057858908172136,0.64614631426424252,0.71226572458494963,0.3904362512248335,1.0936356199373507,1.0684611550321248,2.9100538386257555,1.6307428027215631,0.50585428796135234,1.3510818043986026,0.92101302322478884,2.5161400841538635,1.9310149656361628,-0.93722093962854314,0.48304595881472945,-0.098262558713369835,0.62016824973705476,0.28364681085637566,-0.72114403465326449,-0.66492929653054955,-1.2494259612997998,1.6082225287636243,0.52042000718756576,-1.8422731379209563,-1.6793582335225232,-1.695628927765557,-0.20493742296308048,-0.35927713541735884,-1.4065741941768097,0.046266194331139543,-1.2357305859211771,0.66391549742252243,-0.091358757836074528,-2.7845709928205991,-2.7481892838321631,-2.8123363614964974,-2.1027337928082237,-2.090419362959913,1.8969356493980545,1.8362949085100533,2.6618696766671874,2.9226108583687327,2.3685110025147464,0.44187667965561089,0.19227599536229262,0.29372098338371622,0.90413779176689346,1.3883603384274945,1.9204086321170943,2.7143946848726337,1.8295816515592458,3.5745409155534631,2.9527392279432418,1.1823146804141853,0.92591622124172779,0.31488077776595086,0.86932550011432352,1.0971077119561787,0.49794454256171528,1.0789497470804525,0.45613060520636628,2.1336286088636456,1.8491520863175697,-2.0765911475142951,-1.2279788742726507,-2.057462417269639,-1.8225674429032974,-0.17905378130755434,-3.3447081475340923,-2.9094782633837362,-2.1122307886116047,-1.784503065184458,-1.1035561089226626,-1.4587535060589185,-0.60424953548934945,-1.06462103623018,0.28103936564662918,0.010573262301477454,1.6398519238144524,1.369551128214791,0.68173825117349918,1.928408218813096,1.9650788047231131],"d":[0,0,0,0.68181400005741655,0.68181400005741655,0,0,0,0.31799381735879662,0.31799381735879662,0,0,0,0.78255050448428121,0.78255050448428121,0,0,0,0.32074598368068274,0.32074598368068274,0,0,0,0.19662821686638318,0.19662821686638318,0,0,0,0.40001345385313497,0.40001345385313497,0,0,0,0.3957156236541175,0.3957156236541175,0,0,0,0.50903501114156091,0.50903501114156091,0,0,0,0.79295055066466591,0.79295055066466591,0,0,0,0.30875056498130976,0.30875056498130976,0,0,0,0.7501873413569482,0.7501873413569482,0,0,0,0.24218977339368208,0.24218977339368208,0,0,0,0.58067466921959521,0.58067466921959521,0,0,0,0.88508741967800086,0.88508741967800086,0,0,0,0.21752738046626077,0.21752738046626077,0,0,0,0.69819403815086534,0.69819403815086534,0,0,0,0.47607591335014671,0.47607591335014671,0,0,0,0.9195646708171763,0.9195646708171763,0,0,0,0.21521054728895095,0.21521054728895095,0,0,0,0.14281518352792744,0.14281518352792744,0,0,0,0.7234038445276757,0.7234038445276757,0,0,0,0.74188912792698813,0.74188912792698813,0,0,0,0.33983347357693267,0.33983347357693267,0,0,0,0.94731573817043635,0.94731573817043635,0,0,0,0.87589867402101818,0.87589867402101818,0,0,0,0.32409784190869045,0.32409784190869045,0,0,0,0.82500372284988777,0.82500372284988777,0,0,0,0.48352966413464143,0.48352966413464143,0,0,0,0.49770010631830275,0.49770010631830275,0,0,0,0.53730593370702051,0.53730593370702051,0,0,0,0.94250865791175142,0.94250865791175142,0,0,0,0.64636032916609643,0.64636032916609643,0,0,0,0.3438600486356182,0.3438600486356182,0,0,0,0.38330788470287697,0.38330788470287697,0,0,0,0.72473784532168128,0.72473784532168128,0,0,0,0.41169756996027035,0.41169756996027035,0,0,0,0.59177100123752968,0.59177100123752968,0,0,0,0.38697411600759252,0.38697411600759252,0,0,0,0.74281397216139178,0.74281397216139178,0,0,0,0.9046135413035632,0.9046135413035632,0,0,0,0.70716004289259049,0.70716004289259049,0,0,0,0.35722935962639424,0.35722935962639424,0,0,0,0.68404648007619662,0.68404648007619662,0,0,0,0.90259214400932142,0.90259214400932142,0,0,0,0.64567063676211878,0.64567063676211878,0,0,0,0.70876240009285185,0.70876240009285185,0,0,0,0.19385820370389589,0.19385820370389589,0,0,0,0.77207305724232889,0.77207305724232889,0,0,0,0.66340953522444335,0.66340953522444335,0,0,0,0.60229708039383523,0.60229708039383523,0,0,0,0.39784068287641505,0.39784068287641505,0,0,0,0.29223611311526254,0.29223611311526254,0,0,0,0.2584048777773405,0.2584048777773405,0,0,0,0.66303748667265427,0.66303748667265427,0,0,0,0.19865777603454726,0.19865777603454726,0,0,0,0.35528333289598624,0.35528333289598624,0,0,0,0.72499645670040613,0.72499645670040613,0,0,0,0.70056572650796134,0.70056572650796134,0,0,0,0.26042795492472937,0.26042795492472937,0,0,0,0.58089192008751822,0.58089192008751822,0,0,0,0.64962346392572379,0.64962346392572379,0,0,0,0.44375679695954079,0.44375679695954079,0,0,0,0.58197428847885946,0.58197428847885946,0,0,0,0.77783189342997727,0.77783189342997727,0,0,0,0.83692508191353387,0.83692508191353387,0,0,0,0.097827054510372463,0.097827054510372463,0,0,0,0.78908518181303688,0.78908518181303688,0,0,0,0.44413967384951042,0.44413967384951042,0,0,0,0.56427184833098054,0.56427184833098054,0,0,0,0.68913473980778284,0.68913473980778284,0,0,0,0.77911420719239866,0.77911420719239866,0,0,0,0.50727966444713768,0.50727966444713768,0,0,0,0.093739394809058837,0.093739394809058837,0,0,0,0.73256810327626687,0.73256810327626687,0,0,0,0.13503183423658971,0.13503183423658971,0,0,0,0.61367088620216248,0.61367088620216248,0,0,0,0.27643090074459087,0.27643090074459087,0,0,0,0.41332856169901561,0.41332856169901561,0,0,0,0.36733896578561015,0.36733896578561015,0,0,0,0.67717347163524078,0.67717347163524078,0,0,0,0.51543057390447922,0.51543057390447922,0,0,0,0.44563268262322625,0.44563268262322625,0,0,0,0.30318455020930662,0.30318455020930662,0,0,0,0.15891503891291756,0.15891503891291756,0,0,0,0.51316654275126394,0.51316654275126394,0,0,0,0.12147577878556037,0.12147577878556037,0,0,0,0.53383107798501594,0.53383107798501594,0,0,0,0.30698188035891211,0.30698188035891211,0,0,0,0.7028640046423863,0.7028640046423863,0,0,0,0.39819772629724814,0.39819772629724814,0,0,0,0.37602995860399624,0.37602995860399624,0,0,0,0.54780863038631833,0.54780863038631833,0,0,0,0.67020136956642451,0.67020136956642451,0,0,0,0.66347153194934072,0.66347153194934072,0,0,0,0.56992702494466296,0.56992702494466296,0,0,0,0.20700602861035469,0.20700602861035469,0,0,0,0.62681708274603443,0.62681708274603443,0,0,0,0.57185389526626784,0.57185389526626784,0,0,0,0.30769953148275114,0.30769953148275114,0,0,0,0.71221149543553963,0.71221149543553963,0,0,0,0.81124367901642791,0.81124367901642791,0,0,0,0.66046031024061658,0.66046031024061658,0,0,0,0.71770427237154355,0.71770427237154355,0,0,0,0.94781683705753872,0.94781683705753872,0,0,0,0.60661432678707783,0.60661432678707783,0,0,0,0.37157174630435152,0.37157174630435152,0,0,0,0.44885486906555727,0.44885486906555727,0,0,0,0.58890910315427158,0.58890910315427158,0,0,0,0.49030235943522582,0.49030235943522582,0,0,0,0.59046313738396217,0.59046313738396217,0,0,0,0.25365175748083907,0.25365175748083907,0,0,0,0.31920269521883093,0.31920269521883093,0,0,0,0.023426015704313328,0.023426015704313328,0,0,0,0.4165301942363363,0.4165301942363363,0,0,0,0.53943972192149325,0.53943972192149325,0,0,0,0.85392499215898077,0.85392499215898077,0,0,0,0.28131369496626285,0.28131369496626285,0,0,0,0.25696897237161198,0.25696897237161198,0,0,0,0.26908486049963776,0.26908486049963776,0,0,0,0.77154907275591744,0.77154907275591744,0,0,0,0.23151129645378515,0.23151129645378515,0,0,0,0.38508512070729789,0.38508512070729789,0,0,0,0.37011455219818395,0.37011455219818395,0,0,0,0.33309692425581661,0.33309692425581661,0,0,0,0.83767245549216007,0.83767245549216007,0,0,0,0.88479353864917754,0.88479353864917754,0,0,0,0.61831854327917679,0.61831854327917679,0,0,0,0.88131599852240761,0.88131599852240761,0,0,0,0.74046029072372455,0.74046029072372455,0,0,0,0.93000108869400822,0.93000108869400822,0,0,0,0.75570282385688692,0.75570282385688692,0,0,0,0.63573699262919292,0.63573699262919292,0,0,0,0.56961143413137749,0.56961143413137749,0,0,0,0.25682441444111975,0.25682441444111975,0,0,0,0.48553594318629884,0.48553594318629884,0,0,0,0.6346810343573891,0.6346810343573891,0,0,0,0.35939486007658045,0.35939486007658045,0,0,0,0.55372408950349139,0.55372408950349139,0,0,0,0.81450555797349933,0.81450555797349933,0,0,0,0.86633013919768964,0.86633013919768964,0,0,0,0.19771230293364214,0.19771230293364214,0,0,0,0.61224727625280595,0.61224727625280595,0,0,0,0.30369297352411478,0.30369297352411478,0,0,0,0.50543873681603879,0.50543873681603879,0,0,0,0.10516815743214368,0.10516815743214368,0,0,0,0.75175213144985531,0.75175213144985531,0,0,0,0.44027182328252767,0.44027182328252767,0,0,0,0.51972688675155299,0.51972688675155299,0,0,0,0.27081375526868029,0.27081375526868029,0,0,0,0.53709498815286227,0.53709498815286227,0,0,0,0.51879431844578461,0.51879431844578461,0,0,0,0.051376097403131669,0.051376097403131669,0,0,0,0.54383550023627225,0.54383550023627225,0,0,0,0.58585985334302471,0.58585985334302471,0,0,0,0.24721450987058763,0.24721450987058763,0,0,0,0.38803110925304962,0.38803110925304962,0,0,0,0.19930047023900799,0.19930047023900799,0,0,0,0.94890822086865112,0.94890822086865112,0,0,0,0.79587609825362515,0.79587609825362515,0,0,0,0.56607468577492859,0.56607468577492859,0,0,0,0.89075551856121016,0.89075551856121016,0,0,0,0.19915542353302171,0.19915542353302171,0,0,0,0.18615380558710862,0.18615380558710862,0,0,0,0.3096441553522068,0.3096441553522068,0,0,0,0.53135331325654767,0.53135331325654767,0,0,0,0.49595333098194494,0.49595333098194494,0,0,0,0.78902535482189895,0.78902535482189895,0,0,0,0.68637945364480524,0.68637945364480524,0,0,0,0.12719945789312023,0.12719945789312023,0,0,0,0.42825136193698737,0.42825136193698737,0,0,0,0.25903239114533844,0.25903239114533844,0,0,0,0.60585260667273244,0.60585260667273244,0,0,0,0.31627807739054214,0.31627807739054214,0,0,0,0.45903060700204834,0.45903060700204834,0,0,0,0.33425244577220342,0.33425244577220342,0,0,0,0.11830566817607076,0.11830566817607076,0,0,0,0.38684375438184398,0.38684375438184398,0,0,0,0.8467157640049835,0.8467157640049835,0,0,0,0.14255438303301329,0.14255438303301329,0,0,0,0.80171387499406499,0.80171387499406499,0,0,0,0.44920897476471772,0.44920897476471772,0,0,0,0.34238790984059653,0.34238790984059653,0,0,0,0.69761978493338994,0.69761978493338994,0,0,0,0.14490505704847761,0.14490505704847761,0,0,0,0.81248979005945987,0.81248979005945987,0,0,0,0.6860223993615272,0.6860223993615272,0,0,0,0.64319307514083612,0.64319307514083612,0,0,0,0.88591211717012575,0.88591211717012575,0,0,0,0.73990801506652948,0.73990801506652948,0,0,0,0.43496425716105031,0.43496425716105031,0,0,0,0.4602195735834555,0.4602195735834555,0,0,0,0.03928184401640638,0.03928184401640638,0,0,0,0.4560650319845187,0.4560650319845187,0,0,0,0.43088249712847532,0.43088249712847532,0,0,0,0.1468926809725272,0.1468926809725272,0,0,0,0.48613704373903932,0.48613704373903932,0,0,0,0.39572518733940698,0.39572518733940698,0,0,0,0.63307411162644867,0.63307411162644867,0,0,0,0.5460465356175993,0.5460465356175993,0,0,0,0.23885674473233756,0.23885674473233756]},"combos":{"overall_e1":{"effects":1,"placebo":0,"trends_lin":false,"yatchew":false,"result":{"n_effects_actual":1,"n_placebo_actual":0,"rownames":"Effect_1","estimate":2.6473661402110933,"se":0.8264981709701722,"ci_lo":0.653777935524533,"ci_hi":3.8935912323040629,"n_per_horizon":200,"bw_per_horizon":0.31554506497587592,"n_within_bw":46,"qug_t":1.4774387842258978,"qug_p":0.40364266772890645,"event_id":1}},"event_e2_p2":{"effects":2,"placebo":2,"trends_lin":false,"yatchew":false,"result":{"n_effects_actual":2,"n_placebo_actual":2,"rownames":["Effect_1","Effect_2","Placebo_1","Placebo_2"],"estimate":[2.6473661402110933,2.3176217601535516,1.1385512147666119,0.57832135098240689],"se":[0.8264981709701722,0.92219490280663718,0.85017537365309248,0.75755100177657286],"ci_lo":[0.653777935524533,1.2265072831522941,-1.3402896853105961,-1.0758705017602637],"ci_hi":[3.8935912323040629,4.8414448756071415,1.9923365404952915,1.893674858108378],"n_per_horizon":[200,200,200,200],"bw_per_horizon":[0.31554506497587592,0.37809706789450742,0.2989319366655192,0.3003126817768515],"n_within_bw":[46,63,40,40],"qug_t":[1.4774387842258978,1.4774387842258978,"NA","NA"],"qug_p":[0.40364266772890645,0.40364266772890645,"NA","NA"],"event_id":[1,2,-1,-2]}},"event_e2_p2_yatchew":{"effects":2,"placebo":2,"trends_lin":false,"yatchew":true,"result":{"n_effects_actual":2,"n_placebo_actual":2,"rownames":["Effect_1","Effect_2","Placebo_1","Placebo_2"],"estimate":[2.6473661402110933,2.3176217601535516,1.1385512147666119,0.57832135098240689],"se":[0.8264981709701722,0.92219490280663718,0.85017537365309248,0.75755100177657286],"ci_lo":[0.653777935524533,1.2265072831522941,-1.3402896853105961,-1.0758705017602637],"ci_hi":[3.8935912323040629,4.8414448756071415,1.9923365404952915,1.893674858108378],"n_per_horizon":[200,200,200,200],"bw_per_horizon":[0.31554506497587592,0.37809706789450742,0.2989319366655192,0.3003126817768515],"n_within_bw":[46,63,40,40],"qug_t":[1.4774387842258978,1.4774387842258978,"NA","NA"],"qug_p":[0.40364266772890645,0.40364266772890645,"NA","NA"],"event_id":[1,2,-1,-2],"yatchew_t":[-1.0592019479734889,-0.12949929847802816,0.91083176467162141,-0.340727823562683],"yatchew_p":[0.85524609095016013,0.55151870999410046,0.18119201150309172,0.63334575472470989],"yatchew_n":[200,200,200,200],"yatchew_sigma2_lin":[0.52214484644776371,0.50503000842866941,0.47173770155718292,0.44339821445724009],"yatchew_sigma2_diff":[0.5597170855055762,0.50920505402503635,0.43994980606387107,0.45401512765265722]}},"event_e2_p2_trendslin":{"effects":2,"placebo":2,"trends_lin":true,"yatchew":false,"result":{"n_effects_actual":2,"n_placebo_actual":1,"rownames":["Effect_1","Effect_2","Placebo_1"],"estimate":[3.2103747981430257,4.9572162384045244,-1.6338308343607517],"se":[1.0540252676393409,1.9735977488591603,1.143254911904048],"ci_lo":[-0.073377274461244202,-0.10486973267669653,-1.8907511074309213],"ci_hi":[4.0583258522753525,7.6314912827898631,2.5907257975299705],"n_per_horizon":[200,200,200],"bw_per_horizon":[0.23079268690346477,0.29902177770812322,0.27035949482044808],"n_within_bw":[25,40,36],"qug_t":[1.4774387842258978,1.4774387842258978,"NA"],"qug_p":[0.40364266772890645,0.40364266772890645,"NA"],"event_id":[1,2,-1]}},"event_e2_p2_yatchew_trendslin":{"effects":2,"placebo":2,"trends_lin":true,"yatchew":true,"result":{"n_effects_actual":2,"n_placebo_actual":1,"rownames":["Effect_1","Effect_2","Placebo_1"],"estimate":[3.2103747981430257,4.9572162384045244,-1.6338308343607517],"se":[1.0540252676393409,1.9735977488591603,1.143254911904048],"ci_lo":[-0.073377274461244202,-0.10486973267669653,-1.8907511074309213],"ci_hi":[4.0583258522753525,7.6314912827898631,2.5907257975299705],"n_per_horizon":[200,200,200],"bw_per_horizon":[0.23079268690346477,0.29902177770812322,0.27035949482044808],"n_within_bw":[25,40,36],"qug_t":[1.4774387842258978,1.4774387842258978,"NA"],"qug_p":[0.40364266772890645,0.40364266772890645,"NA"],"event_id":[1,2,-1],"yatchew_t":[-0.33085087332431662,0.44343112784655447,-0.31163610016836146],"yatchew_p":[0.62962143429250594,0.32872696246699495,0.62234145221544424],"yatchew_n":[200,200,200],"yatchew_sigma2_lin":[1.4879326461541424,3.3757426888709148,1.3999333548858255],"yatchew_sigma2_diff":[1.5240210294062284,3.2727283140897905,1.4321192850829783]}}}},"boundary_G200_F4_T5":{"name":"boundary_G200_F4_T5","G":200,"F":4,"T":5,"dose_distribution":"Beta(0.5, 1)","seed":20260426,"panel":{"g":[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15,16,16,16,16,16,17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,20,20,20,20,20,21,21,21,21,21,22,22,22,22,22,23,23,23,23,23,24,24,24,24,24,25,25,25,25,25,26,26,26,26,26,27,27,27,27,27,28,28,28,28,28,29,29,29,29,29,30,30,30,30,30,31,31,31,31,31,32,32,32,32,32,33,33,33,33,33,34,34,34,34,34,35,35,35,35,35,36,36,36,36,36,37,37,37,37,37,38,38,38,38,38,39,39,39,39,39,40,40,40,40,40,41,41,41,41,41,42,42,42,42,42,43,43,43,43,43,44,44,44,44,44,45,45,45,45,45,46,46,46,46,46,47,47,47,47,47,48,48,48,48,48,49,49,49,49,49,50,50,50,50,50,51,51,51,51,51,52,52,52,52,52,53,53,53,53,53,54,54,54,54,54,55,55,55,55,55,56,56,56,56,56,57,57,57,57,57,58,58,58,58,58,59,59,59,59,59,60,60,60,60,60,61,61,61,61,61,62,62,62,62,62,63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,67,68,68,68,68,68,69,69,69,69,69,70,70,70,70,70,71,71,71,71,71,72,72,72,72,72,73,73,73,73,73,74,74,74,74,74,75,75,75,75,75,76,76,76,76,76,77,77,77,77,77,78,78,78,78,78,79,79,79,79,79,80,80,80,80,80,81,81,81,81,81,82,82,82,82,82,83,83,83,83,83,84,84,84,84,84,85,85,85,85,85,86,86,86,86,86,87,87,87,87,87,88,88,88,88,88,89,89,89,89,89,90,90,90,90,90,91,91,91,91,91,92,92,92,92,92,93,93,93,93,93,94,94,94,94,94,95,95,95,95,95,96,96,96,96,96,97,97,97,97,97,98,98,98,98,98,99,99,99,99,99,100,100,100,100,100,101,101,101,101,101,102,102,102,102,102,103,103,103,103,103,104,104,104,104,104,105,105,105,105,105,106,106,106,106,106,107,107,107,107,107,108,108,108,108,108,109,109,109,109,109,110,110,110,110,110,111,111,111,111,111,112,112,112,112,112,113,113,113,113,113,114,114,114,114,114,115,115,115,115,115,116,116,116,116,116,117,117,117,117,117,118,118,118,118,118,119,119,119,119,119,120,120,120,120,120,121,121,121,121,121,122,122,122,122,122,123,123,123,123,123,124,124,124,124,124,125,125,125,125,125,126,126,126,126,126,127,127,127,127,127,128,128,128,128,128,129,129,129,129,129,130,130,130,130,130,131,131,131,131,131,132,132,132,132,132,133,133,133,133,133,134,134,134,134,134,135,135,135,135,135,136,136,136,136,136,137,137,137,137,137,138,138,138,138,138,139,139,139,139,139,140,140,140,140,140,141,141,141,141,141,142,142,142,142,142,143,143,143,143,143,144,144,144,144,144,145,145,145,145,145,146,146,146,146,146,147,147,147,147,147,148,148,148,148,148,149,149,149,149,149,150,150,150,150,150,151,151,151,151,151,152,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,155,156,156,156,156,156,157,157,157,157,157,158,158,158,158,158,159,159,159,159,159,160,160,160,160,160,161,161,161,161,161,162,162,162,162,162,163,163,163,163,163,164,164,164,164,164,165,165,165,165,165,166,166,166,166,166,167,167,167,167,167,168,168,168,168,168,169,169,169,169,169,170,170,170,170,170,171,171,171,171,171,172,172,172,172,172,173,173,173,173,173,174,174,174,174,174,175,175,175,175,175,176,176,176,176,176,177,177,177,177,177,178,178,178,178,178,179,179,179,179,179,180,180,180,180,180,181,181,181,181,181,182,182,182,182,182,183,183,183,183,183,184,184,184,184,184,185,185,185,185,185,186,186,186,186,186,187,187,187,187,187,188,188,188,188,188,189,189,189,189,189,190,190,190,190,190,191,191,191,191,191,192,192,192,192,192,193,193,193,193,193,194,194,194,194,194,195,195,195,195,195,196,196,196,196,196,197,197,197,197,197,198,198,198,198,198,199,199,199,199,199,200,200,200,200,200],"t":[1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5],"y":[-1.2481976087462026,0.33516584772394142,-0.69652385632476399,-0.11057077335096283,0.20321305719150751,-1.0004767215467067,-0.65773183371464561,-0.4315030051462877,0.81825173883593061,0.74995673069048141,-0.8376848656398912,-0.82292030328775334,-0.28230423676302774,-0.52322561871964091,-0.039911476699024395,-1.3502556720219618,-0.47835914308532312,-0.37463307000886414,0.8814223595061581,0.75738821780904209,0.028564281625720667,-0.88771009250153943,-0.20390652055351802,-0.26669704379889508,-0.1293012452008542,-0.78405095207953124,-0.94481072249866949,-0.85774650964545018,-0.70565420579130567,-0.050318759131375401,-1.055080381556512,-1.0410501417555875,-1.7885232449922268,-2.1518738431189948,-1.1278187330091984,0.018178826348799526,0.78263229846998406,0.68434859123845748,1.4909971915547111,2.0162938034142872,0.45557229061822152,0.55710780061696152,0.50019518770008498,1.1244942363938104,1.1072827344389267,-1.2683095304528753,-0.62260368239352559,-1.1276085654404513,-0.23290131008853365,-0.29061830676211814,0.10441246827896644,1.0726580754030139,0.71493404859853915,1.9956187043730691,1.7464089550133459,-0.24034275410595177,0.60845095334667154,0.30448286162443072,-0.0088071632744953221,-0.55620133278426598,-0.6455934884985588,-0.53743505521753243,0.066315540321114863,-0.32828429335466458,0.26132163168931521,0.096150240463075298,0.37485581479640329,1.0951452368408456,2.1987487541182733,2.3970245497768237,1.1190297127267588,1.4434429001925579,-0.088660852996109707,1.6641665309132043,1.1658797589336727,0.5085813008275446,0.95221726886404601,0.17410374176644855,0.3763982145049477,1.2967158906612672,2.0028153164321139,1.6326643654005797,2.2835342572523967,1.8188190839378477,2.1238276534301015,-0.70292159302829493,-0.14311746900120936,-0.63308429161201563,-0.60131694381633705,0.67075471835802258,-0.48366458294384529,-0.79882159903028249,-1.0285938933054779,-0.50705127711975706,0.55939816045796897,-0.8851144049251054,-0.9024119262896737,0.026866495585643912,0.67704078384981137,-0.15246929108102342,1.4719572466536572,0.5515278851769041,1.6792613525207043,2.7160732577037336,2.7288501412430763,-0.15238359521482803,-0.29506468654208273,-0.60387260052811298,0.10098321296136825,-0.58911592908204469,-0.66318094919300075,-0.64515823715742859,0.014444696178840456,2.0488929357819914,2.9157655754655174,-1.2639262803387092,-1.1835270578404933,-0.38533568941590096,-0.26017847616516465,0.20942404247307961,-1.1673564651151553,-1.2629550170641282,-0.6429883142754762,-0.44793355730717466,-0.12687722106039256,-1.5254309508163653,-1.4258139830118861,-1.5121360951500606,0.0050040601809872021,0.92757734415503967,-0.87935392708567661,-0.38451746797533626,0.2802679163799397,0.14474215653313591,-0.092277462100157109,-2.2044387457976078,-1.5902724152012642,-1.2392130258801251,-1.8568083760190515,-1.3775614175832824,1.1556871702279972,1.5559811987842804,1.7356153110963368,2.258198358020314,2.3502189922355958,0.51344990766209686,0.63378545842404399,1.0179610387767823,1.4344741068444782,2.305662195245691,-0.64593276633740082,0.27021022898658781,-0.12959156162073385,0.94440951933938955,1.8011861627858698,-0.25721026043614276,-0.0027665278429818696,0.35739226466389351,0.65991586535788438,1.0847998900458267,0.5815556176967529,0.051256914787265462,0.28792708586127852,2.0319531960403432,2.2306020856048723,1.191989864627049,2.098844901228754,1.7983221007861776,2.2419384108467204,1.1291335469743751,-0.17660193347076514,0.12645375382791091,-1.1696045450552746,-0.43266540335906362,-0.59451177047277115,-1.5720107653850905,-1.253076447703745,-1.6035084306997962,0.024082057158171577,0.029992876138524832,-0.99172763891795135,-1.1616092623557612,-0.30170617241914544,-0.89888693439056411,-0.70359737377364839,-1.397064855932898,-1.8333627581029046,-1.1341108608339916,-1.8096258854408334,-1.5280102577456711,1.8606948369300516,0.70745583448155402,1.689898661431632,1.1467976488963381,2.1960001152799946,-0.38104491110105698,-0.19458545814007269,-0.2838234951212053,-0.32719486020168276,-0.056125709928863531,0.056665459499193771,-0.67715242221320571,0.1999580968188126,1.8914088931638118,2.3470545945074583,0.99012105023405561,1.7173799128274494,1.5511713537636727,3.4418108589734953,3.0663277609564905,0.83355800668779234,0.66766952459316753,-0.062393515415196998,1.3856822336535468,1.7094335160956313,0.85863528176647919,0.81463113735936332,0.82877997978309037,2.443247480402865,2.8818454166917697,0.54471046770607567,1.0814469930703536,1.2929979911574008,0.55165595397030598,0.58054641690667286,-2.0118095335669599,0.18601080155299154,-1.1085017465513451,1.7395968405293665,0.42712154322893009,-1.850534748102437,-1.3754303723655816,-1.4479026590895108,-0.5711572813249628,0.30668539453512061,-0.6858809615273942,0.1813768921787921,1.0455448576319657,0.4652886859787948,-0.34750319060508056,0.084855938609760456,0.16655334847406494,-0.20098228442447785,0.85573759167782359,1.2733278340395189,-1.3954730378394178,-0.012837190898052131,-0.5257320356502212,0.81882842084814078,0.18539305625999478,-0.45760869361396567,0.14564516083015899,0.20269362835951343,0.16863195626623495,0.089080546385809423,0.6333722886806642,0.26581495085304097,0.7914197039257711,0.73571649218270496,0.1827285179632212,0.81891694774334134,1.4785405574192825,0.33540938725035563,0.45295576198206189,1.8269015636674131,-0.13201085785916133,0.49096012354139301,0.80094213619371035,2.3702836069231008,2.6223587865410352,-0.49245871773978167,-1.1113472106530158,-0.53263816503133998,-0.73661418751043395,-0.88664523109331017,0.86955011477678135,0.14439821517716223,-0.17144083586836256,0.29380666744876693,1.2952544175445304,1.2725462170617887,1.404058001479332,1.3836854961629701,1.2020446787439274,1.6602169897385128,0.85528424453809271,0.79930272828428295,1.0051824094098012,0.8746367445655584,0.74813528667169082,0.26060055843434327,0.87027136534179805,-0.28806290043680605,2.5385363577112412,1.9237141311813133,2.1520691295681904,2.2913647332895661,1.6085328404182881,2.0187858565994588,2.0236026733029973,-0.088357991012390447,0.044575070775824677,1.5853198399567283,1.3312012834632279,1.6955693809722172,0.61855251924503207,0.78543994824635566,0.60757089823806376,0.84145634582008766,2.0867792469621014,-1.5934198868257219,-0.52874298267771747,-0.9989058314188497,-1.2813579962061266,-1.0425581554027699,3.0086321313483277,2.4899020246544383,2.1935078549366844,3.3619649076431886,2.8223756405623028,-2.4336434497477382,-2.2761897656209191,-1.7389434320135764,-0.094102557756644986,-0.58967439813603584,-1.9730852823962448,-1.7005688275691169,-1.2824533799159654,-1.6116174652824384,-0.64781233272213679,-2.2733963370132528,-0.48718262816479374,-0.81241864789718732,-1.2293972162197857,0.10654178975308459,-0.59289045858785883,-0.16688579429424005,0.47547455137687733,0.95139747137489361,0.39809562350269051,-1.5216915267905122,-2.439870058991469,-1.4004214553093799,-0.10790162890081612,0.0050916527886870622,-0.67730338336495155,0.55922345382229333,-0.82547662059432703,1.4303205988970171,1.7400886222341811,0.79966485677075783,0.78167347729280134,0.81333938686238549,2.3296442230459178,1.3737513488525437,-0.25147321922443222,0.54026528428936482,1.1159509902194826,1.8332537969308365,1.2268459012864044,-0.27691375493956033,0.3864993117091724,0.43200515983271959,1.0436834638073735,0.13494800159426501,0.71464840828685028,-0.16861515118192708,0.36639612454336379,1.3934658376579947,1.0117592518713605,0.036061354530013412,0.62624106779751854,0.72195930267385888,1.229360661041319,1.5052657392183815,-0.059301327510256054,0.39133510872254501,1.2142866597818749,1.9883790684219225,2.9542476038906278,-0.45026539137776472,0.44240187099163208,1.0247655939597928,2.6716868079243619,2.0489315416738427,0.11947689088530847,0.61062231915730913,-0.46975615084180572,2.0435429056516807,0.77363504983325404,-0.66398350181665688,0.27096103627078699,-0.094155791413704365,0.22446415624448224,0.16551778441722911,-1.6975901909232816,-2.1934008831032177,-2.7543507490457331,-0.79640385003129888,-0.83235807047809984,0.59369102548196495,1.1425148239644107,-0.27664132447310785,-0.030555027019948611,1.3610684266250876,-0.25207511934079668,-1.0921119967534119,-0.68923973019036167,1.0033832316470415,-0.038191547634222278,-0.583831739107046,-0.26256617306279123,-0.72963312511088674,0.057890682723939546,0.16240910496778155,-0.47002302829063258,-1.1914598742823181,-0.19488737921408747,0.10433241250423791,0.012912743407694618,2.0649857282926258,1.4895858347377244,0.31282366175466847,2.3305106142411747,0.90114804761457856,-1.8736729105612615,-0.8050931912842817,-1.865255547767271,-0.60056554511102556,0.0048886698609658386,-0.34249604952935203,0.20291779245900834,-0.88581143620954617,0.55373283136810614,0.89857906009305055,1.3530657464626896,1.2528698391829958,2.217085314892445,3.8990264970479274,3.6103450438202627,0.96406300840570069,-0.39969764368903515,1.533864061298591,0.87433004745638354,1.6459499769810628,1.5772564416944184,1.1016399601183142,1.8711572519750046,0.92393874537954601,1.5381633563888828,1.5663253116513769,1.5070162017273478,1.3306908005699865,3.2224622213737808,3.8535108900638759,1.743658936172696,0.76333961168977238,0.68668803160840697,0.69554456673719667,0.30546534898123467,1.7601728162220449,2.4520809690248035,2.0601506150732134,2.9311696713428326,4.1628674212494721,1.7321246267762405,2.292385561552674,2.4309657004227243,2.9012364706514093,2.748854722276632,0.8881711901625492,0.58099202937506389,0.72875823146273189,1.0124517315126116,1.5044662537951616,-1.0863816007510803,0.17325377077186388,-0.89707912000465351,-0.33639659766488966,1.9967639981699412,1.2568182623530482,1.250840674800513,0.90810133096008927,1.8435502835603592,1.4757845511723429,0.35186948296101789,1.4697771129936501,1.2418220432009219,2.2514656050710071,1.9936752034880501,-0.91846261667407381,-1.279407072475272,-1.2524460289323094,-0.021561663131003717,-0.87716377905988696,0.74840441873161911,0.60200647306480704,1.385079522765795,0.94963219147019651,1.994572844262027,-0.71115208452455825,-1.1608257400969801,-1.5289056219692869,0.38517047298079099,1.0470357887394084,-0.237940621123922,-0.3727075168973259,0.28556022785151192,1.4785960805183378,0.75736562952030861,0.66829263614986023,0.27760230229839233,1.6893196530720385,0.76076887711981289,1.1926117991783907,0.24117614411548333,0.71079437481623264,0.71528329155413095,2.2235062244570845,2.4453152453710096,0.050692772224158078,0.29404796193604865,-0.15279830022379237,2.7467367590612781,1.7225958812881699,-0.79452472152624321,-0.29470283734244423,-0.16573023912301763,1.5599271247219151,2.3649421045500665,-0.20352842932273285,-1.0262299634971981,-1.132846927646423,0.60552874444711646,-0.22176304965752991,1.2531137862751569,2.2999110902912365,1.5722966468464741,3.2902301203365631,3.4817558642410531,-0.63610662182459099,-0.67043529532994439,-0.34971725104818363,-1.7795966610399596,0.26692807425847159,0.65707225030358218,0.26050857928371079,0.78486373716247426,0.46401833927608405,1.0975625154771176,0.25807407419333905,-0.069074345702453188,-0.036382374836353301,-0.70267139297102266,0.39862727138873599,0.45257097313350225,0.93448356848648051,0.42688005194625783,0.0017431420351863292,-0.36804277350465431,-0.90358407346735725,-0.8049009636272787,-0.88445361639369424,-1.3550311537178223,-1.496316600584171,-1.256337202988175,-0.86647411391015861,-0.87299606967883703,-1.5858645602564123,-0.63645485515436273,-1.2984337773074808,-0.91477231929239433,-0.26368643560102623,1.5494628528733709,0.66333382130284657,0.63579151922199462,-0.15796488613324261,0.46914641319149325,0.6993442273821322,0.44754886626210888,0.020089354184026798,-1.2698054586157439,-1.2948293144841745,0.32951926577822044,-0.31550350475280597,-0.052238539157843968,0.67980060409033249,-0.58019698792930841,1.2550492085974025,0.44176573647584116,0.84139405633626729,1.6079490418207574,1.6766957804523321,1.8761579082250366,1.4483310753680545,-0.11065740999238963,0.56375401586464746,0.54794908864074898,2.3510143639503278,2.8374647543286997,-1.558165015996646,-0.95590714552714184,-1.1241176692657437,-0.94961578501361932,-0.3540452331506142,-0.38583919996460037,-1.6642629825538426,-1.0003388034215617,-0.047471385312689171,-0.43482011600107262,-1.1124077333370228,-1.0219008688389666,-0.69216829792602441,-0.048499288760525894,0.19884065493082143,0.027335240478659158,-0.12595014359066317,-0.73750258962417892,1.1705974811058621,2.0591176191614244,0.72938590076809229,0.34783549773903744,0.49450336004966577,1.1384181160104121,1.2650956602541874,1.1082414259799611,0.58063547064338528,0.33765583485871259,1.4868614372082321,1.5584952180974228,-0.69390740180366128,0.3783022733157555,1.241591870549887,0.1542951644918466,1.2998182965547314,-0.30581531376165921,0.018974594663268035,1.3714461021503519,1.610146774148238,2.2845827625414574,-0.31128324860010148,-0.75152442018096255,-0.53051125483126471,1.1547952591066721,1.0043204844699389,-1.3861900394307523,-0.79615840716968422,0.16343573158786984,0.6025562616937703,0.59223614782870071,0.81785495012133325,0.32361085569433601,0.14382632669462619,0.86402674489951403,1.5339338534082199,-1.8857471798431993,-1.7289767541039072,-1.724031889335675,-0.28498245062829386,1.1105376855078894,0.76055403572738811,-0.31164444523453672,1.0892943447657502,2.3054377123404253,1.2945899242623067,1.4851140222134291,1.3996868773436038,1.8926797599701637,1.9259405224393145,2.1131328691236688,-0.053884105590754683,0.91215512365181262,0.77942341981835761,1.2051209192169834,1.3214550833712337,-0.41482919988521033,-0.57473014870117589,-0.013741987960638536,0.87614155683447625,1.064762346863865,0.25256249835800709,0.45772620490953836,0.65022894417318078,1.5057578508656921,1.1619237187905498,-0.51251954179099324,-1.1442087679922124,0.5623477752147813,0.038595416742704503,0.66765885923028179,-1.0098852833490941,-0.88826385169033495,-0.89886968509236465,0.31233688388648145,1.0715947564874182,-0.79657091580369566,0.079762050484129765,-0.49613229077185189,0.13621475692503893,0.86443809189543386,0.086131226013897028,0.58859163434457396,0.12022050781125446,2.4810901836774391,2.0935624114611122,-0.83668450051128918,-1.8366912845731826,-0.04176436332279071,0.35776021197425428,0.11434897350520518,-1.0687793055305099,-0.16377930614451786,-0.32421454692389873,1.6539767564318442,-0.082959879182494256,-1.1199571669646948,0.33670949526572463,-0.24890888403455635,-0.11828724996215473,-0.41498938657200529,0.54631062826616872,0.088130176363804075,1.3053148117038953,2.2188188274252463,2.2494059661949146,1.3643141509806356,0.71460326330250712,1.096421389015785,2.1752781025258461,2.1477445125849979,0.22575274376357346,1.174890215104915,-0.4977747178692562,3.3215547658906823,2.6826761125443142,0.4226819254333572,0.77689408354182876,0.59158229359482339,0.6586578259800262,1.1501759127648263,-0.13412823529455825,-0.44384676375720677,0.43409123354743778,1.6045956625150199,0.23146241600020334,-2.0612967322929663,-0.70258713364277148,-0.95172730827486995,-0.20963393140360942,0.070344060892593041,0.51391744981154752,0.75904353313058159,0.77129208868264032,0.97146232962646684,1.7394897853148836,0.52425162806585679,0.40333035150970198,0.51896591565285743,1.6851526267691601,2.5331957513695431,1.0644323324767546,2.6551022650194951,2.1014939712284928,1.3064080280326389,2.7699527989022852,1.0619239284868212,0.81752018057352593,0.90875879733858156,1.1348033953943104,0.94969118039348133,-1.0645662185741833,-0.0080850871212820863,-0.71211084893844689,0.24175879017985322,-0.63930774345844621,-0.71477424230635378,-0.21421583127575125,-0.39404418187009888,0.8716954394528833,0.79001473297190039,-0.89240231501570544,-1.8590800221969936,-1.228847762154252,-0.31172791128339417,-0.22679155106868612,-1.1339395231089875,-1.1109385627766235,-1.0596755690905426,-0.285908000458098,-0.87721704582794491,-0.90362244067578312,0.13012161823134427,-0.16201105285358922,-0.26854108652174435,0.58167385682294204,-0.2430149596229946,-0.46990600868988858,-0.39483335363482386,0.85426881557713807,2.1018648190882443,-2.6138627768413256,-2.2925144620440117,-1.6114827149624802,-1.0597826356412281,-1.2366422913784321,0.1993860056784037,1.6386614677708162,0.015289623449104561,0.91938878672077229,1.4338059144292217,0.29397308510538045,0.30843133361873076,0.94953480861035799,0.57980686621706445,0.61999619841119469,1.6296313470661528,1.6213365011382022,1.6477307312347591,3.1996446721630676,3.1004955208906129,0.95443819096590521,-0.062488692828586323,1.1639844348527748,0.75498419790626792,0.32760488681509548,0.91137265014016333,1.3454295478199438,0.86711120187423507,2.9709916827755385,2.7471732022359725,0.6632787541254368,1.1090750975390387,0.75904530397378223,1.6611238845747243,1.3384991158140187,0.41144886609561426,0.17190690274420117,0.38511500298196422,-0.46321540226666064,-0.0014860881833348438,1.1404502045008691,0.60356153217516495,0.95186328864210568,0.84651054914108503,1.1205758066299896,1.1406813127101239,0.66856158479569094,-0.21180316573365388,1.2761796898225664,2.1369800274241095,-0.97840388508320775,-1.0133686812905278,-0.59872564914944404,1.1767660195265521,1.7682776413080965,-0.87642229351117906,-1.0131314107134222,-0.71364402653740866,0.67968300951769711,0.46052596826880365,1.953773957216373,1.8104150992831904,2.1604489474344342,3.2008259341959753,3.2274578203583859,-1.2002144379609423,-1.8232480317293354,-1.7382592536786716,0.20228897156059161,0.095372126607350907,1.5411254367536111,0.72893536550964722,0.84554966511548824,0.81867032492375325,1.1349428273351601,0.27908720959562128,0.27861999438396068,0.32265831885826518,0.92999876637628842,1.2117128071148624,-2.8095539198611674,-1.0753757798822003,-1.1808033075579927,-2.1015161207203197,-1.8916895089965919,0.47516189340835302,0.17361724603430811,0.35736518182179966,-0.17974203531700428,0.95080041268535576,0.084705260915975855,-0.78889487132225278,-0.24101215862291697,-0.80776721443489574,-0.69214100866867578,-1.1333204035257338,-0.96780574899574145,-0.63257252379064721,-0.002758808536228341,-0.43390799472069402,-0.27616048371959495,-0.2891995458635197,-0.59524373567006705,0.22044515997440375,1.3082953442641876,0.43432304838334379,1.6735083806429378,2.4702308013061867,2.6022933080505712,2.3767318569242888,-1.1266087123965263,-1.1950223223570473,-0.58573689518592542,0.39262674510654527,-0.63190798208235965,0.36920482845698305,-0.33108897746365751,1.4057858908172136,0.63316303731365187,0.69928244763435921,0.3904362512248335,1.0936356199373507,1.0684611550321248,1.8599740046981132,0.58066296879392099,0.50585428796135234,1.3510818043986026,0.92101302322478884,2.3782032030977414,1.7930780845800407,-0.93722093962854314,0.48304595881472945,-0.098262558713369835,0.02366847902434624,-0.31285295985633288,-0.72114403465326449,-0.66492929653054955,-1.2494259612997998,0.57229119335173673,-0.51551132822432177,-1.8422731379209563,-1.6793582335225232,-1.695628927765557,-0.48290605960849042,-0.63724577206276878,-1.4065741941768097,0.046266194331139543,-1.2357305859211771,0.14009182939312076,-0.61518242586547622,-2.7845709928205991,-2.7481892838321631,-2.8123363614964974,-2.7475353891884495,-2.7352209593401389,1.8969356493980545,1.8362949085100533,2.6618696766671874,3.3614146136926326,2.8073147578386464,0.44187667965561089,0.19227599536229262,0.29372098338371622,1.8849897704729377,2.3692123171335386,1.9204086321170943,2.7143946848726337,1.8295816515592458,4.0591602396615629,3.4373585520513417,1.1823146804141853,0.92591622124172779,0.31488077776595086,0.92199738880185234,1.1497796006437073,0.49794454256171528,1.0789497470804525,0.45613060520636628,1.41327158056108,1.1287950580150043,-2.0765911475142951,-1.2279788742726507,-2.057462417269639,-2.292260314857177,-0.64874665326143388,-3.3447081475340923,-2.9094782633837362,-2.1122307886116047,-1.2994835303658532,-0.61853657410405771,-1.4587535060589185,-0.60424953548934945,-1.06462103623018,0.39856994028487697,0.12810383693972524,1.6398519238144524,1.369551128214791,0.68173825117349918,3.3581516441554902,3.3948222300655075],"d":[0,0,0,0.039716598675226832,0.039716598675226832,0,0,0,0.58957677720094392,0.58957677720094392,0,0,0,0.051750195621632356,0.051750195621632356,0,0,0,0.92164269121131537,0.92164269121131537,0,0,0,0.038532338170298885,0.038532338170298885,0,0,0,0.398276315387254,0.398276315387254,0,0,0,0.42932542000927609,0.42932542000927609,0,0,0,0.039834461211591739,0.039834461211591739,0,0,0,0.0011438257680773444,0.0011438257680773444,0,0,0,0.13036982235400105,0.13036982235400105,0,0,0,0.182433672078153,0.182433672078153,0,0,0,0.08163840442923205,0.08163840442923205,0,0,0,0.0647326044492222,0.0647326044492222,0,0,0,0.95938736486555176,0.95938736486555176,0,0,0,0.085349039175399516,0.085349039175399516,0,0,0,0.017807637514219897,0.017807637514219897,0,0,0,0.070678822329669988,0.070678822329669988,0,0,0,0.44103894170827207,0.44103894170827207,0,0,0,0.51211073642729288,0.51211073642729288,0,0,0,0.016209696041169609,0.016209696041169609,0,0,0,0.97556798723764226,0.97556798723764226,0,0,0,0.27116257798799104,0.27116257798799104,0,0,0,0.89926566373310235,0.89926566373310235,0,0,0,0.22013872827722575,0.22013872827722575,0,0,0,0.57238688881072031,0.57238688881072031,0,0,0,0.91072001985101092,0.91072001985101092,0,0,0,0.070773593664092443,0.070773593664092443,0,0,0,0.0091331637606220772,0.0091331637606220772,0,0,0,0.12006529177268296,0.12006529177268296,0,0,0,0.64239213955870245,0.64239213955870245,0,0,0,0.50816902448381507,0.50816902448381507,0,0,0,0.53363719740481963,0.53363719740481963,0,0,0,0.51309660621529207,0.51309660621529207,0,0,0,0.020081594177735199,0.020081594177735199,0,0,0,0.12577512837544266,0.12577512837544266,0,0,0,0.64654282640505045,0.64654282640505045,0,0,0,0.012282750506408247,0.012282750506408247,0,0,0,0.17025631797109556,0.17025631797109556,0,0,0,0.5449181029657103,0.5449181029657103,0,0,0,0.091841846576975986,0.091841846576975986,0,0,0,0.6906477925764789,0.6906477925764789,0,0,0,0.050031142443076,0.050031142443076,0,0,0,0.51528474334351482,0.51528474334351482,0,0,0,0.69818795903939779,0.69818795903939779,0,0,0,0.011572669771245783,0.011572669771245783,0,0,0,0.98304883464272819,0.98304883464272819,0,0,0,6.2163112792332096e-05,6.2163112792332096e-05,0,0,0,0.11596085502310344,0.11596085502310344,0,0,0,0.64729243834033301,0.64729243834033301,0,0,0,0.11786241357682507,0.11786241357682507,0,0,0,0.039687893955290598,0.039687893955290598,0,0,0,0.17692842336446138,0.17692842336446138,0,0,0,0.33904321212002675,0.33904321212002675,0,0,0,0.93171598501419683,0.93171598501419683,0,0,0,0.022703862687503597,0.022703862687503597,0,0,0,0.041528108626102372,0.041528108626102372,0,0,0,0.010352843934672944,0.010352843934672944,0,0,0,0.025541554729635946,0.025541554729635946,0,0,0,0.86508709266071415,0.86508709266071415,0,0,0,0.032715510873804574,0.032715510873804574,0,0,0,0.55673099187982888,0.55673099187982888,0,0,0,0.46528781542634712,0.46528781542634712,0,0,0,0.05250444842400559,0.05250444842400559,0,0,0,0.019234771136061264,0.019234771136061264,0,0,0,0.78454018299999551,0.78454018299999551,0,0,0,0.043238637262987142,0.043238637262987142,0,0,0,0.0061259955406956336,0.0061259955406956336,0,0,0,0.087896457494439154,0.087896457494439154,0,0,0,0.42425267667664235,0.42425267667664235,0,0,0,0.76543220288791469,0.76543220288791469,0,0,0,0.29575397791796532,0.29575397791796532,0,0,0,0.2365099288892267,0.2365099288892267,0,0,0,0.11563356458001114,0.11563356458001114,0,0,0,0.49306559871650679,0.49306559871650679,0,0,0,0.49910549089381517,0.49910549089381517,0,0,0,0.84999846117444589,0.84999846117444589,0,0,0,0.66431734196333314,0.66431734196333314,0,0,0,0.26370990083846413,0.26370990083846413,0,0,0,0.13661090316928515,0.13661090316928515,0,0,0,0.20219965638391874,0.20219965638391874,0,0,0,0.059183149082153712,0.059183149082153712,0,0,0,0.60631934384161223,0.60631934384161223,0,0,0,0.056698603316843628,0.056698603316843628,0,0,0,0.00023411641311360382,0.00023411641311360382,0,0,0,0.17835333494081804,0.17835333494081804,0,0,0,0.40791650919338873,0.40791650919338873,0,0,0,0.015372974898934046,0.015372974898934046,0,0,0,0.69863441311476959,0.69863441311476959,0,0,0,0.011245562259866559,0.011245562259866559,0,0,0,0.045429394769936023,0.045429394769936023,0,0,0,0.85964519796879635,0.85964519796879635,0,0,0,0.0063205440945139279,0.0063205440945139279,0,0,0,0.63957399436163809,0.63957399436163809,0,0,0,0.2755290285342244,0.2755290285342244,0,0,0,0.0021630780534828225,0.0021630780534828225,0,0,0,0.23321829830778437,0.23321829830778437,0,0,0,0.060025346866346453,0.060025346866346453,0,0,0,0.92668535887476777,0.92668535887476777,0,0,0,0.36612793574153274,0.36612793574153274,0,0,0,0.20360459048718896,0.20360459048718896,0,0,0,0.82184041088948412,0.82184041088948412,0,0,0,0.47191224540934101,0.47191224540934101,0,0,0,0.01146596566830025,0.01146596566830025,0,0,0,0.67822633955589851,0.67822633955589851,0,0,0,0.95812015838419484,0.95812015838419484,0,0,0,0.53716469656360755,0.53716469656360755,0,0,0,0.1011120790867054,0.1011120790867054,0,0,0,0.41033305273404919,0.41033305273404919,0,0,0,0.036122136583803408,0.036122136583803408,0,0,0,0.18841019921805641,0.18841019921805641,0,0,0,0.078621805658889057,0.078621805658889057,0,0,0,0.0089312965653902242,0.0089312965653902242,0,0,0,0.062886636497025236,0.062886636497025236,0,0,0,0.026022017186818947,0.026022017186818947,0,0,0,0.97789073376214231,0.97789073376214231,0,0,0,0.010370056959917499,0.010370056959917499,0,0,0,0.021689587967875276,0.021689587967875276,0,0,0,0.63351721706844522,0.63351721706844522,0,0,0,0.40314361021597755,0.40314361021597755,0,0,0,0.81465637948239011,0.81465637948239011,0,0,0,0.015197037194632128,0.015197037194632128,0,0,0,0.45850164214850697,0.45850164214850697,0,0,0,0.7180285284328064,0.7180285284328064,0,0,0,0.35614646690005203,0.35614646690005203,0,0,0,0.080975748639523795,0.080975748639523795,0,0,0,0.16302897314963488,0.16302897314963488,0,0,0,0.030365112367064372,0.030365112367064372,0,0,0,0.86741181991044458,0.86741181991044458,0,0,0,0.71099152468627125,0.71099152468627125,0,0,0,0.030815097310705853,0.030815097310705853,0,0,0,4.7521594019250551e-05,4.7521594019250551e-05,0,0,0,0.76830866740155412,0.76830866740155412,0,0,0,0.60706926195457089,0.60706926195457089,0,0,0,0.011410332796730663,0.011410332796730663,0,0,0,0.21978291125176463,0.21978291125176463,0,0,0,0.36129983457798742,0.36129983457798742,0,0,0,0.36529751896148271,0.36529751896148271,0,0,0,0.2878328820248699,0.2878328820248699,0,0,0,0.84352174586740225,0.84352174586740225,0,0,0,0.60436399475216462,0.60436399475216462,0,0,0,0.95193533036312805,0.95193533036312805,0,0,0,0.4223195534475121,0.4223195534475121,0,0,0,0.53860565420578432,0.53860565420578432,0,0,0,0.17844318040917687,0.17844318040917687,0,0,0,0.73824006134071463,0.73824006134071463,0,0,0,0.66936507046383886,0.66936507046383886,0,0,0,0.79482163448308174,0.79482163448308174,0,0,0,0.0073385343940421408,0.0073385343940421408,0,0,0,0.0031095546894705467,0.0031095546894705467,0,0,0,0.052403310320292433,0.052403310320292433,0,0,0,0.089197063424920689,0.089197063424920689,0,0,0,0.74955562037888745,0.74955562037888745,0,0,0,0.53849483807614484,0.53849483807614484,0,0,0,0.076086362245183503,0.076086362245183503,0,0,0,0.060409271597455044,0.060409271597455044,0,0,0,0.71977760285152681,0.71977760285152681,0,0,0,0.70975512945292552,0.70975512945292552,0,0,0,0.14506044596536902,0.14506044596536902,0,0,0,0.032529474898389948,0.032529474898389948,0,0,0,0.71473007526537979,0.71473007526537979,0,0,0,0.51141483513639385,0.51141483513639385,0,0,0,0.25289463289892145,0.25289463289892145,0,0,0,8.1533651944152929e-06,8.1533651944152929e-06,0,0,0,0.79277479427594766,0.79277479427594766,0,0,0,0.0044365061068801321,0.0044365061068801321,0,0,0,0.77399958477620212,0.77399958477620212,0,0,0,0.06196683593162608,0.06196683593162608,0,0,0,0.35702455527637517,0.35702455527637517,0,0,0,0.25441045038109938,0.25441045038109938,0,0,0,0.31186050927911535,0.31186050927911535,0,0,0,0.78983263801061399,0.78983263801061399,0,0,0,0.77239313359927908,0.77239313359927908,0,0,0,0.77711818549945977,0.77711818549945977,0,0,0,0.54447822491467035,0.54447822491467035,0,0,0,0.27534366762892354,0.27534366762892354,0,0,0,0.4017510318049729,0.4017510318049729,0,0,0,0.00073152044239097611,0.00073152044239097611,0,0,0,0.25738394934044562,0.25738394934044562,0,0,0,0.1876441404648406,0.1876441404648406,0,0,0,0.024234731219834858,0.024234731219834858,0,0,0,0.4671803713655272,0.4671803713655272,0,0,0,0.16543388577292223,0.16543388577292223,0,0,0,0.37028911085714827,0.37028911085714827,0,0,0,0.13475921076902042,0.13475921076902042,0,0,0,0.32009122363471426,0.32009122363471426,0,0,0,0.62637127570404227,0.62637127570404227,0,0,0,0.34284674546281119,0.34284674546281119,0,0,0,0.44064927635495094,0.44064927635495094,0,0,0,0.62223137060982714,0.62223137060982714,0,0,0,0.091890609943520746,0.091890609943520746,0,0,0,0.026516887775280499,0.026516887775280499,0,0,0,0.3541830381186668,0.3541830381186668,0,0,0,0.87655814410056898,0.87655814410056898,0,0,0,0.66239474687743061,0.66239474687743061,0,0,0,0.18639786522348142,0.18639786522348142,0,0,0,0.0021048105045465256,0.0021048105045465256,0,0,0,0.076741483925281975,0.076741483925281975,0,0,0,0.82999115683401092,0.82999115683401092,0,0,0,0.60079240972847792,0.60079240972847792,0,0,0,0.90557913849731753,0.90557913849731753]},"combos":{"overall_e1":{"effects":1,"placebo":0,"trends_lin":false,"yatchew":false,"result":{"n_effects_actual":1,"n_placebo_actual":0,"rownames":"Effect_1","estimate":1.8354503874478514,"se":0.3389871859819219,"ci_lo":1.2902179288460427,"ci_hi":2.6190232803363385,"n_per_horizon":200,"bw_per_horizon":0.25158762529407713,"n_within_bw":95,"qug_t":0.20710520736639748,"qug_p":0.82842820484699142,"event_id":1}},"event_e2_p2":{"effects":2,"placebo":2,"trends_lin":false,"yatchew":false,"result":{"n_effects_actual":2,"n_placebo_actual":2,"rownames":["Effect_1","Effect_2","Placebo_1","Placebo_2"],"estimate":[1.8354503874478514,1.6321106576377173,0.34867498822950738,0.222037987356727],"se":[0.3389871859819219,0.36790873137274871,0.35604743741538836,0.35633725566867297],"ci_lo":[1.2902179288460427,0.85160824269868252,0.033567955622483492,-0.50795600438787503],"ci_hi":[2.6190232803363385,2.2937839688754997,1.4292482638663633,0.88886037053300493],"n_per_horizon":[200,200,200,200],"bw_per_horizon":[0.25158762529407713,0.36102874896913456,0.21924600896957722,0.3597049892404296],"n_within_bw":[95,111,91,111],"qug_t":[0.20710520736639748,0.20710520736639748,"NA","NA"],"qug_p":[0.82842820484699142,0.82842820484699142,"NA","NA"],"event_id":[1,2,-1,-2]}},"event_e2_p2_yatchew":{"effects":2,"placebo":2,"trends_lin":false,"yatchew":true,"result":{"n_effects_actual":2,"n_placebo_actual":2,"rownames":["Effect_1","Effect_2","Placebo_1","Placebo_2"],"estimate":[1.8354503874478514,1.6321106576377173,0.34867498822950738,0.222037987356727],"se":[0.3389871859819219,0.36790873137274871,0.35604743741538836,0.35633725566867297],"ci_lo":[1.2902179288460427,0.85160824269868252,0.033567955622483492,-0.50795600438787503],"ci_hi":[2.6190232803363385,2.2937839688754997,1.4292482638663633,0.88886037053300493],"n_per_horizon":[200,200,200,200],"bw_per_horizon":[0.25158762529407713,0.36102874896913456,0.21924600896957722,0.3597049892404296],"n_within_bw":[95,111,91,111],"qug_t":[0.20710520736639748,0.20710520736639748,"NA","NA"],"qug_p":[0.82842820484699142,0.82842820484699142,"NA","NA"],"event_id":[1,2,-1,-2],"yatchew_t":[-0.92110442819369864,-0.50048467898905169,0.95411553731396825,-0.97489926370081903],"yatchew_p":[0.82150204572915397,0.69163307925941564,0.1700125810656572,0.8351948843042849],"yatchew_n":[200,200,200,200],"yatchew_sigma2_lin":[0.52600004236674092,0.50406362521457915,0.47173770155718298,0.44339821445724076],"yatchew_sigma2_diff":[0.5599464242416734,0.52183532672346633,0.44421723484412623,0.4717790780711027]}},"event_e2_p2_trendslin":{"effects":2,"placebo":2,"trends_lin":true,"yatchew":false,"result":{"n_effects_actual":2,"n_placebo_actual":1,"rownames":["Effect_1","Effect_2","Placebo_1"],"estimate":[2.2075189185099999,2.1528981738164998,-0.54882296074010317],"se":[0.55753340844334187,0.96225291057458384,0.71563968633468611],"ci_lo":[1.5804833796899809,1.1131383152117049,-2.6852318447982939],"ci_hi":[3.7659741811435996,4.885100412701755,0.12002417744875693],"n_per_horizon":[200,200,200],"bw_per_horizon":[0.22155452126876168,0.24861100717375795,0.20707501454894253],"n_within_bw":[93,95,91],"qug_t":[0.20710520736639748,0.20710520736639748,"NA"],"qug_p":[0.82842820484699142,0.82842820484699142,"NA"],"event_id":[1,2,-1]}},"event_e2_p2_yatchew_trendslin":{"effects":2,"placebo":2,"trends_lin":true,"yatchew":true,"result":{"n_effects_actual":2,"n_placebo_actual":1,"rownames":["Effect_1","Effect_2","Placebo_1"],"estimate":[2.2075189185099999,2.1528981738164998,-0.54882296074010317],"se":[0.55753340844334187,0.96225291057458384,0.71563968633468611],"ci_lo":[1.5804833796899809,1.1131383152117049,-2.6852318447982939],"ci_hi":[3.7659741811435996,4.885100412701755,0.12002417744875693],"n_per_horizon":[200,200,200],"bw_per_horizon":[0.22155452126876168,0.24861100717375795,0.20707501454894253],"n_within_bw":[93,95,91],"qug_t":[0.20710520736639748,0.20710520736639748,"NA"],"qug_p":[0.82842820484699142,0.82842820484699142,"NA"],"event_id":[1,2,-1],"yatchew_t":[0.26766354580134533,-0.10340897506092189,1.5845345634062067],"yatchew_p":[0.39447915621740393,0.54118080536619884,0.05653606133402489],"yatchew_n":[200,200,200],"yatchew_sigma2_lin":[1.5043599155808882,3.3995320064128789,1.3999333548858239],"yatchew_sigma2_diff":[1.4784802760585405,3.4218933658422501,1.2542756805895328]}}}}}} diff --git a/diff_diff/had.py b/diff_diff/had.py index b7a77f05..2819cd09 100644 --- a/diff_diff/had.py +++ b/diff_diff/had.py @@ -2795,12 +2795,15 @@ def fit( # PR #376 R4 P1: preserve pre-PR positional-or-keyword status of # `survey`, `weights`, `cband` for back-compat with positional # callers. `survey_design=` is the only new addition and is - # keyword-only. + # keyword-only. PR #389 (Phase 4 R-parity): `trends_lin=` is + # likewise keyword-only (additive; no positional callers can + # exist for it pre-PR). survey: Any = None, weights: Optional[np.ndarray] = None, cband: bool = True, *, survey_design: Any = None, + trends_lin: bool = False, ) -> HeterogeneousAdoptionDiDResults: """Fit the HAD estimator. @@ -2900,6 +2903,24 @@ def fit( effect on ``aggregate="overall"`` or on unweighted event- study. ``n_bootstrap`` and ``seed`` (constructor params) control replicate count and RNG; defaults are 999 / ``None``. + trends_lin : bool, default False, keyword-only + When ``True``, applies paper Eq 17 linear-trend detrending + to per-event-time outcome evolutions. Mirrors R + ``DIDHAD::did_had(..., trends_lin=TRUE)``. Per-group slope + is estimated as ``Y[g, F-1] - Y[g, F-2]``; each event-time + ``e`` evolution is replaced by ``dy_dict[e] - (e+1) × + slope`` (uniform formula that absorbs both effect-side + detrending and placebo-side anchor swap). Requires + ``aggregate="event_study"`` AND ``F >= 3`` (panel must + include both ``F-1`` and ``F-2``); raises + ``NotImplementedError`` on ``aggregate="overall"`` and + ``ValueError`` on ``F < 3``. The "consumed" placebo at + event time ``e=-2`` is auto-dropped (R reduces max + placebo lag by 1 with the same effect). Mutually + exclusive with survey weighting (``survey_design`` / + ``survey`` / ``weights``); raises ``NotImplementedError`` + if combined. Default ``False`` preserves bit-exact + backcompat with all pre-PR fits. Returns ------- @@ -2915,6 +2936,37 @@ def fit( if n_set > 1: raise ValueError(HAD_DUAL_KNOB_MUTEX_MSG_DATA_IN) + # ---- trends_lin scope gates (PR #389 / Phase 4 R-parity). + # `trends_lin=True` implements paper Eq 17 linear-trend detrending + # (per-group slope from Y[F-1]-Y[F-2], applied to per-event-time + # outcome evolutions). Requires F >= 3 (need both F-1 and F-2 in + # panel) and is currently event-study-only — the overall path is + # 2-period and cannot accommodate the F-2 row. + if trends_lin: + if aggregate != "event_study": + raise NotImplementedError( + "HAD.fit(trends_lin=True) requires " + "aggregate='event_study' (the linear-trend slope " + "estimator needs Y at F-2, which a 2-period panel " + "does not contain). Pass a panel with at least 3 " + "periods and aggregate='event_study'; the per-" + "horizon arrays in the resulting " + "HeterogeneousAdoptionDiDEventStudyResults provide " + "the same single-effect / per-effect estimates as " + "the overall path." + ) + if survey_design is not None or survey is not None or weights is not None: + raise NotImplementedError( + "HAD.fit(trends_lin=True) is not yet supported with " + "survey weighting (`survey_design=` / `survey=` / " + "`weights=`). The per-group slope estimator's " + "weighted variant (weighted-OLS slope? per-PSU slope?) " + "is not derived from the paper. Use trends_lin=True " + "WITHOUT survey weights, or use survey weights " + "WITHOUT trends_lin. Tracked in TODO.md as a follow-" + "up if user demand emerges." + ) + # Soft deprecation: route legacy survey=/weights= aliases to # survey_design=. The internal back-end paths (legacy weights= and # survey= routing below) are unchanged; only the entry signature @@ -2986,6 +3038,7 @@ def fit( survey=survey, weights=weights, cband=cband, + trends_lin=trends_lin, ) # ---- Resolve effective fit-time state (local vars only, per @@ -3788,6 +3841,7 @@ def _fit_event_study( survey: Any = None, weights: Optional[np.ndarray] = None, cband: bool = True, + trends_lin: bool = False, ) -> HeterogeneousAdoptionDiDEventStudyResults: """Multi-period event-study fit (paper Appendix B.2). @@ -3949,6 +4003,56 @@ def _fit_event_study( f"got n_units={n_units} after aggregation." ) + # ---- Apply trends_lin detrending (paper Eq 17, R `did_had(trends_lin=TRUE)`). + # Compute the per-group linear-trend slope from the F-2 → F-1 + # outcome change and subtract a (e+1) × slope adjustment from + # every event-time evolution. This is the unified formula that + # absorbs both R's effect-side detrending (Effect_i -= i × slope, + # i = e+1 for our event-time convention) AND R's placebo-side + # anchor shift (placebos under trends_lin re-anchor to F-2 and + # add i × slope; algebraically equivalent to dy_dict[e] - + # (e+1) × slope on the F-1 anchor convention). + # + # The shallowest placebo (e=-2) is dropped under trends_lin + # because the F-2 → F-1 evolution is "consumed" by the slope + # estimator (R reduces max placebo lag by 1; same effect). + # + # Optimization: dy_dict[-2] = Y[F-2] - Y[F-1] = -slope, so we + # can read slope directly from the dy_dict entry without + # re-pivoting the wide outcome matrix. + if trends_lin: + if len(t_pre_list) < 2: + raise ValueError( + "HAD.fit(trends_lin=True) requires F >= 3 (at least " + "two pre-treatment periods F-2 and F-1) so the per-" + "group linear-trend slope Y[F-1] - Y[F-2] is " + f"identified. Got F={F!r} with pre-periods " + f"{t_pre_list!r} (need at least 2 entries)." + ) + if -2 not in dy_dict: + # Should be unreachable given the len(t_pre_list) >= 2 + # check above (the validator guarantees pre-periods are + # contiguous, so F-2 → e=-2 must be in dy_dict). Defensive + # belt-and-suspenders. + raise ValueError( + "HAD.fit(trends_lin=True): expected event time -2 " + "(period F-2) to be present in the panel so the " + "linear-trend slope can be estimated. Got dy_dict " + f"keys {sorted(dy_dict.keys())!r}." + ) + # slope[g] = Y[g, F-1] - Y[g, F-2] = -dy_dict[-2][g]. + lin_trend_slope = -dy_dict[-2] + # Detrend every event-time evolution. Apply BEFORE dropping + # e=-2 so the math is unambiguous (the e=-2 entry itself + # detrends to zero by construction; we drop it anyway). + dy_dict = {e: dy_dict[e] - (e + 1) * lin_trend_slope for e in dy_dict.keys()} + # Drop the consumed placebo (F-2 row used by the slope + # estimator). R reduces max placebo lag by 1 with the same + # effect (R's placebo lags shift from {1, 2, ..., F-2} to + # {1, 2, ..., F-3}; in our event_time convention this maps + # to dropping e = -2). + del dy_dict[-2] + # ---- Zero-weight subpopulation convention (mirrors static path). # Design decisions (auto-detect, d_lower, mass-point threshold) # run on the positive-weight subset; variance composition runs diff --git a/diff_diff/had_pretests.py b/diff_diff/had_pretests.py index 1b0e3bcf..8feda360 100644 --- a/diff_diff/had_pretests.py +++ b/diff_diff/had_pretests.py @@ -49,11 +49,16 @@ (Step 4 in the paper's workflow is the decision itself - "use TWFE if none of the tests rejects" - not a separate test.) -Eq. 18 linear-trend detrending (paper Section 5.2 Pierce-Schott -application, published p=0.51) is the one remaining deferred item; -tracked in ``TODO.md`` and slated for Phase 4 alongside the replication -harness. See ``docs/methodology/REGISTRY.md`` for the full algorithm -narrative, invariants, and deviation notes. +Eq. 17 / Eq. 18 linear-trend detrending (paper Section 5.2 Pierce-Schott +application) shipped in PR #389 (Phase 4 R-parity) as the +``trends_lin: bool = False`` keyword-only kwarg on +:func:`joint_pretrends_test`, :func:`joint_homogeneity_test`, AND +:meth:`HeterogeneousAdoptionDiD.fit` (event-study path). Mirrors R +``DIDHAD::did_had(..., trends_lin=TRUE)``. Survey-weighted variant is +not yet derived from the paper and raises ``NotImplementedError``; +tracked in ``TODO.md`` if user demand emerges. See +``docs/methodology/REGISTRY.md`` for the full algorithm narrative, +invariants, and deviation notes. """ from __future__ import annotations @@ -428,8 +433,10 @@ class StuteJointResult: :func:`joint_pretrends_test` (mean-independence: ``E[Y_t - Y_base | D] = mu_t``, design matrix ``[1]``) and :func:`joint_homogeneity_test` (linearity: ``E[Y_t - Y_base | D_t] = beta_{0,t} + beta_{fe,t} * D``, - design matrix ``[1, D]``). Eq 18 linear-trend detrending (paper - Section 5.2 Pierce-Schott application) is a Phase 4 follow-up. + design matrix ``[1, D]``). Both wrappers accept a ``trends_lin: + bool = False`` keyword-only flag (PR #392): when ``True``, applies + paper Eq 17 / Eq 18 linear-trend detrending before the joint CvM + using per-group slope ``Y[g, F-1] - Y[g, F-2]``. Attributes ---------- @@ -3363,6 +3370,7 @@ def joint_pretrends_test( survey_design: Any = None, survey: Any = None, weights: Optional[np.ndarray] = None, + trends_lin: bool = False, ) -> StuteJointResult: """Joint Stute pre-trends test (paper Section 4.2 step 2). @@ -3413,6 +3421,29 @@ def joint_pretrends_test( DEPRECATED alias for the per-row pweight shortcut. Prefer ``survey_design=SurveyDesign(weights='col_name')`` against your dataframe instead. Will be removed in the next minor release. + trends_lin : bool, default False, keyword-only + When ``True``, applies paper Eq 17 / Eq 18 linear-trend + detrending: per-group slope estimated as ``Y[g, base] - + Y[g, base - 1]`` and subtracted from each pre-period horizon's + outcome evolution as ``(t - base) × slope``. Mirrors R + ``DIDHAD::did_had(..., trends_lin=TRUE)`` on its joint Stute + pre-trends surface (paper Section 5.2 Pierce-Schott + application). **Requires** ``base_period`` to equal the last + validated pre-period (``t_pre_list[-1]``, i.e. the canonical + ``F-1`` anchor). Direct callers passing a non-terminal base + get a ``ValueError`` — Eq 17 / R both anchor at ``F-1`` and + any other anchor would compute a different slope and + detrending. The previous validated pre-period + (``t_pre_list[-2]``, ``F-2``) must also be present so the + slope is identified. The "consumed" placebo at ``F-2`` is + dropped from ``pre_periods`` explicitly (its detrended + residual is mechanically zero by construction); a + ``UserWarning`` fires when the filter triggers. If + ``pre_periods`` becomes empty after the drop, raises + ``ValueError`` (no testable placebo horizons remain). + Mutually exclusive with survey weighting (``survey_design`` / + ``survey`` / ``weights``); raises ``NotImplementedError`` if + combined. Default ``False`` preserves bit-exact backcompat. Returns ------- @@ -3439,6 +3470,21 @@ def joint_pretrends_test( if survey_design is not None and survey is None: survey = survey_design + # ---- trends_lin × survey_design gate (PR #389 / Phase 4 R-parity). ---- + # Detrending under survey weighting (weighted slope? per-PSU slope?) + # is not derived from the paper. Use trends_lin without survey weights, + # OR survey weights without trends_lin. Tracked as TODO follow-up. + if trends_lin and (survey is not None or weights is not None): + raise NotImplementedError( + "joint_pretrends_test(trends_lin=True) is not yet supported " + "with survey weighting (`survey_design=` / `survey=` / " + "`weights=`). The per-group slope estimator's weighted " + "variant is not derived from the paper. Use trends_lin=True " + "WITHOUT survey weights, or use survey weights WITHOUT " + "trends_lin. Tracked in TODO.md as a follow-up if user " + "demand emerges." + ) + if len(pre_periods) == 0: raise ValueError( "pre_periods must be non-empty. Workflow dispatch handles " @@ -3478,6 +3524,15 @@ def joint_pretrends_test( f"(base_period={base_period!r})." ) + # ---- trends_lin: defer the consumed-placebo drop and base-1 + # identification until AFTER the validator block runs (so we can + # use t_pre_list to enforce the non-terminal-base guard and the + # observed-period predecessor consistently). On 2-period panels + # the validator does not run and trends_lin needs F-2, which is + # impossible — front-door-reject here. + base_minus_1_period: Any = None + pre_periods_effective = list(pre_periods) + # Event-study validation contract (paper Appendix B.2): # When the panel has >= 3 distinct periods, always route through # `_validate_had_panel_event_study`. This enforces (a) balanced @@ -3489,6 +3544,13 @@ def joint_pretrends_test( # panels the validator does not apply; skip and fall through to the # simpler balance/invariant guards in `_aggregate_for_joint_test`. n_periods = int(data[time_col].nunique()) + if trends_lin and n_periods < 3: + raise ValueError( + f"joint_pretrends_test(trends_lin=True) requires a panel " + f"with at least 3 distinct time periods so the per-group " + f"slope Y[g, base] - Y[g, base - 1] is identified. Got " + f"n_periods={n_periods}." + ) data_filtered: pd.DataFrame = data if n_periods >= 3: F_val, t_pre_list, _t_post_list, data_filtered, _filter_info = ( @@ -3522,6 +3584,68 @@ def joint_pretrends_test( f"periods. Not-pre entries: {not_pre!r}. Validator's " f"pre-period set: {list(t_pre_list)!r}." ) + # PR #392 R3 P1 (non-terminal base guard): paper Eq 17 / Eq 18 + # and R `DIDHAD::did_had(..., trends_lin=TRUE)` anchor the + # detrending at F-1 (the last validated pre-period) and use + # Y[F-1] - Y[F-2] as the slope. A direct caller passing + # base_period < F-1 (e.g. F-2) would compute a different slope + # at a different anchor, silently changing the methodology + # away from the documented Eq 17/18 construction. Reject + # explicitly. Workflow + HAD.fit always pass F-1; this check + # only fires on direct user calls with non-terminal bases. + if trends_lin and base_period != t_pre_list[-1]: + raise ValueError( + f"joint_pretrends_test(trends_lin=True) requires " + f"base_period to equal the last validated pre-period " + f"({t_pre_list[-1]!r}, the canonical Eq 17 anchor " + f"F-1). Got base_period={base_period!r}. Anchoring at " + f"any other pre-period would compute a different " + f"slope and detrending that does not match paper " + f"Eq 17 / Eq 18 or R DIDHAD::did_had(trends_lin=TRUE)." + ) + # PR #392 R3 P1 (observed-period base-1 lookup) + R1 P0 + # (consumed-placebo drop) consolidated: + # base_minus_1_period = t_pre_list[-2] (= F-2, the validated + # observed pre-period immediately before F-1). Using + # t_pre_list ensures correctness on ordered-categorical panels + # with unused intermediate levels (the validator's t_pre_list + # is built from observed contiguous pre-periods, not from the + # full dtype's category list). Then drop t_pre_list[-2] from + # pre_periods if present (the consumed placebo whose detrended + # residual is mechanically zero). + if trends_lin: + if len(t_pre_list) < 2: + raise ValueError( + f"joint_pretrends_test(trends_lin=True) requires " + f"at least 2 validated pre-periods so the per-" + f"group slope Y[g, F-1] - Y[g, F-2] is identified. " + f"Got t_pre_list={list(t_pre_list)!r}." + ) + base_minus_1_period = t_pre_list[-2] + if base_minus_1_period in pre_periods_effective: + warnings.warn( + f"joint_pretrends_test(trends_lin=True): dropping " + f"period {base_minus_1_period!r} from pre_periods " + f"— it is the 'consumed' placebo (the F-2 → F-1 " + f"evolution used by the per-group slope " + f"estimator), so under trends_lin its detrended " + f"residual is mechanically zero. R's " + f"`did_had(trends_lin=TRUE)` reduces max placebo " + f"lag by 1 with the same effect.", + UserWarning, + stacklevel=2, + ) + pre_periods_effective = [ + t for t in pre_periods_effective if t != base_minus_1_period + ] + if len(pre_periods_effective) == 0: + raise ValueError( + f"joint_pretrends_test(trends_lin=True): no testable " + f"placebo horizons remain after dropping the consumed " + f"placebo at base_period - 1 = {base_minus_1_period!r}. " + f"Pass at least one earlier observed pre-period when " + f"using trends_lin=True." + ) d_arr, dy_by_horizon, _ = _aggregate_for_joint_test( data_filtered, @@ -3529,7 +3653,7 @@ def joint_pretrends_test( dose_col=dose_col, time_col=time_col, unit_col=unit_col, - horizons=list(pre_periods), + horizons=list(pre_periods_effective), base_period=base_period, ) G = d_arr.shape[0] @@ -3537,7 +3661,9 @@ def joint_pretrends_test( # HAD invariant: D_{g,t} = 0 for every g and every pre_period (and # for base_period - it is itself a pre-period relative to the # treatment onset). We check this on the passed-in panel subset. - needed_all_zero = list(pre_periods) + [base_period] + # Use pre_periods_effective so the consumed placebo (dropped above + # under trends_lin) is not in the dose-zero check window. + needed_all_zero = list(pre_periods_effective) + [base_period] subset_zero_check = data_filtered[data_filtered[time_col].isin(needed_all_zero)] if (subset_zero_check[dose_col] != 0).any(): n_nonzero = int((subset_zero_check[dose_col] != 0).sum()) @@ -3551,6 +3677,54 @@ def joint_pretrends_test( f"anchor." ) + # ---- Apply trends_lin detrending (paper Eq 17 / Eq 18). + # base_minus_1_period was computed and validated above (before the + # consumed-placebo drop). Compute the per-group slope and apply the + # `(t - base) × slope` adjustment to each remaining horizon. + if trends_lin: + # Extract Y[g, base] and Y[g, base-1] in unit_col-sorted order + # (matching d_arr / dy_by_horizon ordering produced by + # _aggregate_for_joint_test's wide-pivot sort by index). + slope_subset = data_filtered[ + data_filtered[time_col].isin([base_period, base_minus_1_period]) + ] + wide_y = slope_subset.pivot(index=unit_col, columns=time_col, values=outcome_col) + wide_y = wide_y.sort_index() + if wide_y[base_period].isna().any() or wide_y[base_minus_1_period].isna().any(): + raise ValueError( + f"joint_pretrends_test(trends_lin=True): NaN value(s) " + f"in outcome at base_period={base_period!r} or " + f"base_period-1={base_minus_1_period!r}. The slope " + f"estimator requires complete observations at both " + f"periods for every unit." + ) + slope = wide_y[base_period].to_numpy(dtype=np.float64) - wide_y[ + base_minus_1_period + ].to_numpy(dtype=np.float64) + # PR #392 R4 P0: build the detrending rank from OBSERVED + # periods (on data_filtered), not from the full categorical + # dtype. Otherwise unused intermediate categorical levels + # silently change the (t - base) multiplier and corrupt the + # joint statistic. Mirrors HAD.fit's + # `_aggregate_multi_period_first_differences` convention which + # uses `sorted(t_pre_list + t_post_list, ...)` for the + # event-time rank. + observed_rank = { + p: i + for i, p in enumerate( + sorted( + set(data_filtered[time_col].unique()), + key=lambda p: period_rank[p], + ) + ) + } + base_rank_observed = observed_rank[base_period] + # Apply detrending in place to remaining dy_by_horizon entries. + for t in pre_periods_effective: + label = str(t) + delta = observed_rank[t] - base_rank_observed # < 0 for pre-periods + dy_by_horizon[label] = dy_by_horizon[label] - delta * slope + # Phase 4.5 C: aggregate per-row weights/survey to per-unit (G,) # using the existing HAD helpers (constant-within-unit invariant # enforced; replicate-weight rejected on the survey path). @@ -3653,6 +3827,7 @@ def joint_homogeneity_test( survey_design: Any = None, survey: Any = None, weights: Optional[np.ndarray] = None, + trends_lin: bool = False, ) -> StuteJointResult: """Joint Stute homogeneity-linearity test (paper Section 4.3 joint). @@ -3695,6 +3870,22 @@ def joint_homogeneity_test( DEPRECATED alias for the per-row pweight shortcut. Prefer ``survey_design=SurveyDesign(weights='col_name')`` against your dataframe instead. Will be removed in the next minor release. + trends_lin : bool, default False, keyword-only + When ``True``, applies paper page-32 linear-trend detrending: + per-group slope estimated as ``Y[g, base] - Y[g, base - 1]`` + and applied to each post-period horizon's outcome evolution as + ``(t - base) × slope`` (forward extrapolation into post). Same + slope estimator as :func:`joint_pretrends_test`. Mirrors R + ``DIDHAD::did_had(..., trends_lin=TRUE)`` on its joint + homogeneity surface (paper Section 4.3, Pierce-Schott p=0.40 + anchor). **Requires** ``base_period`` to equal the last + validated pre-period (``t_pre_list[-1]``, the canonical + ``F-1`` anchor) AND ``F-2`` to be present in the panel so + the slope is identified. Direct callers passing a non- + terminal base get a ``ValueError`` — Eq 17 / R both anchor + at ``F-1``. Mutually exclusive with survey weighting; raises + ``NotImplementedError`` if combined. Default ``False`` + preserves bit-exact backcompat. Returns ------- @@ -3721,6 +3912,19 @@ def joint_homogeneity_test( if survey_design is not None and survey is None: survey = survey_design + # ---- trends_lin × survey_design gate (PR #389 / Phase 4 R-parity). + # Twin of joint_pretrends_test guard. ---- + if trends_lin and (survey is not None or weights is not None): + raise NotImplementedError( + "joint_homogeneity_test(trends_lin=True) is not yet " + "supported with survey weighting (`survey_design=` / " + "`survey=` / `weights=`). The per-group slope estimator's " + "weighted variant is not derived from the paper. Use " + "trends_lin=True WITHOUT survey weights, or use survey " + "weights WITHOUT trends_lin. Tracked in TODO.md as a " + "follow-up if user demand emerges." + ) + if len(post_periods) == 0: raise ValueError( "post_periods must be non-empty. Workflow dispatch handles " @@ -3768,6 +3972,14 @@ def joint_homogeneity_test( # time-varying post-dose would make the per-horizon refit on # `[1, D_g]` misspecify the regressor. n_periods = int(data[time_col].nunique()) + if trends_lin and n_periods < 3: + raise ValueError( + f"joint_homogeneity_test(trends_lin=True) requires a " + f"panel with at least 3 distinct time periods so the " + f"per-group slope Y[g, base] - Y[g, base - 1] is " + f"identified. Got n_periods={n_periods}." + ) + base_minus_1_period_validated: Any = None # set inside validator block under trends_lin data_filtered: pd.DataFrame = data if n_periods >= 3: F_val, t_pre_list, t_post_list, data_filtered, _filter_info = ( @@ -3799,6 +4011,30 @@ def joint_homogeneity_test( f"periods. Not-post entries: {not_post!r}. Validator's " f"post-period set: {list(t_post_list)!r}." ) + # PR #392 R3 P1 (non-terminal base guard + observed-period + # base-1 lookup, twin of joint_pretrends_test). Eq 17 anchors + # at F-1 and uses Y[F-1] - Y[F-2] as slope; require base == + # t_pre_list[-1] AND derive base-1 from t_pre_list[-2]. + if trends_lin and base_period != t_pre_list[-1]: + raise ValueError( + f"joint_homogeneity_test(trends_lin=True) requires " + f"base_period to equal the last validated pre-period " + f"({t_pre_list[-1]!r}, the canonical Eq 17 anchor " + f"F-1). Got base_period={base_period!r}. Anchoring at " + f"any other pre-period would compute a different " + f"slope and detrending that does not match paper " + f"Eq 17 / page 32 or R DIDHAD::did_had(trends_lin=TRUE)." + ) + if trends_lin and len(t_pre_list) < 2: + raise ValueError( + f"joint_homogeneity_test(trends_lin=True) requires " + f"at least 2 validated pre-periods so the per-group " + f"slope Y[g, F-1] - Y[g, F-2] is identified. Got " + f"t_pre_list={list(t_pre_list)!r}." + ) + # Capture the validator's predecessor for downstream use. + if trends_lin: + base_minus_1_period_validated = t_pre_list[-2] d_arr, dy_by_horizon, _ = _aggregate_for_joint_test( data_filtered, @@ -3834,6 +4070,54 @@ def joint_homogeneity_test( f"zero-dose invariant)." ) + # ---- Apply trends_lin detrending (paper Eq 17 / page 32 joint-Stute + # post-period homogeneity null with industry-specific linear trends). + # Twin of joint_pretrends_test detrending: per-group slope from + # Y[g, base] - Y[g, base-1], applied to each post-period horizon's + # dy_t. The post-period delta = t_rank - base_rank > 0, so the + # subtraction extrapolates the linear trend FORWARD into post-periods. + if trends_lin: + # PR #392 R3 P1: use the validator's t_pre_list[-2] as the + # predecessor (captured above as base_minus_1_period_validated). + # This is robust to ordered-categorical panels with unused + # intermediate levels because the validator builds t_pre_list + # from observed contiguous pre-periods, not the full dtype + # category list. + base_minus_1_period_h = base_minus_1_period_validated + slope_subset_h = data_filtered[ + data_filtered[time_col].isin([base_period, base_minus_1_period_h]) + ] + wide_y_h = slope_subset_h.pivot(index=unit_col, columns=time_col, values=outcome_col) + wide_y_h = wide_y_h.sort_index() + if wide_y_h[base_period].isna().any() or wide_y_h[base_minus_1_period_h].isna().any(): + raise ValueError( + f"joint_homogeneity_test(trends_lin=True): NaN value(s) " + f"in outcome at base_period={base_period!r} or " + f"base_period-1={base_minus_1_period_h!r}. The slope " + f"estimator requires complete observations at both " + f"periods for every unit." + ) + slope_h = wide_y_h[base_period].to_numpy(dtype=np.float64) - wide_y_h[ + base_minus_1_period_h + ].to_numpy(dtype=np.float64) + # PR #392 R4 P0: build the detrending rank from OBSERVED + # periods on data_filtered (matching HAD.fit). Twin of + # joint_pretrends_test fix. + observed_rank_h = { + p: i + for i, p in enumerate( + sorted( + set(data_filtered[time_col].unique()), + key=lambda p: period_rank[p], + ) + ) + } + base_rank_observed_h = observed_rank_h[base_period] + for t in post_periods: + label = str(t) + delta = observed_rank_h[t] - base_rank_observed_h # > 0 for post-periods + dy_by_horizon[label] = dy_by_horizon[label] - delta * slope_h + # Phase 4.5 C: aggregate weights/survey to per-unit; thread through. # R2 P1 fix: subset row-level `weights` to data_filtered's rows BEFORE # resolution, mirroring did_had_pretest_workflow / joint_pretrends_test @@ -4027,6 +4311,7 @@ def did_had_pretest_workflow( survey_design: Any = None, survey: Any = None, weights: Optional[np.ndarray] = None, + trends_lin: bool = False, ) -> HADPretestReport: """Run the HAD pre-test workflow (paper Section 4.2-4.3). @@ -4049,9 +4334,17 @@ def did_had_pretest_workflow( users who need Yatchew robustness under multi-period data should call :func:`yatchew_hr_test` on each (base, post) pair manually. - Eq 18 linear-trend detrending (paper Section 5.2 Pierce-Schott - application) is a Phase 4 follow-up; the event-study path here - implements the simpler mean-independence / linearity nulls. + Eq 17 / Eq 18 linear-trend detrending (paper Section 5.2 Pierce- + Schott application) is now SHIPPED on the event-study path via + the ``trends_lin`` keyword-only parameter (PR #392 / Phase 4 + R-parity). When ``trends_lin=True``, this workflow forwards the + flag to both :func:`joint_pretrends_test` and + :func:`joint_homogeneity_test`; the consumed placebo at + ``base_period - 1`` is auto-dropped from step 2 and the workflow + skips step 2 (``pretrends_joint=None``) if no earlier placebo + survives. Mirrors R ``DIDHAD::did_had(..., trends_lin=TRUE)``. + Mutually exclusive with ``aggregate="overall"`` (raises + ``NotImplementedError``). Parameters ---------- @@ -4096,6 +4389,24 @@ def did_had_pretest_workflow( be removed in the next minor release. Currently routed through a synthetic trivial ``ResolvedSurveyDesign`` so the same kernel handles both paths. + trends_lin : bool, default False, keyword-only + Forwards into :func:`joint_pretrends_test` and + :func:`joint_homogeneity_test` on the event-study dispatch + path. Mirrors R ``DIDHAD::did_had(..., trends_lin=TRUE)``. + Requires ``aggregate="event_study"``; raises + ``NotImplementedError`` on ``aggregate="overall"`` (the + overall path's qug + stute + yatchew block has no + joint-pretest surface). Mutually exclusive with survey + weighting at the joint-pretest layer; the joint wrappers + raise ``NotImplementedError`` if combined. **Effective step-2 + rule under trends_lin**: the consumed placebo at + ``base_period - 1`` is dropped before step 2 is dispatched; + if no earlier placebo survives the drop (e.g., a minimal + 4-period panel with ``F=3`` where ``base_period=2`` and the + only earlier placebo at ``t=1`` is the consumed one), step 2 + is skipped (``pretrends_joint=None``) and the workflow + proceeds to step 3 (homogeneity). Default ``False`` preserves + bit-exact backcompat. Returns ------- @@ -4169,6 +4480,22 @@ def did_had_pretest_workflow( f"aggregate must be one of {list(_VALID_AGGREGATES)!r}; " f"got {aggregate!r}." ) + # ---- trends_lin scope gate (PR #392 R1 P1). + # `trends_lin=True` is only meaningful on the event-study path because + # it forwards into joint_pretrends_test / joint_homogeneity_test. The + # overall path runs qug + stute + yatchew on a 2-period panel and has + # no joint-pretest surface to receive the kwarg. Front-door reject + # rather than silently ignore. + if trends_lin and aggregate != "event_study": + raise NotImplementedError( + "did_had_pretest_workflow(trends_lin=True) requires " + "aggregate='event_study' (the trends_lin kwarg forwards " + "into the joint pretests, which only run on the event-" + "study path). The overall path's qug + stute + yatchew " + "block has no per-group slope surface; pass a multi-" + "period panel and aggregate='event_study'." + ) + # Three-way mutex on survey_design / survey / weights (data-in pattern). # R6 P1 fix: do NOT call _resolve_pretest_unit_weights on the FULL panel # here -- under aggregate='event_study' the panel may be staggered and the @@ -4307,6 +4634,21 @@ def did_had_pretest_workflow( # whose lexical and chronological order disagree (e.g. "q10" < # "q2" lexically but > chronologically). earlier_pre = list(t_pre_list[:-1]) + # PR #392 R2 P1: under trends_lin=True, the consumed placebo at + # base_period - 1 (= t_pre_list[-2] in the contiguous validated + # pre-period list) is dropped by joint_pretrends_test downstream + # because its detrended residual is mechanically zero. Pre-filter + # it here so we can preserve the EXISTING "no earlier placebo → + # pretrends_joint=None, skip step 2" verdict path (rather than + # propagating joint_pretrends_test's `ValueError("no testable + # placebo horizons remain")` and aborting the whole workflow). + # The minimal valid trends_lin event-study panel (4 periods, + # F=3, base=2, only earlier placebo at 1 = the consumed one) + # hits this path; the workflow should still run step 3 + # (homogeneity) and emit the standard "step 2 skipped" verdict. + if trends_lin and len(t_pre_list) >= 2: + consumed_placebo_period = t_pre_list[-2] + earlier_pre = [t for t in earlier_pre if t != consumed_placebo_period] # PR #376 R2 P3: when `weights=joint_weights` is forwarded to the joint # wrappers (the only joint-internal entry that takes a numpy array), # the wrapper would re-emit a DeprecationWarning. Suppress those @@ -4335,6 +4677,7 @@ def did_had_pretest_workflow( seed=seed, survey_design=joint_survey, weights=joint_weights, + trends_lin=trends_lin, ) else: pretrends_joint = None @@ -4354,6 +4697,7 @@ def did_had_pretest_workflow( seed=seed, survey_design=joint_survey, weights=joint_weights, + trends_lin=trends_lin, ) # Event-study `all_pass`. On the unweighted path, every implemented diff --git a/docs/choosing_estimator.rst b/docs/choosing_estimator.rst index 796864be..574c83f4 100644 --- a/docs/choosing_estimator.rst +++ b/docs/choosing_estimator.rst @@ -392,17 +392,36 @@ before estimation; see :doc:`api/had` for the full API and SE-regime contract. .. code-block:: python + import numpy as np + import pandas as pd from diff_diff import HeterogeneousAdoptionDiD, did_had_pretest_workflow - pretests = did_had_pretest_workflow(data, outcome_col='y', unit_col='unit', - time_col='period', dose_col='dose') + # Build a HAD-shape panel: D=0 in pre-periods (t < F), D > 0 only at F+. + rng = np.random.default_rng(42) + G, F, T = 200, 4, 5 + doses = rng.beta(0.5, 1.0, size=G) + rows = [] + for g in range(G): + for t in range(1, T + 1): + y = (rng.normal() + + (doses[g] + doses[g] ** 2) * (t >= F) + + rng.normal(0, 0.5)) + d = doses[g] if t >= F else 0.0 + rows.append({'unit': g, 'period': t, 'y': y, 'dose': d}) + had_data = pd.DataFrame(rows) + + pretests = did_had_pretest_workflow(had_data, outcome_col='y', unit_col='unit', + time_col='period', dose_col='dose', + aggregate='event_study') est = HeterogeneousAdoptionDiD() - results = est.fit(data, outcome_col='y', unit_col='unit', - time_col='period', dose_col='dose') + results = est.fit(had_data, outcome_col='y', unit_col='unit', + time_col='period', dose_col='dose', + aggregate='event_study') - print(f"Resolved estimand: {results.target_parameter}") - print(f"Estimate: {results.att:.3f}") + # Event-study results: per-horizon WAS at each event time + for e, att in zip(results.event_times, results.att): + print(f" e={e}: {att:.3f}") Efficient DiD ~~~~~~~~~~~~~ diff --git a/docs/methodology/REGISTRY.md b/docs/methodology/REGISTRY.md index 3f3239b6..7d3cf4c7 100644 --- a/docs/methodology/REGISTRY.md +++ b/docs/methodology/REGISTRY.md @@ -2498,7 +2498,8 @@ Shipped in `diff_diff/had_pretests.py` as `stute_joint_pretest()` (residuals-in - `joint_homogeneity_test(post_periods, base_period)`: `null_form="linearity"`, design matrix `[1, D]`; residuals from `OLS(Y_t - Y_base ~ 1 + D)` per post-period (paper Section 4.3 page 32 joint across post-periods, Pierce-Schott reports p=0.40). - **Note:** Sum-of-CvMs aggregation is a standard joint specification-test construction (Delgado 1993; Escanciano 2006); the paper does not prescribe an aggregation rule. Sum-of-CvMs balances power across diffuse vs concentrated alternatives and bootstraps cleanly with shared-η. - **Note:** Event-study dispatch adjudicates step 3 via joint Stute only; there is no joint Yatchew variant because the paper does not derive one. The overall two-period path still uses the Phase 3 "Stute OR Yatchew" adjudication. Users who need Yatchew-style adjacent-difference variance-ratio robustness under multi-period data can run `yatchew_hr_test` on each (base, post) pair manually. -- **Note:** Eq 18 linear-trend detrending (paper Section 5.2 Pierce-Schott application, p=0.51) is DEFERRED to Phase 4 where the Pierce-Schott replication harness exercises the exact detrending formula against the published value. The Phase 3 follow-up ships the simpler mean-independence null that paper step 2 requires. +- **Note (Phase 4 — Eq 17 / Eq 18 linear-trend detrending shipped):** `trends_lin: bool = False` (keyword-only) on `HeterogeneousAdoptionDiD.fit(aggregate="event_study")`, `joint_pretrends_test`, and `joint_homogeneity_test` (PR #389, 2026-04). Mirrors R `DIDHAD::did_had(..., trends_lin=TRUE)` (Credible-Answers/did_had v2.0.0, SHA `edc09197`). Per-group linear-trend slope estimated as `Y[g, F-1] - Y[g, F-2]` and applied as `(t - base) × slope` adjustment to per-event-time outcome evolutions (HAD.fit) or to `Y[g, t] - Y[g, base]` directly (joint pretests). The "consumed" placebo at our event-time `e=-2` is auto-dropped (R reduces max placebo lag by 1 with the same effect). Requires F ≥ 3 / `base_period - 1` in panel — front-door `ValueError` if not. Mutually exclusive with survey weighting (raises `NotImplementedError` per `feedback_per_method_survey_element_contract`; weighted slope estimator not derived from paper). Pierce-Schott published-number replication (paper p=0.51 / p=0.40 anchors) deferred indefinitely — primary analysis panel is LBD-restricted (Census FSRDC); the public-deposit proxy panel has filtering ambiguity that prevents exact published-number parity. Replaced by end-to-end R-package parity below, which is a strictly stronger correctness signal. +- **Note (R-package end-to-end parity, PR #389):** Validated against `DIDHAD` v2.0.0 (Credible-Answers/did_had, SHA `edc09197`) on the **`design="continuous_at_zero"` (Design 1') surface**, on 3 paper-derived synthetic DGPs (Uniform, Beta(2,2), Beta(0.5,1)) × 5 method combinations (overall, event-study, placebo, yatchew, trends_lin). Generator: `benchmarks/R/generate_did_had_golden.R`; fixture: `benchmarks/data/did_had_golden.json`; test: `tests/test_did_had_parity.py`. **Scope qualifier (PR #392 R8 P3):** the harness explicitly forces `HeterogeneousAdoptionDiD(design="continuous_at_zero")` because R `did_had` always evaluates the local-linear at `d=0` regardless of dose distribution. Our default `design="auto"` may legitimately resolve to `continuous_near_d_lower` (`d_lower=d.min()`, Design 1) or `mass_point` (Design 2) on dose distributions with boundary density bounded away from zero (e.g., Beta(2,2) at G=200), in which case the WAS estimand evaluates at a different point and diverges from R's `did_had` numerically. That divergence is methodologically defensible — our auto-detect uses more information when boundary mass is sparse — but is out of scope for this parity contract. Tolerances: point estimate / SE / CI bounds at `atol=1e-8`; closed-form Yatchew T-stat at `atol=1e-10` after a documented `× G/(G-1)` finite-sample convention shift. Two intentional convention deviations from R: **(a)** we report the **bias-corrected** point estimate `att = (mean(ΔY) - tau.bc) / mean(D)` (modern CCF 2018 convention); R's `Estimate` column reports the **conventional** estimate `(mean(ΔY) - tau.us) / mean(D)` with the bias-corrected CI separately — our `att` matches R's CI midpoint, our `se` / `conf_int_low` / `conf_int_high` match R's `se` / `ci_lo` / `ci_hi` directly. **(b)** Our `yatchew_hr_test` follows paper Appendix E's literal `(1/G)` and `(1/(2G))` variance-denominator convention; R's `YatchewTest::yatchew_test` uses base-R `var()`'s `(1/(N-1))` sample-variance convention. Ratio is exactly `N/(N-1)`; both converge to the same asymptotic null distribution. Yatchew on placebos with R's mean-independence null (`order=0`, fits `Y ~ 1`) is not yet exposed in our `yatchew_hr_test` (we always fit `Y ~ D`, the linearity null) and is skipped in the parity test; tracked as TODO follow-up to add a `null="mean_independence"` mode. - **Note:** Horizon labels in `StuteJointResult.horizon_labels` are `str(t)` verbatim and carry STRING IDENTITY ONLY — NOT a chronological ordering key. Callers who need chronological order must preserve the original period values alongside (e.g. from the `pre_periods` / `post_periods` argument). - **Note:** NaN propagation is explicit: when any horizon has NaN in residuals, `cvm_stat_joint=NaN`, `p_value=NaN`, `reject=False`, AND `per_horizon_stats={label: np.nan for every horizon}` (full dict preserved with NaN values — not empty, not partial). diff --git a/tests/test_did_had_parity.py b/tests/test_did_had_parity.py new file mode 100644 index 00000000..31cc9dbf --- /dev/null +++ b/tests/test_did_had_parity.py @@ -0,0 +1,502 @@ +"""Cross-language end-to-end parity tests against R `DIDHAD::did_had`. + +HAD Phase 4 (PR #389): verifies that Python +``HeterogeneousAdoptionDiD.fit()`` (overall, event-study, placebo, +yatchew, trends_lin) and ``yatchew_hr_test()`` match R +``DIDHAD::did_had()`` v2.0.0 bit-exactly on shared synthetic input. + +Tolerances (per Phase 4 plan): +- Point / SE / CI bounds: ``atol=1e-8`` (full pipeline through nprobust + numerical paths). +- Yatchew T-stat (closed-form ratio): ``atol=1e-10``. +- Per-horizon arrays: shape exact, values at the appropriate per-field + tolerance. + +Point-estimate convention deviation (documented in REGISTRY.md): + +R `DIDHAD::did_had` reports the CONVENTIONAL local-linear estimate in +its ``Estimate`` column (= ``(mean(ΔY) - tau.us) / mean(D)``) and +constructs the BIAS-CORRECTED CI separately. Python `HAD.fit` reports +the BIAS-CORRECTED estimate directly in ``att`` (= ``(mean(ΔY) - tau.bc) +/ mean(D)``). Both are valid Calonico-Cattaneo-Farrell (2018) +constructions; the CCF paper shows the BC CI has correct coverage even +when applied to the conventional point estimate. + +For parity testing, ``Python att`` matches ``R (ci_lo + ci_hi) / 2`` +(R's CI midpoint, which is the bias-corrected location). ``Python se``, +``conf_int_low``, ``conf_int_high`` match R's ``se``, ``ci_lo``, +``ci_hi`` directly. + +Fixture generated by ``benchmarks/R/generate_did_had_golden.R``. +Guard per ``feedback_golden_file_pytest_skip``: CI isolated-install jobs +copy ``tests/`` only, not ``benchmarks/data/``, so a missing fixture +downgrades to pytest.skip rather than fail. + +R-side row index → our event-time convention mapping: + R row "Effect_i" (ID = +i) → our event time e = i - 1 + R row "Placebo_i" (ID = -i) → + without trends_lin: our event time e = -(i + 1) + with trends_lin: our event time e = -(i + 2) + +The convention shift on the placebo side mirrors R's anchor swap +(F-1 anchor without trends_lin; F-2 anchor with trends_lin); on our +end the F-1 anchor is preserved and the math collapses to a uniform +``adjusted_dy[e] = dy_dict[e] - (e+1) × slope`` adjustment with the +``e=-2`` placebo dropped (R's "consumed" placebo). +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any, Dict, List, Tuple + +import numpy as np +import pandas as pd +import pytest + +from diff_diff import HeterogeneousAdoptionDiD +from diff_diff.had_pretests import yatchew_hr_test + +FIXTURE_PATH = Path(__file__).parent.parent / "benchmarks" / "data" / "did_had_golden.json" + +POINT_ATOL = 1e-8 +SE_ATOL = 1e-8 +CI_ATOL = 1e-8 +YATCHEW_ATOL = 1e-10 + + +def _load_fixture(): + if not FIXTURE_PATH.exists(): + pytest.skip( + f"Golden fixture {FIXTURE_PATH} missing — regenerate via " + f"`Rscript benchmarks/R/generate_did_had_golden.R`." + ) + with open(FIXTURE_PATH) as f: + return json.load(f) + + +@pytest.fixture(scope="module") +def fixture(): + return _load_fixture() + + +def _panel_from_fixture(dgp_entry: Dict[str, Any]) -> pd.DataFrame: + """Reconstruct the long-format panel DataFrame from the JSON entry. + + R serializes columns as parallel arrays; rebuild a DataFrame with + int unit/time columns to match HAD.fit's contract.""" + panel = dgp_entry["panel"] + return pd.DataFrame( + { + "g": np.asarray(panel["g"], dtype=np.int64), + "t": np.asarray(panel["t"], dtype=np.int64), + "y": np.asarray(panel["y"], dtype=np.float64), + "d": np.asarray(panel["d"], dtype=np.float64), + } + ) + + +def _r_id_to_event_time(r_id: int, trends_lin: bool) -> int: + """Map R's ID column to our event_time convention. + + R reports Effect_i with ID = +i (Effect_1 is at our e=0); Placebo_i + with ID = -i (under trends_lin=False, Placebo_1 is at our e=-2; + under trends_lin=True, Placebo_1 is at our e=-3). The placebo shift + reflects R's F-2 anchor swap under trends_lin.""" + if r_id > 0: + return r_id - 1 + placebo_lag = -r_id # 1, 2, ... + return -(placebo_lag + (2 if trends_lin else 1)) + + +def _python_fit( + panel: pd.DataFrame, + effects: int, + placebo: int, + trends_lin: bool, +) -> Any: + """Run HAD.fit on the same panel + same options as R. + + Forces ``design="continuous_at_zero"`` (Design 1', d_lower=0) to + match R's ``did_had`` which always evaluates the local linear at + ``d=0`` regardless of dose distribution. Our auto-detect would + otherwise pick Design 1 (``continuous_near_d_lower``, + ``d_lower=d.min()``) for dose distributions with boundary density + bounded away from zero (e.g., Beta(2,2)), producing different + point estimates. + + For the ``overall_e1`` combo (effects=1, placebo=0, trends_lin=False) + we slice the panel to exactly two periods (F-1 and F, where F=4 in + our fixture) and route through ``aggregate="overall"`` — that + exercises the actual two-period overall code path, not the + event-study path with a single horizon. PR #392 R1 P2 fix.""" + est = HeterogeneousAdoptionDiD(design="continuous_at_zero") + if effects == 1 and placebo == 0 and not trends_lin: + # Slice to the two periods (F-1=3, F=4) that R's effects=1 case + # actually consumes for Effect_1 = Y[F] - Y[F-1]. + F = int(panel.loc[panel["d"] > 0, "t"].min()) + panel_2p = panel[panel["t"].isin([F - 1, F])].copy() + return est.fit( + panel_2p, + outcome_col="y", + dose_col="d", + time_col="t", + unit_col="g", + aggregate="overall", + ) + return est.fit( + panel, + outcome_col="y", + dose_col="d", + time_col="t", + unit_col="g", + aggregate="event_study", + trends_lin=trends_lin, + ) + + +def _as_list(x: Any) -> list: + """jsonlite::write_json with auto_unbox=TRUE collapses single-element + vectors to scalars; rewrap them as 1-element lists so iteration is + uniform across single-horizon and multi-horizon results.""" + if isinstance(x, (list, tuple)): + return list(x) + return [x] + + +def _zip_r_python( + r_result: Dict[str, Any], py_result: Any, trends_lin: bool +) -> List[Tuple[int, int, str]]: + """Build (r_row_idx, py_event_idx, r_rowname) tuples zipping R rows + to Python event-time positions for parity assertions. + + PR #392 R5 P3: also asserts the EXACT mapped event-time set is a + subset of Python's ``event_times`` and that the mapping is total + over R's reported rows (no R row maps to a missing Python + horizon). This catches future horizon-shape regressions where + Python silently drops an event-time the R fixture lists.""" + py_event_times = py_result.event_times.tolist() + py_idx_by_event_time = {int(e): i for i, e in enumerate(py_event_times)} + pairs = [] + r_event_ids = _as_list(r_result["event_id"]) + r_rownames = _as_list(r_result["rownames"]) + expected_event_times = [] + for i, (r_id, rowname) in enumerate(zip(r_event_ids, r_rownames)): + e = _r_id_to_event_time(int(r_id), trends_lin) + expected_event_times.append(e) + if e not in py_idx_by_event_time: + raise AssertionError( + f"R row {rowname!r} (ID={r_id}) maps to our e={e}, but " + f"Python event_times = {py_event_times}. Mapping bug?" + ) + pairs.append((i, py_idx_by_event_time[e], rowname)) + # PR #392 R6 P3: exact set equality between R-mapped horizons and + # Python's event_times (was: subset inclusion). Catches + # horizon-selection regressions in BOTH directions: + # - missing_in_python: Python silently dropped a horizon R requested + # - extra_in_python: Python emitted an extra horizon R did not + # request (e.g. effects/placebo cap drift in our event_study path) + expected_set = set(expected_event_times) + py_set = set(py_event_times) + missing_in_python = expected_set - py_set + extra_in_python = py_set - expected_set + assert not missing_in_python and not extra_in_python, ( + f"event_times set-equality mismatch: " + f"R-mapped {sorted(expected_set)}; Python emitted " + f"{sorted(py_event_times)}; missing in Python: " + f"{sorted(missing_in_python)}; extra in Python: " + f"{sorted(extra_in_python)}." + ) + return pairs + + +# --------------------------------------------------------------------------- +# DGPs and combos enumerated from the fixture metadata. +# --------------------------------------------------------------------------- + +DGP_NAMES = ["uniform_G200_F4_T5", "beta22_G200_F4_T5", "boundary_G200_F4_T5"] + +# Combos that produce a HAD point/SE/CI block (excludes pure-yatchew rows +# which are validated separately). +POINT_COMBOS = [ + ("overall_e1", 1, 0, False), + ("event_e2_p2", 2, 2, False), + ("event_e2_p2_yatchew", 2, 2, False), + ("event_e2_p2_trendslin", 2, 2, True), + ("event_e2_p2_yatchew_trendslin", 2, 2, True), +] + +YATCHEW_COMBOS = [ + ("event_e2_p2_yatchew", 2, 2, False), + ("event_e2_p2_yatchew_trendslin", 2, 2, True), +] + + +@pytest.fixture(scope="module") +def panels(fixture): + """Per-DGP DataFrames built from the JSON fixture.""" + return {name: _panel_from_fixture(fixture["fixtures"][name]) for name in DGP_NAMES} + + +# --------------------------------------------------------------------------- +# Test classes — separated by mode for clean per-field shape handling. +# --------------------------------------------------------------------------- + + +class TestPointSEParity: + """Point estimate / SE / CI parity vs R DIDHAD across all method combos.""" + + @pytest.mark.parametrize( + "dgp_name,combo_name,effects,placebo,trends_lin", + [(dgp, name, eff, pla, tl) for dgp in DGP_NAMES for name, eff, pla, tl in POINT_COMBOS], + ) + def test_point_se_ci_parity( + self, fixture, panels, dgp_name, combo_name, effects, placebo, trends_lin + ): + r_combo = fixture["fixtures"][dgp_name]["combos"][combo_name] + r_result = r_combo["result"] + py = _python_fit(panels[dgp_name], effects, placebo, trends_lin) + + # `overall_e1` returns a scalar HeterogeneousAdoptionDiDResults + # (aggregate="overall" path); all others return the array + # HeterogeneousAdoptionDiDEventStudyResults. Build a uniform + # (rowname, py_att, py_se, py_ci_lo, py_ci_hi) iterator that + # works on both. + r_se_list = _as_list(r_result["se"]) + r_ci_lo_list = _as_list(r_result["ci_lo"]) + r_ci_hi_list = _as_list(r_result["ci_hi"]) + if combo_name == "overall_e1": + # R's overall_e1 has exactly one row (Effect_1 → ID=1 → e=0). + r_idx = 0 + rowname = "Effect_1" + py_att = float(py.att) + py_se = float(py.se) + py_ci_lo = float(py.conf_int[0]) + py_ci_hi = float(py.conf_int[1]) + iterations = [(r_idx, rowname, py_att, py_se, py_ci_lo, py_ci_hi)] + else: + pairs = _zip_r_python(r_result, py, trends_lin) + iterations = [] + for r_idx, py_idx, rowname in pairs: + iterations.append( + ( + r_idx, + rowname, + float(py.att[py_idx]), + float(py.se[py_idx]), + float(py.conf_int_low[py_idx]), + float(py.conf_int_high[py_idx]), + ) + ) + + for r_idx, rowname, py_att, py_se, py_ci_lo, py_ci_hi in iterations: + r_se = r_se_list[r_idx] + r_ci_lo = r_ci_lo_list[r_idx] + r_ci_hi = r_ci_hi_list[r_idx] + # R's `Estimate` column is the CONVENTIONAL (non-bias- + # corrected) point estimate; the bias-corrected location + # is the CI midpoint. Python ships the bias-corrected + # location directly in `att`. Compare to CI midpoint. + r_att_bc = 0.5 * (r_ci_lo + r_ci_hi) + + np.testing.assert_allclose( + py_att, + r_att_bc, + atol=POINT_ATOL, + rtol=0, + err_msg=( + f"{dgp_name}/{combo_name}/{rowname}: bias-corrected " + f"estimate mismatch (Python att vs R CI midpoint)" + ), + ) + np.testing.assert_allclose( + py_se, + r_se, + atol=SE_ATOL, + rtol=0, + err_msg=f"{dgp_name}/{combo_name}/{rowname}: SE mismatch", + ) + np.testing.assert_allclose( + py_ci_lo, + r_ci_lo, + atol=CI_ATOL, + rtol=0, + err_msg=f"{dgp_name}/{combo_name}/{rowname}: ci_lo mismatch", + ) + np.testing.assert_allclose( + py_ci_hi, + r_ci_hi, + atol=CI_ATOL, + rtol=0, + err_msg=f"{dgp_name}/{combo_name}/{rowname}: ci_hi mismatch", + ) + + +class TestYatchewParity: + """Yatchew T-stat parity vs R `DIDHAD::did_had(yatchew=TRUE)`. + + R computes the trends-adjusted Effect_i / Placebo_i internally and + runs Yatchew on each horizon. Python parity reproduces the same + trends-adjusted dy values via the HAD pipeline and runs + `yatchew_hr_test` on each horizon. + + Convention deviation (documented in REGISTRY.md): + Our `yatchew_hr_test` follows paper Appendix E literally with + the (1/G) population-variance convention: + sigma2_lin = sum(eps^2) / G + sigma2_diff = sum(diff_dy^2) / (2G) + R's `YatchewTest::yatchew_test` uses base R's `var()` (1/(N-1) + sample-variance) and `mean()` conventions, which scale the + numerator by N/(N-1) relative to ours. Both converge to the + same asymptotic distribution; for finite samples they differ + by exactly N/(N-1). + + Parity is asserted at atol=1e-10 AFTER applying the convention + shift: ``R_T = py_T_hr × G / (G-1)``. This is bit-exact parity + against R's reported T-stat under the documented convention + transformation. + + Placebo rows use a DIFFERENT null in R: per the DIDHAD README, + the yatchew flag on placebo tests "the null being tested is that + groups' F-1 to F-1-ℓ outcome evolution is mean independent of + their F-1+ℓ treatment". R's `YatchewTest::yatchew_test(order=0)` + fits Y ~ 1 (intercept only) instead of Y ~ D, producing a + fundamentally different residual. Our `yatchew_hr_test` always + fits Y ~ D (the linearity null). For parity we restrict to the + EFFECT rows (post-treatment, where both R and Python use the + Y ~ D linearity null). Placebo Yatchew parity requires a separate + Python helper exposing the mean-independence null and is + deferred.""" + + @pytest.mark.parametrize( + "dgp_name,combo_name,effects,placebo,trends_lin", + [(dgp, name, eff, pla, tl) for dgp in DGP_NAMES for name, eff, pla, tl in YATCHEW_COMBOS], + ) + def test_yatchew_t_stat_parity( + self, fixture, panels, dgp_name, combo_name, effects, placebo, trends_lin + ): + r_combo = fixture["fixtures"][dgp_name]["combos"][combo_name] + r_result = r_combo["result"] + # PR #392 R5 P3: assert R's reported (effects + placebo) row + # count matches the parametrize spec — catches future fixture + # drift where R's effects/placebo args don't actually drive + # the row count we expect. + n_yatchew_rows = len(_as_list(r_result["yatchew_t"])) + # Under trends_lin, R drops one placebo (consumed). Otherwise + # rows = effects + placebo (the auto-truncation cap from R is + # capped at the panel's max via did_het_adoption_main). + expected_rows = effects + placebo - (1 if trends_lin else 0) + assert n_yatchew_rows == expected_rows, ( + f"R fixture row count for {combo_name} = {n_yatchew_rows}, " + f"expected effects+placebo{'-1' if trends_lin else ''} = " + f"{expected_rows}; fixture/combo spec drift?" + ) + if "yatchew_t" not in r_result: + pytest.fail( + f"{combo_name} expected to have yatchew_t in fixture; " + f"check generate_did_had_golden.R" + ) + + # Reproduce trends-adjusted per-event-time dy in Python by + # extracting the dy_dict that HAD.fit consumes internally. We + # rebuild via the same _aggregate_multi_period_first_differences + # path + apply trends_lin detrending. + from diff_diff.had import _aggregate_multi_period_first_differences + + panel = panels[dgp_name] + F = int(panel.loc[panel["d"] > 0, "t"].min()) + all_periods = sorted(panel["t"].unique().tolist()) + t_pre_list = [t for t in all_periods if t < F] + t_post_list = [t for t in all_periods if t >= F] + d_arr, dy_dict, _, _, _ = _aggregate_multi_period_first_differences( + panel, + "y", + "d", + "t", + "g", + F, + t_pre_list, + t_post_list, + None, + ) + if trends_lin: + slope = -dy_dict[-2] + dy_dict = {e: dy_dict[e] - (e + 1) * slope for e in dy_dict.keys()} + del dy_dict[-2] + + # Build a (r_row_idx → our event_time) map and run Yatchew per + # horizon. R reports yatchew_test rows in the SAME ORDER as + # resmat (Effect_1, Effect_2, ..., Placebo_1, ...). + r_yatchew_t = _as_list(r_result["yatchew_t"]) + r_yatchew_n = _as_list(r_result["yatchew_n"]) + r_event_ids = _as_list(r_result["event_id"]) + for r_idx, r_id in enumerate(r_event_ids): + e = _r_id_to_event_time(int(r_id), trends_lin) + if e not in dy_dict: + continue + # Skip placebo rows: R uses `order=0` (Y ~ 1, intercept-only + # baseline, mean-independence null) on placebos; our + # `yatchew_hr_test` always fits Y ~ D (linearity null). + # Different test, not a parity opportunity. + if int(r_id) < 0: + continue + dy_e = dy_dict[e] + # Yatchew test on (d, dy_e) with no weights. + r = yatchew_hr_test(d_arr, dy_e) + # Apply documented convention shift: R's T = our T × G/(G-1) + # (sample-variance vs population-variance denominators). + G_horizon = int(r_yatchew_n[r_idx]) + py_t_in_r_convention = float(r.t_stat_hr) * G_horizon / (G_horizon - 1) + np.testing.assert_allclose( + py_t_in_r_convention, + float(r_yatchew_t[r_idx]), + atol=YATCHEW_ATOL, + rtol=0, + err_msg=( + f"{dgp_name}/{combo_name}/Yatchew row {r_idx} " + f"(R ID={r_id}, our e={e}, G={G_horizon}): T_hr mismatch " + f"after N/(N-1) convention shift" + ), + ) + + +class TestFixtureMetadata: + """Sanity checks on the fixture itself.""" + + def test_metadata_versions_match(self, fixture): + """Ensure the JSON metadata lists the EXACT pinned upstream + versions. PR #392 R4 P3: exact pin (not >=) so future + regeneration does not silently re-anchor the goldens to a + newer CRAN release while changelog / registry still cite the + old version. Bump these pins (here AND in + ``benchmarks/R/generate_did_had_golden.R``) when intentionally + re-anchoring.""" + meta = fixture["metadata"] + assert meta["didhad_version"] == "2.0.0", ( + f"Fixture was generated against DIDHAD={meta['didhad_version']!r}; " + f"the parity test pins exactly 2.0.0. Regenerate after bumping " + f"the pin in both the generator and this test." + ) + assert meta["yatchewtest_version"] == "1.1.1", ( + f"Fixture was generated against YatchewTest=" + f"{meta['yatchewtest_version']!r}; the parity test pins exactly " + f"1.1.1. Regenerate after bumping the pin." + ) + # PR #392 R5 P3: nprobust is on the parity contract path + # (DIDHAD's local-linear bandwidth + bias-correction calls go + # through it), so pin it exactly too. Bump in lockstep with + # the generator's stopifnot guards. + assert meta["nprobust_version"] == "0.5.0", ( + f"Fixture was generated against nprobust=" + f"{meta['nprobust_version']!r}; the parity test pins exactly " + f"0.5.0. Regenerate after bumping the pin." + ) + + def test_metadata_n_dgps(self, fixture): + meta = fixture["metadata"] + assert meta["n_dgps"] == len(DGP_NAMES) == 3 + + def test_all_dgps_present(self, fixture): + for name in DGP_NAMES: + assert name in fixture["fixtures"], f"missing DGP {name!r}" diff --git a/tests/test_had_pretests.py b/tests/test_had_pretests.py index b122e339..dac0e84a 100644 --- a/tests/test_had_pretests.py +++ b/tests/test_had_pretests.py @@ -4242,3 +4242,867 @@ def test_workflow_overall_fpc_only_survey_smoke(self): assert report.qug is None assert report.stute is not None and np.isfinite(report.stute.p_value) assert report.yatchew is not None and np.isfinite(report.yatchew.p_value) + + +class TestJointPretestsTrendsLin: + """Direct unit tests for trends_lin=True on joint_pretrends_test and + joint_homogeneity_test (PR #389 / Phase 4 R-parity). + + Locks: anchor-period guard, naive-Python detrending baseline at + atol=1e-12, survey_design × trends_lin NotImplementedError, default + bit-exact backcompat. R-parity is exercised by the separate + test_did_had_parity.py module.""" + + @staticmethod + def _panel(rng_seed: int = 11, G: int = 200, F: int = 4, T: int = 5) -> pd.DataFrame: + """Build a 5-period (default) panel with F=4. Beta(0.5, 1) doses + give heavy density near zero; per-unit linear trends ensure + trends_lin=True changes the test statistic vs trends_lin=False.""" + rng = np.random.default_rng(rng_seed) + d = rng.beta(0.5, 1.0, size=G) + unit_fe = rng.normal(0, 1, G) + trend = rng.normal(0.1, 0.05, G) + rows = [] + for g in range(G): + for t in range(1, T + 1): + treated = t >= F + y = ( + unit_fe[g] + + trend[g] * (t - 1) + + (d[g] + d[g] ** 2) * treated + + rng.normal(0, 0.5) + ) + dose = d[g] if treated else 0.0 + rows.append({"unit": g, "time": t, "y": y, "d": dose}) + return pd.DataFrame(rows) + + def test_pretrends_default_bit_exact_backcompat(self): + """trends_lin=False (default) preserves pre-PR numerics exactly.""" + df = self._panel(rng_seed=7) + r1 = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1, 2], + base_period=3, + n_bootstrap=99, + seed=42, + ) + r2 = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1, 2], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=False, + ) + assert r1.cvm_stat_joint == r2.cvm_stat_joint + assert r1.p_value == r2.p_value + + def test_homogeneity_default_bit_exact_backcompat(self): + df = self._panel(rng_seed=8) + r1 = joint_homogeneity_test( + df, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=3, + n_bootstrap=99, + seed=42, + ) + r2 = joint_homogeneity_test( + df, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=False, + ) + assert r1.cvm_stat_joint == r2.cvm_stat_joint + assert r1.p_value == r2.p_value + + def test_pretrends_trends_lin_changes_stat(self): + """Per-unit linear trends in DGP → trends_lin=True changes CvM.""" + df = self._panel(rng_seed=9) + r_no = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1], + base_period=3, + n_bootstrap=99, + seed=42, + ) + r_yes = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + assert r_no.cvm_stat_joint != r_yes.cvm_stat_joint + + def test_pretrends_trends_lin_naive_baseline(self): + """trends_lin detrending matches a hand-coded naive Python baseline. + + Detrended dy_t = (Y[g,t] - Y[g,base]) - (t - base) × (Y[g,base] - Y[g,base-1]). + Build the detrended outcome manually as a new column and confirm + joint_pretrends_test on the manual column with trends_lin=False + matches joint_pretrends_test on raw Y with trends_lin=True at + atol=1e-12 (deterministic given fixed seed).""" + df = self._panel(rng_seed=10) + base, base_minus_1 = 3, 2 + # Manual detrending: replace y with detrended y. + wide = df.pivot(index="unit", columns="time", values="y").sort_index() + slope = wide[base].to_numpy() - wide[base_minus_1].to_numpy() + # Apply Y_detrended[t] = Y[t] - (t - base) × slope per row. + df_manual = df.copy() + df_manual = df_manual.merge( + pd.DataFrame({"unit": np.arange(len(slope)), "slope": slope}), + on="unit", + ) + df_manual["y"] = df_manual["y"] - (df_manual["time"] - base) * df_manual["slope"] + df_manual = df_manual.drop(columns=["slope"]) + + # joint_pretrends_test on manual-detrended y, trends_lin=False + r_manual = joint_pretrends_test( + df_manual, + "y", + "d", + "time", + "unit", + pre_periods=[1], + base_period=base, + n_bootstrap=99, + seed=42, + ) + # joint_pretrends_test on raw y, trends_lin=True + r_auto = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1], + base_period=base, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + np.testing.assert_allclose( + r_auto.cvm_stat_joint, + r_manual.cvm_stat_joint, + atol=1e-12, + rtol=0, + ) + + def test_pretrends_trends_lin_consumed_placebo_dropped_with_warning(self): + """When pre_periods includes base_period-1, trends_lin drops it + (the consumed placebo). Warning fires; remaining horizons get + their normal detrending. Regression for PR #392 R1 P0.""" + df = self._panel(rng_seed=30) + # Panel has periods 1..5, F=4. Base=3, base-1=2. + # Caller passes [1, 2] which includes the consumed placebo at 2. + with pytest.warns(UserWarning, match=r"dropping period .*2.*consumed.*placebo"): + r = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1, 2], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + # Result keeps only the surviving horizon (label "1"). + assert list(r.horizon_labels) == ["1"], ( + f"Expected only horizon '1' after dropping consumed placebo, " + f"got {list(r.horizon_labels)}" + ) + + def test_pretrends_trends_lin_only_consumed_raises(self): + """When pre_periods is exactly [base_period-1], trends_lin has + no testable horizons left → ValueError. Regression for PR #392 + R1 P0 (concrete fix step 2).""" + df = self._panel(rng_seed=31) + # Panel periods 1..5, F=4. Base=3, base-1=2. + # Caller passes only [2] (the consumed placebo). + with pytest.raises(ValueError, match="no testable placebo horizons remain"): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[2], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + + def test_pretrends_trends_lin_no_consumed_in_pre_periods_no_warning(self): + """When pre_periods does NOT include base_period-1, no consumed- + placebo warning fires (negative regression).""" + df = self._panel(rng_seed=32) + # Panel periods 1..5, F=4. Base=3, base-1=2. Pass only [1]. + with warnings.catch_warnings(): + warnings.simplefilter("error", UserWarning) + r = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + assert list(r.horizon_labels) == ["1"] + + def test_homogeneity_trends_lin_naive_baseline(self): + df = self._panel(rng_seed=12) + base, base_minus_1 = 3, 2 + wide = df.pivot(index="unit", columns="time", values="y").sort_index() + slope = wide[base].to_numpy() - wide[base_minus_1].to_numpy() + df_manual = df.copy() + df_manual = df_manual.merge( + pd.DataFrame({"unit": np.arange(len(slope)), "slope": slope}), + on="unit", + ) + df_manual["y"] = df_manual["y"] - (df_manual["time"] - base) * df_manual["slope"] + df_manual = df_manual.drop(columns=["slope"]) + + r_manual = joint_homogeneity_test( + df_manual, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=base, + n_bootstrap=99, + seed=42, + ) + r_auto = joint_homogeneity_test( + df, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=base, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + np.testing.assert_allclose( + r_auto.cvm_stat_joint, + r_manual.cvm_stat_joint, + atol=1e-12, + rtol=0, + ) + + def test_homogeneity_trends_lin_missing_base_minus_1_raises(self): + """When base is the FIRST period in panel (no base-1), trends_lin + must error. joint_homogeneity_test is the reachable surface for + this guard: it allows base to be a pre-period AND the earliest + period (joint_pretrends_test additionally requires pre_periods < + base, which structurally forces base-1 to exist if any pre_period + is in the panel).""" + # Panel periods [3, 4, 5], F=4, base=3 (earliest, last pre-period + # before treatment), post=[4, 5]. + df = self._panel(rng_seed=14, F=4, T=5) + df_trim = df[df["time"] >= 3].copy() + with pytest.raises(ValueError, match=r"at least 2 (validated )?pre-periods"): + joint_homogeneity_test( + df_trim, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + + def test_pretrends_trends_lin_with_survey_design_raises(self): + from diff_diff import SurveyDesign + + df = self._panel(rng_seed=15) + df["w"] = 1.0 + with pytest.raises(NotImplementedError, match="trends_lin=True.*survey"): + joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1, 2], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + survey_design=SurveyDesign(weights="w"), + ) + + def test_pretrends_trends_lin_with_weights_alias_raises(self): + df = self._panel(rng_seed=16) + with pytest.raises(NotImplementedError, match="trends_lin=True.*survey"): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1, 2], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + weights=np.ones(len(df)), + ) + + def test_homogeneity_trends_lin_with_survey_design_raises(self): + from diff_diff import SurveyDesign + + df = self._panel(rng_seed=17) + df["w"] = 1.0 + with pytest.raises(NotImplementedError, match="trends_lin=True.*survey"): + joint_homogeneity_test( + df, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + survey_design=SurveyDesign(weights="w"), + ) + + def test_workflow_trends_lin_forwards_to_joint_wrappers(self): + """`did_had_pretest_workflow(aggregate='event_study', trends_lin=True)` + must forward trends_lin to both joint pretests AND match the + direct-surface call. Regression for PR #392 R1 P1.""" + df = self._panel(rng_seed=33) + # Run the workflow with trends_lin=True (event_study path). + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + report = did_had_pretest_workflow( + df, + "y", + "d", + "time", + "unit", + aggregate="event_study", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + # Direct calls to the joint wrappers, same params, trends_lin=True. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + r_pretrends_direct = joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1, 2], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + r_homogeneity_direct = joint_homogeneity_test( + df, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=3, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + # Workflow's joint pretests must match the direct calls bit-exactly. + assert report.pretrends_joint is not None + assert report.homogeneity_joint is not None + assert report.pretrends_joint.cvm_stat_joint == r_pretrends_direct.cvm_stat_joint + assert report.homogeneity_joint.cvm_stat_joint == r_homogeneity_direct.cvm_stat_joint + + def test_workflow_trends_lin_minimal_panel_skips_step2_gracefully(self): + """Minimal valid trends_lin event-study panel: 4 periods, F=3 + (so t_pre_list=[1,2], base=2, base-1=1 is the only earlier + placebo AND the consumed one). Workflow should set + pretrends_joint=None (step 2 skipped) and still run step 3 + (homogeneity). Regression for PR #392 R2 P1. + """ + rng = np.random.default_rng(35) + G = 200 + T = 4 + F = 3 # treatment onset at t=3 → pre={1,2}, post={3,4} + d = rng.beta(0.5, 1.0, size=G) + unit_fe = rng.normal(0, 1, G) + trend = rng.normal(0.1, 0.05, G) + rows = [] + for g in range(G): + for t in range(1, T + 1): + treated = t >= F + y = ( + unit_fe[g] + + trend[g] * (t - 1) + + (d[g] + d[g] ** 2) * treated + + rng.normal(0, 0.5) + ) + dose = d[g] if treated else 0.0 + rows.append({"unit": g, "time": t, "y": y, "d": dose}) + df = pd.DataFrame(rows) + # Run workflow with trends_lin=True. Should not raise. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + report = did_had_pretest_workflow( + df, + "y", + "d", + "time", + "unit", + aggregate="event_study", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + # Step 2 (pretrends) skipped (the only earlier placebo at base-1 + # is the consumed one); step 3 (homogeneity) still runs. + assert report.pretrends_joint is None, ( + "expected pretrends_joint=None on the minimal-panel trends_lin " + "case where the only earlier placebo is the consumed one" + ) + assert ( + report.homogeneity_joint is not None + ), "expected homogeneity_joint to still run after step 2 skip" + assert np.isfinite(report.homogeneity_joint.p_value) + + def test_pretrends_trends_lin_nonterminal_base_raises(self): + """Direct caller passing base_period < t_pre_list[-1] under + trends_lin=True must raise — Eq 17 anchors at F-1. + Regression for PR #392 R3 P1 (methodology guard).""" + df = self._panel(rng_seed=40) + # Panel periods 1..5, F=4. t_pre_list = [1, 2, 3], F-1 = 3. + # Pass base_period=2 (non-terminal pre-period). + with pytest.raises(ValueError, match="last validated pre-period"): + joint_pretrends_test( + df, + "y", + "d", + "time", + "unit", + pre_periods=[1], + base_period=2, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + + def test_homogeneity_trends_lin_nonterminal_base_raises(self): + """Twin guard for joint_homogeneity_test.""" + df = self._panel(rng_seed=41) + with pytest.raises(ValueError, match="last validated pre-period"): + joint_homogeneity_test( + df, + "y", + "d", + "time", + "unit", + post_periods=[4, 5], + base_period=2, + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + + def test_pretrends_trends_lin_unused_categorical_observed_only(self): + """Ordered categorical time column with an unused intermediate + level: trends_lin must resolve base_period - 1 to the previous + OBSERVED period, not to the unused level (which would KeyError + on the slope-pivot lookup). Regression for PR #392 R3 P1.""" + df_int = self._panel(rng_seed=42) + # Convert time to ordered categorical with an unused intermediate + # level inserted between observed levels t3 and t4. + cat_levels = ["t1", "t2", "t3", "t_unused", "t4", "t5"] + time_map = {1: "t1", 2: "t2", 3: "t3", 4: "t4", 5: "t5"} + df = df_int.copy() + df["time"] = pd.Categorical( + df["time"].map(time_map), + categories=cat_levels, + ordered=True, + ) + # Sanity: t_unused is in the dtype but absent from data. + assert "t_unused" in df["time"].cat.categories + assert "t_unused" not in set(df["time"].dropna().unique()) + # F=4 → t_pre_list = [t1, t2, t3], base must equal t3 under + # trends_lin. Under the OLD period_rank lookup, base_minus_1 + # by rank would resolve to t_unused (rank 2 below t3=rank 4... + # wait actually rank-1 = rank(t3)-1 = 3-1 = 2, which is t3 + # itself wait no t3 = rank 2). Let me reason: cat_levels + # ranks t1=0, t2=1, t3=2, t_unused=3, t4=4, t5=5. For + # base=t3 (rank 2), base-1 by rank = rank 1 = t2 (correct). + # Better demonstration: pass base=t4 (post-period) — but that + # would be invalid by other guards. Use a setup where the + # unused level lies BEFORE base in chronology: place + # t_unused between t2 and t3, then base=t3. + cat_levels2 = ["t1", "t2", "t_unused", "t3", "t4", "t5"] + df2 = df_int.copy() + df2["time"] = pd.Categorical( + df2["time"].map(time_map), + categories=cat_levels2, + ordered=True, + ) + # Now base=t3 (rank 3); base-1 by rank = t_unused (rank 2, + # not in data). Old code would KeyError on the slope pivot; + # new observed-only lookup resolves to t2 (the previous + # observed period). Verify the call SUCCEEDS. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + r = joint_pretrends_test( + df2, + "y", + "d", + "time", + "unit", + pre_periods=["t1"], + base_period="t3", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + assert np.isfinite(r.cvm_stat_joint) + assert np.isfinite(r.p_value) + + def test_pretrends_trends_lin_unused_categorical_invariant(self): + """Same observed panel with vs without unused intermediate + categorical levels must produce IDENTICAL joint statistics on + the pretrends path. Reviewer-requested invariant for PR #392 + R4 P0 — detrending delta must use observed-period rank, not + full-categorical rank.""" + df_int = self._panel(rng_seed=50) + time_map = {1: "t1", 2: "t2", 3: "t3", 4: "t4", 5: "t5"} + df_a = df_int.copy() + df_a["time"] = pd.Categorical( + df_a["time"].map(time_map), + categories=["t1", "t2", "t3", "t4", "t5"], + ordered=True, + ) + df_b = df_int.copy() + df_b["time"] = pd.Categorical( + df_b["time"].map(time_map), + categories=["t1", "t_unused1", "t2", "t3", "t_unused2", "t4", "t5"], + ordered=True, + ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + r_a = joint_pretrends_test( + df_a, + "y", + "d", + "time", + "unit", + pre_periods=["t1"], + base_period="t3", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + r_b = joint_pretrends_test( + df_b, + "y", + "d", + "time", + "unit", + pre_periods=["t1"], + base_period="t3", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + assert r_a.cvm_stat_joint == r_b.cvm_stat_joint, ( + f"unused-categorical invariance broken on pretrends: " + f"a={r_a.cvm_stat_joint}, b={r_b.cvm_stat_joint}" + ) + assert r_a.p_value == r_b.p_value + + def test_homogeneity_trends_lin_unused_categorical_invariant(self): + """Twin invariant for joint_homogeneity_test.""" + df_int = self._panel(rng_seed=51) + time_map = {1: "t1", 2: "t2", 3: "t3", 4: "t4", 5: "t5"} + df_a = df_int.copy() + df_a["time"] = pd.Categorical( + df_a["time"].map(time_map), + categories=["t1", "t2", "t3", "t4", "t5"], + ordered=True, + ) + df_b = df_int.copy() + df_b["time"] = pd.Categorical( + df_b["time"].map(time_map), + # Insert unused level between base (t3) and post (t4) — would + # change the post-period delta under the buggy full-cat rank. + categories=["t1", "t2", "t3", "t_unused", "t4", "t5"], + ordered=True, + ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + r_a = joint_homogeneity_test( + df_a, + "y", + "d", + "time", + "unit", + post_periods=["t4", "t5"], + base_period="t3", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + r_b = joint_homogeneity_test( + df_b, + "y", + "d", + "time", + "unit", + post_periods=["t4", "t5"], + base_period="t3", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + assert r_a.cvm_stat_joint == r_b.cvm_stat_joint, ( + f"unused-categorical invariance broken on homogeneity: " + f"a={r_a.cvm_stat_joint}, b={r_b.cvm_stat_joint}" + ) + assert r_a.p_value == r_b.p_value + + def test_workflow_trends_lin_with_overall_aggregate_raises(self): + """trends_lin=True only valid on event_study aggregate.""" + df = self._panel(rng_seed=34) + df_2p = df[df["time"].isin([3, 4])].copy() + with pytest.raises(NotImplementedError, match="trends_lin=True.*event_study"): + did_had_pretest_workflow( + df_2p, + "y", + "d", + "time", + "unit", + aggregate="overall", + n_bootstrap=99, + seed=42, + trends_lin=True, + ) + + def test_pretrends_consumed_e_minus_2_dropped_in_HAD_fit(self): + """Cross-surface: HAD.fit(trends_lin=True) drops e=-2 from event_times. + + Locks the consumed-placebo invariant (R reduces max placebo lag + by 1; in our event_time convention that maps to dropping e=-2).""" + from diff_diff import HeterogeneousAdoptionDiD + + df = self._panel(rng_seed=18) + est_no = HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + ) + est_yes = HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=True, + ) + assert -2 in est_no.event_times.tolist() + assert -2 not in est_yes.event_times.tolist() + + +class TestHADFitTrendsLin: + """Direct unit tests for HAD.fit(trends_lin=True) (event-study path). + + Locks F>=3 guard, aggregate='overall' rejection, survey_design × + trends_lin NotImplementedError, get/set_params idempotence.""" + + @staticmethod + def _panel(F: int = 4, T: int = 5, G: int = 200, rng_seed: int = 21) -> pd.DataFrame: + rng = np.random.default_rng(rng_seed) + d = rng.beta(0.5, 1.0, size=G) + unit_fe = rng.normal(0, 1, G) + trend = rng.normal(0.1, 0.05, G) + rows = [] + for g in range(G): + for t in range(1, T + 1): + treated = t >= F + y = ( + unit_fe[g] + + trend[g] * (t - 1) + + (d[g] + d[g] ** 2) * treated + + rng.normal(0, 0.5) + ) + dose = d[g] if treated else 0.0 + rows.append({"unit": g, "time": t, "y": y, "d": dose}) + return pd.DataFrame(rows) + + def test_fit_default_bit_exact_backcompat(self): + from diff_diff import HeterogeneousAdoptionDiD + + df = self._panel(rng_seed=22) + r1 = HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + ) + r2 = HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=False, + ) + np.testing.assert_array_equal(r1.event_times, r2.event_times) + np.testing.assert_array_equal(r1.att, r2.att) + + def test_fit_aggregate_overall_with_trends_lin_raises(self): + from diff_diff import HeterogeneousAdoptionDiD + + df = self._panel(F=2, T=2, rng_seed=23) + with pytest.raises(NotImplementedError, match="aggregate='event_study'"): + HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="overall", + trends_lin=True, + ) + + def test_fit_F2_with_trends_lin_raises(self): + """F=2 (only 1 pre-period at t=1) → cannot identify slope.""" + from diff_diff import HeterogeneousAdoptionDiD + + # Build a 3-period panel with F=2 (treatment at t=2; only t=1 is pre). + df = self._panel(F=2, T=3, rng_seed=24) + with pytest.raises(ValueError, match="F >= 3"): + HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=True, + ) + + def test_fit_with_survey_design_and_trends_lin_raises(self): + from diff_diff import HeterogeneousAdoptionDiD, SurveyDesign + + df = self._panel(rng_seed=25) + df["w"] = 1.0 + with pytest.raises(NotImplementedError, match="trends_lin=True.*survey"): + HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=True, + survey_design=SurveyDesign(weights="w"), + ) + + def test_fit_with_weights_alias_and_trends_lin_raises(self): + from diff_diff import HeterogeneousAdoptionDiD + + df = self._panel(rng_seed=26) + with pytest.raises(NotImplementedError, match="trends_lin=True.*survey"): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + HeterogeneousAdoptionDiD().fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=True, + weights=np.ones(len(df)), + ) + + def test_fit_idempotence_with_trends_lin(self): + """Repeat-fit with the same estimator + trends_lin=True yields + identical numbers (per feedback_fit_does_not_mutate_config).""" + from diff_diff import HeterogeneousAdoptionDiD + + df = self._panel(rng_seed=27) + est = HeterogeneousAdoptionDiD() + r1 = est.fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=True, + ) + r2 = est.fit( + df, + outcome_col="y", + dose_col="d", + time_col="time", + unit_col="unit", + aggregate="event_study", + trends_lin=True, + ) + np.testing.assert_array_equal(r1.event_times, r2.event_times) + np.testing.assert_array_equal(r1.att, r2.att) + # get_params unchanged (no fit-time mutation). + assert est.get_params() == HeterogeneousAdoptionDiD().get_params()