Estimate a shift-share IV regression with several confidence intervals
Source:R/estimate.R
ssb_estimate.RdComputes the shift-share 2SLS point estimate of `x` on `y` (instrumented by the constructed Bartik instrument, controls partialled out via FWL) and reports a panel of intervals side by side so the practical importance of the inference method is visible:
`iid` — classical (homoskedastic) IV,
`ehw` — Eicker-Huber-White (heteroskedasticity-robust),
`akm`, `akm0` — Adao-Kolesar-Morales exposure-robust inference, via ShiftShareSE when installed,
`cluster` — naive cluster-robust (needs `cluster` in the design),
`twoway` — two-way cluster-robust (needs `cluster` in the design and `cluster2` here).
The point estimate is identical across rows; only the standard errors and intervals differ, which is exactly what makes the comparison instructive.
Arguments
- design
An [ssb_design()] object.
- methods
Which methods to report. `NULL` (default) resolves by the design's route: `c("ehw", "cluster")` on the share route (dropping `cluster` if no cluster variable is set), `c("akm", "akm0")` on the shift route. Supply a character vector of `"iid"`, `"ehw"`, `"cluster"`, `"twoway"`, `"akm"`, `"akm0"` to override.
- level
Confidence level for the reported intervals.
- cluster2
Optional second clustering column in `data` for the `"twoway"` method (paired with the design's `cluster`).
- shock_cluster
Optional grouping of the shocks for the AKM / AKM0 variance: a column name in the shocks table, or a vector of length equal to the number of shock-cells. Use it when shocks are mutually correlated within groups — e.g. sub-industries within broader industries, or sector cells of the same sector across periods — so the exposure-robust variance is clustered at the group level (Adao, Kolesar & Morales 2019). It is passed to ShiftShareSE as `sector_cvar`, which clusters the shock-level scores; the package's test suite verifies this against a hand-computed cluster-robust shock-level regression, so the grouping is genuinely used rather than shocks being treated as independent.
Value
A `data.frame` of class `ssb_estimate` with one row per method (`estimate`, `std.error`, `conf.low`, `conf.high`), carrying the first-stage F and the identification route as attributes. Plot with [ssb_plot_ci()].
Details
The primary object of the comparison is the **confidence interval**, not the standard error: AKM0 in particular is defined directly as a (possibly asymmetric, possibly unbounded) interval, and the `std.error` reported for it is a symmetric pseudo-SE implied by that interval rather than a conventional standard error. Read the table and [ssb_plot_ci()] figure as a comparison of intervals. When the instrument is weak the AKM0 confidence *set* need not be an interval at all: it can be the whole real line or the complement of an interval (a union of two rays). ShiftShareSE encodes the latter as `conf.low > conf.high`; `ssb_estimate()` flags both cases in the `note` column and the table/plot methods render them accordingly.
**The default methods follow the identification route of the design** (Borusyak-Hull-Jaravel JEP practical guide):
`exogenous = "share"` (Goldsmith-Pinkham-Sorkin-Swift): identification comes from the shares, and conventional inference is appropriate — the default is `ehw` plus `cluster` when the design has a `cluster` variable.
`exogenous = "shift"` (Borusyak-Hull-Jaravel; Adao-Kolesar-Morales): with as-good-as-random shocks, conventional EHW / cluster standard errors over-reject because residuals are correlated across units with similar exposure — the default is the exposure-robust panel `akm` / `akm0`.
Supplying `methods` explicitly overrides the default, e.g. to place the conventional and exposure-robust intervals side by side for a methodological comparison; the printed table always states the route so the headline inference is unambiguous. `iid` (homoskedastic) and `twoway` are only reported on request (add `cluster2` for two-way clustering). The `cluster` row needs a `cluster` column in the design (set via [ssb_design()]); without one it is reported as `NA` with a note rather than an error.
Examples
sim <- ssb_simulate(n_loc = 80, n_sec = 10, seed = 1)
d <- ssb_design(sim$data, sim$shares, sim$shocks, exogenous = "share")
ssb_estimate(d) # share route: conventional (EHW) inference
#> <ssBartik estimate>
#> route : exogenous SHARE -> conventional (EHW / cluster) inference
#> first-stage F : 19.8
#> method estimate std.error conf.low conf.high note
#> EHW 1.45 0.199 1.06 1.84