lsaplot -- Plot event-study/dynamic treatment effects for difference-in-differences

Syntax

lsaplot depvar [indepvars]
[if] [in]
,
Treat(varname)
ID(varname)
Time(varname)
[start(#)
end(#)
base(#)
level(#)
cluster(varname)
absorb(fe_spec)
bin
trim
title(string)
keepdata
nograph
name(name) ]

Description

lsaplot estimates and plots dynamic treatment effects (coefficients for each relative time period) from an event-study or staggered difference-in-differences (DID) model. It automatically generates publication-ready graphs with point estimates and confidence intervals.

The command features a "smart engine" that switches between estimation methods:

1. For standard two-way fixed effects models, it uses the efficient xtreg.

2. When high-dimensional fixed effects (via absorb()) or non-nested clustering is detected, it automatically switches to reghdfe to avoid estimation errors.

Two methods are provided for handling observations outside the specified event-time window (start to end):

1. bin (binning/cohorting, recommended): Pools all pre-periods before start into the start period and all post-periods after end into the end period. This helps capture cumulative long-term effects.

2. trim (trimming): Drops all treated observations whose event time falls outside the specified window.

3. Default (not recommended): Neither bins nor trims. Variation from outside the window remains in the base group, which may lead to biased estimates.

Options

Treat(varname) specifies a numeric variable indicating the first period when a unit receives treatment (e.g., the year of policy adoption). For never-treated control units, this variable should be 0 or a missing value (.). This variable is used to generate relative-time dummies. Required.

ID(varname) specifies the panel unit identifier (e.g., firm ID, city code). Required.

Time(varname) specifies the time identifier (e.g., year). Required.

start(#) sets the first relative period to be included in the estimation and plot (e.g., -5). Defaults to the minimum computable relative period in the sample.

end(#) sets the last relative period to be included (e.g., 5). Defaults to the maximum computable relative period.

base(#) defines the base (omitted) relative period. The coefficient for this period is normalized to zero and excluded from the plot. Default is -1.

level(#) sets the confidence level for confidence intervals, e.g., 90, 95 (the default), or 99.

cluster(varname) specifies the level for cluster-robust standard errors (e.g., industry, province). Any level is allowed. The default is heteroskedasticity-robust standard errors (robust).

absorb(fe_spec) manually specifies high-dimensional fixed effects to absorb, using syntax like absorb(firm_id year industry#year). When this option is specified, the command forces the use of reghdfe and strictly follows this specification; it does not automatically include time fixed effects.

bin enables binning for observations outside the event window. All periods earlier than start are binned into the start period; all periods later than end are binned into the end period.

trim enables trimming for observations outside the event window. It drops all treated observations whose relative time is outside the [start, end] window.

title(string) adds a custom title to the generated graph.

keepdata retains the intermediate dataset (containing relative time, coefficients, confidence intervals, etc.) in memory after estimation, replacing the current data.

nograph runs the estimation but suppresses graph output. Useful for quickly checking regression results.

name(name) assigns a name to the generated graph for later use with commands like graph combine.

Examples

Basic example: Quick preview of dynamic effects

. webuse nlswork, clear

. // Generate gvar (first treatment year)

. bys idcode: egen gvar = min(year) if union == 1

. replace gvar = 0 if mi(gvar)

. lsaplot ln_wage, treat(gvar) id(idcode) time(year)

Standard DID plot: Set window, cluster, and use binning

. lsaplot ln_wage tenure hours, treat(gvar) id(idcode) time(year) start(-5) end(5) cl(idcode) bin title("Dynamic Effects on Wage")

High-dimensional fixed effects with trimming

. lsaplot ln_wage, treat(gvar) id(idcode) time(year) absorb(idcode occ_code#year) cl(occ_code) trim

Create and combine multiple graphs

. lsaplot ln_wage, treat(gvar) id(idcode) time(year) start(-3) bin name(fig1, replace)

. lsaplot hours, treat(gvar) id(idcode) time(year) start(-3) bin name(fig2, replace)

. graph combine fig1 fig2, row(1)

Stored results

lsaplot stores standard estimation results in e(), the contents of which depend on the underlying engine used (xtreg or reghdfe).

If the keepdata option is specified, the resulting dataset in memory will contain the following variables used for plotting:
reltime, beta, cil, ciu (representing relative time, coefficient estimate, lower and upper confidence interval bounds, respectively).

Installation and requirements

lsaplot is self-contained but relies on external packages for advanced features. It is recommended to install:

. ssc install reghdfe, replace

. ssc install ftools, replace

The latest version can also be installed from GitHub:

. net install lsaplot, from(https://raw.githubusercontent.com/hurilen/lsaplot/main/) replace

Author

Linze Li
School of Public Finance and Taxation, Shandong University of Finance and Economics
Email: lilinze626@gmail.com

References

Sun, L., and S. Abraham. 2021. Estimating Dynamic Treatment Effects in Event Studies with Heterogeneous Treatment Effects. Journal of Econometrics.

For methodological discussions on event-study and staggered DID designs, please refer to the recent econometrics literature.