*! version 1.2.2 January 2020

Title

ssaggregate -- Create industry-level aggregates for shift-share IV

Syntax

Using "long" exposure weights, saved separately (executes faster):

ssaggregate
varlist
[if]
[in]
[weight], n(varlist) s(varname) l(varlist)
sfilename(filename)
[other options]

Using "wide" exposure weights, saved in memory:

ssaggregate
varlist
[if]
[in]
[weight], n(varname) s(stubname)
[other options]

General
t(varlist) period identifiers

controls(strings) sets of control variables to be partialled out

absorb(strings) sets of catagorical variables that identify fixed effects to be absorbed

addmissing create "missing industry" observations

With "long" exposure weights
n(varlist) industry identifiers (required)

s(varname) name of exposure weight variable (required)

l(varlist) location identifiers (required)

sfilename(filename) exposure weight dataset (required)

With "wide" exposure weights
n(varname) industry identifier (required)

s(stubname) stub of the names of exposure weight variables (required)

string indicates that the industry identifier is a string

aweights, fweights, iweights, and pweights are allowed; see weight.

Description

ssaggregate converts "location-level" variables in a shift-share IV dataset to a dataset of exposure-weighted "industry-level" aggregates, as described in Borusyak, Hull, and Jaravel (2020).

There are two ways to specify ssaggregate, depending on whether the industry exposure weights are saved in "long" format (in a separate dataset) or in "wide" format (in the dataset in memory).
In general ssaggregate will execute faster with "long" exposure weights. See the
examples for proper syntax in both cases.

In the "long" case the dataset in memory must be uniquely identified by the cross-sectional variables in l() and, when applicable, the period identifiers in t(). The
separate shares dataset is given by sfilename() and should be uniquely indexed by the variables in l() and n() (and t(), when specified).
s() should contain the name of the exposure weight variable, and the two datasets should contain only matching values of l() and t().

In the "wide" case the s() option should contain the common stub of the names of exposure weight variables, and n() should contain the target name of the shock identifier.
For example, s(share) should be specified when the exposure variables are named share101, share102, etc., with 101 and 102 being values of the shock identifier in n().
The string option should be specified when the shock identifer is a string variable. Missing values in any of the exposure weight variables are interpreted as zeros.
The dataset in memory may be a panel or repeated cross section, with periods indexed by t().

In both cases there should be no missing values for the location-level variables, conditional
on any if and in sample restrictions. The resulting industry-level dataset will contain exposure-weighted
de-meaned averages of the location-level variables, along with the average exposure weight s_n. This dataset will be be indexed by
the variables in n() (and t(), when specified).

When the controls()
option is included the location-level variables are first residualized by each of the specified sets of controls. Each set should be included in the option as a separate string (in quotes);
for example as controls("var1" "var1 var2" "var1 var3 var4"), where var1-var4 are names of control variables in memory. If one set of controls is empty, it can be included (as "") anywhere but in the final position
due to a technical issue. The transformed variables are then indexed by the control set number: for example
y1-y3 when y is the variable in memory to be transformed. An exception is when either controls() or absorb() or both are included with only a single set of controls or fixed effects (still in quotes),
or when both commands are omitted. Then the transformation of variable y is also named y.

When the absorb()
option is included the location-level variables are first residualized by each of the specified sets of fixed effects. Each set should be included as a separate string (in quotes), as in the controls() option.
If one set of controls is empty, it can be included (as "") anywhere but in the final position due to a technical issue.
When both options are specified the sets of controls and fixed effects in the same position are included together in the first-step residualization. For example specifying both controls("var1" "var1 var2")
and absorb("fe1" "fe1" "fe1 fe2") will produce transformed variables y1-y3 when y is the variable in memory to be transformed, where y1 is residualized on var1 and fe1, y2 is
residualized on var1, var2, and fe1, and y3 is residualized on fe1 and fe2.

Including the addmissing option generates a "missing industry" observation, with exposure weights equal to one minus
the sum of a location's exposure weights. Borusyak, Hull, and Jaravel (2020) recommend including
this option when the the sum of exposure weights varies across locations (see Section 3.2). The missing industry observations will
be identified by missing identifiers in n().

Note that no information on industry shocks is used in the execution of ssaggregate; once run, users can merge shocks and any industry-level controls to the aggregated dataset.
They can then estimate and validate quasi-experimental shift-share IV regressions with
other Stata procedures. See Section 4 of Borusyak, Hull, and Jaravel (2020) for details and below for examples of such procedures.

Options

General

t(varlist) specifies the unique period indicators given a panel or repeated cross section of locations. The same variables must index the same time periods in the separate "long" exposure share dataset, when used.

controls(varlist) gives sets of control variable to be separately partialled out prior to aggregation. Each set should be included as a separate string in quotes, even when only one set is included.
Variable wildcards, factor variables, and time-series operators are allowed in the strings, as if they were sets of regression controls.

absorb(varlist) gives sets of catagorical variables identifying fixed effects to be separately absorbed prior to aggregation. Each set should be included as a separate string in quotes, even when only one set is included.

addmissing creates observations corresponding to the "missing industry" for when the exposure shares do not sum to one.
The identifiers in n() will be missing for these observations (when t() is specified, an observation is created
for each time period).

With "long" exposure shares

n(varlist) specifies the industry identifiers in the external dataset. n() is a required option.

s(varname) specifies the exposure weight variable in the external dataset. s() is a required option.

l(varlist) specifies the location identifiers in both the dataset in memory and the external dataset. l() is a required option.

sfilename(filename) specifies the external dataset. sfilename() is a required option.

With "wide" exposure shares

n(varname) specifies the industry identifier. n() is a required option.

s(stubname) specifies stubs of the names of exposure weight variables in memory. s() is a required option.

string indicates that the shock identifier as a string (default is numeric).

Examples

The following examples of ssaggregate can be run from the Borusyak, Hull, and Jaravel (2020) data archive,
after loading into memory the main Autor, Dorn, and Hanson (2013) replication dataset (with use location_level, clear).

Using separate "long" share dataset

. ssaggregate y x z l_sh_routine33 [aw=wei], n(sic87dd) t(year) s(ind_share) sfile(Lshares) l(czone) controls("t2 Lsh_manuf")

Using "wide" shares in memory

. merge 1:1 czone year using Lshares_wide, assert(3) nogen

. ssaggregate y x z l_sh_routine33 [aw=wei], n(sic87dd) t(year) s(ind_share) controls("t2 Lsh_manuf")

Partialling out multiple control sets

. ssaggregate y x z l_sh_routine33 [aw=wei], n(sic87dd) t(year) s(ind_share) sfile(Lshares) l(czone) controls("t2 Lsh_manuf" "t2 c.Lsh_manuf#t2")

Including the "missing industry"

. ssaggregate y x z l_sh_routine33 [aw=wei], n(sic87dd) t(year) s(ind_share) sfile(Lshares) l(czone) controls("t2 Lsh_manuf") addmissing

After aggregation, shocks and any shock-level controls can be merged on to the new dataset. For example, after the previous command a user could run

. replace sic87dd = 0 if missing(sic87dd)

. merge 1:1 sic87dd year using shocks, assert(1 3) nogen

. merge m:1 sic87dd using industries, assert(1 3) nogen

. foreach v of varlist g year sic3 {

replace `v'= 0 if sic87dd == 0

}

Note that the first line and last loop are unnecessary without the addmissing option.

The user can then use the shock-level dataset to estimate different shift-share analyses:

Basic shift-share IV

. ivreg2 y (x=g) year [aw=s_n], r

Conditional shift-share IV with clustered standard errors

. ivreg2 y (x=g) year if g < 45 [aw=s_n], cluster(sic3)

Shift-share reduced form regression (y on z)

. ivreg2 y (z=g) year [aw=s_n], r

Shock-level balance check

. reg l_sh_routine33 g year [aw=s_n], r

See Borusyak, Hull, and Jaravel (2020) for other examples of shock-level
analyses and guidance on specifying and validating a quasi-experimental shift-share IV.

Authors

Kirill Borusyak, UCL: k.borusyak@ucl.ac.uk
Peter Hull, University of Chicago: hull@uchicago.edu
Xavier Jaravel, London School of Economics: x.jaravel@lse.ac.uk