signflip

A tiny, deterministic staggered-adoption panel — 12 units over 6 years, no noise — engineered so that naive stacked two-way fixed effects gets the sign of the treatment effect wrong while the corrective Q-weighted estimator recovers it exactly. A small early cohort has a large negative effect and a large late cohort has a positive effect; because unweighted pooling overweights the small early cohort by its greater precision, it lands on the wrong side of zero. It is the scenario the interactive app is built around, in miniature.

Codebook

Variable Type Description
id integer Unit identifier (1–12)
group character "early" (2 units, adopt year 4, effect −20), "late" (8 units, adopt year 5, effect +8), or "never" (2 units)
adopt_year integer Treatment adoption year; NA for never-treated
year integer Calendar year (1–6)
outcome numeric Deterministic outcome (baseline + effect; no noise, so worked numbers are exact)

The treated-unit-weighted target is (2·(−20) + 8·8) / 10 = +2.4. Unweighted pooled OLS on the stack gives −2.769 (wrong sign); the Q-weighted estimate is exactly +2.4.

Source and citation

Generated deterministically by the package (data-raw/signflip.R). No external data. See Wing, C., Freedman, S., & Hollingsworth, A. (2024). Stacked Difference-in-Differences. Working paper.

The data

library(stacked)
data(signflip, package = "stacked")
head(signflip)
   id group adopt_year year outcome
1:  1 early          4    1      10
2:  1 early          4    2      10
3:  1 early          4    3      10
4:  1 early          4    4     -10
5:  1 early          4    5     -10
6:  1 early          4    6     -10
dim(signflip)
[1] 72  5

Preview

source("../assets/style-web.R")

d <- copy(signflip)
agg <- d[, .(outcome = mean(outcome)), by = .(year, group)]
agg[, group := factor(group, levels = c("early", "late", "never"))]

p_preview <- ggplot(agg, aes(year, outcome, color = group, fill = group, shape = group)) +
  geom_line(linewidth = 0.7) +
  geom_point(size = pt_size, stroke = pt_stroke, color = border_color) +
  scale_color_manual(values = c(early = highlight_color, late = grey_line,  never = grey_line)) +
  scale_fill_manual(values  = c(early = highlight_color, late = grey_fill,  never = "white")) +
  scale_shape_manual(values = c(early = shp_primary,     late = shp_grey,   never = shp_grey)) +
  labs(title = "Outcome paths by cohort",
       subtitle = "Deterministic outcome; early cohort (highlighted) drops sharply at adoption",
       x = "Year") +
  base_theme

Download

Load it

data(signflip, package = "stacked")
stacked use signflip, clear