Research validation: estimands, heterogeneity and empirical credibility
Source:vignettes/research-validation.Rmd
research-validation.RmdA matching target is part of a valid coverage study
E[log(1 + Y)], log(E[Y]), and
log(1 + E[Y]) are different quantities. Comparing an
interval for the first with a target for the second can look like poor
uncertainty estimation when the principal problem is estimand mismatch.
Widening the interval does not repair that mismatch.
The fixed-effects elasticity estimator now defaults to Poisson pseudo-likelihood for the conditional count mean, with clustered covariance. It does not require the conditional variance to equal the conditional mean. Its consistency still requires a correctly specified conditional mean and suitable exogeneity. Clustered inference needs sufficiently many independent clusters; it does not resolve unmodelled dependence or confounding.
sim <- lamp_simulate(n_areas = 24, n_months = 24, design = "continuous",
effect = -0.25, base_rate = 30, seed = 83)
fit <- lamp_elasticity(sim, "affected_crime", lags = 0)
lamp_effect_summary(fit)
#> # A tibble: 1 × 6
#> estimate std_error conf_low conf_high level definition
#> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
#> 1 -0.244 0.0245 -0.292 -0.196 0.95 1 * .log_s_lag0
fit$diagnostics$estimand
#> [1] "log conditional count mean per log(1 + stops)"The covariate is log(1 + stops), so its coefficient is a
slope with respect to that covariate. At stop intensity S, the
conventional elasticity is slope * S / (1 + S). The sum
over lags is the response to a persistent change in log-one-plus
intensity. When the outcome combines affected and unaffected crime
types, its mean response need not have one constant slope.
The former FE response remains available with
family = "ols_log". CCE methods continue to use it; they
should be described as effects on log-one-plus observed outcomes, rather
than interchangeable count-mean elasticities. Their auxiliary CD
statistic describes shared movements in an OLS model and is not a causal
validity test or a formal test of the primary Poisson model’s
residuals.
Effects can vary by cohort and exposure duration
In the additive count design, the untreated area and time components are additive. This permits count-level parallel trends, unlike assuming that parallel trends on a log scale automatically imply it for counts.
sim <- lamp_simulate(n_areas = 24, n_months = 24, design = "staggered",
adoption = c(8, 12, 16), cohort_effects = c("8" = -10, "12" = -4, "16" = 5),
dynamic_effect = c(0, 0.5, 1), effect_scale = "additive", base_rate = 50, seed = 41)
truth <- lamp_simulation_truth(sim, "affected_crime", "identity")
head(truth[which(truth$rel_time >= 0), c("cohort", "rel_time", "effect")])
#> # A tibble: 6 × 3
#> cohort rel_time effect
#> <int> <int> <dbl>
#> 1 8 0 0
#> 2 8 1 -5.00
#> 3 8 2 -10
#> 4 8 3 -10
#> 5 8 4 -10
#> 6 8 5 -10effect and cohort_effects are combined
affected-outcome count differences for the additive design, and log mean
ratios for the default multiplicative design. The dynamic vector
multiplies each cohort’s effect; its last entry persists. The exact
counterfactual means remain available when observations are missing.
effect_crime_total is unavailable when there is no single
constant total-outcome effect.
For a count-level staggered analysis, select
family = "identity". Use each backend’s overall ATT when
aggregating; the simple ATT and an equal-weight mean of dynamic
coefficients generally have different weights.
adoption <- attr(sim, "truth")$adoption
tr <- lamp_treatment(sim, "staggered",
adoption = adoption[!is.na(adoption$adoption_month), ])
fit <- lamp_did_staggered(sim, "affected_crime", tr, family = "identity")
lamp_effect_summary(fit)Negative-binomial dispersion,
missing_mechanism = "high_outcome", and non-zero
trend_violation are stress scenarios. A good estimator need
not recover a causal effect when its identifying assumption is
deliberately violated. Such failures belong in the validation
results.
Summaries and sensitivity retain uncertainty
An average of event coefficients has variance w' V w,
including the off-diagonal covariances. Averaging confidence limits or
assuming independent coefficients is not a valid replacement.
lamp_effect_summary() uses the joint covariance, or the
backend’s overall ATT. It errors when the requested joint covariance is
unavailable.
lamp_trend_sensitivity() subtracts a user-specified
differential linear trend from an event-study average. Its intervals are
conditional on each trend assumption. It is a transparent bias
calculation, not a general robust confidence procedure or evidence that
the true trend lies inside the grid.
Empirical evidence needs a design, not just a coefficient
lamp_design_audit() documents missing outcomes,
treated-area pre/post support and detection-sensitive offences. It
leaves intervention assignment as a substantive question even when the
data checks pass.
lamp_elasticity_robustness() runs explicit
outcome/lag/family grids and optional leave-one-area-out checks. All
failures are retained. Specify the primary analysis before examining
results; a grid is not permission to select a significant result.
The London validation uses cached, hash-verified source files. It retains seven outcomes, three lag windows, two response scales, borough omission checks, future-exposure diagnostics and the reverse allocation model. These are audited associations. Bicycle theft is a comparison outcome, not a proven unaffected negative control, and drugs and weapons counts respond to police detection. Independently documented intervention deployment and assignment are needed for a causal case.
Reproduce the research evidence
The source scripts run outside package checks. Script
inst/scripts/05-research-validation.R uses fixed seeds,
records failed fits and Monte Carlo uncertainty, and needs no downloads.
Script 05b-elasticity-original-design.R also checks the
original study dimensions; 08-staggered-bias-audit.R
investigates finite-run bias with independent analytic contrasts. Script
06-london-robustness.R requires the London source cache and
refuses network access. Script 07-research-figures.R
creates shareable figures from these saved summaries. Historical results
remain separate in inst/validation; new results are in
inst/validation/research-upgrade.
The validation is a finite set of designs, not a guarantee of nominal coverage in every policing dataset. A publication should add substantively relevant interventions, larger Monte Carlo experiments, alternative dependence structures and independent reproduction.