-
Notifications
You must be signed in to change notification settings - Fork 5
/
evstudy.ado
400 lines (324 loc) · 11.3 KB
/
evstudy.ado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
program evstudy , rclass
version 14
syntax varlist [if], basevar(string) periods(string) ///
varstem(varlist min=1 max=1) [absorb(varlist) ///
bys(varlist min=1) cl(varlist min=1) connected datevar(varlist min=1 max=1) debug ///
file(string) force generate kernel kopts(string) leftperiods(string) ///
maxperiods(string) mevents nolabel omit_graph ///
othervar(varlist min=2 max=2) overlap(string) qui ///
regopts(string) tline(string) surround twopts(string)]
*----------------------- Checks ---------------------------------------------
// Verify that tsperiods is installed
capture findfile tsperiods.ado
if "`r(fn)'" == "" {
di as error "user-written package 'tsperiods' needs to be installed first;"
exit 498
}
// Verify that regsave is installed
capture findfile regsave.ado
if "`r(fn)'" == "" {
di as error "user-written package 'regsave' needs to be installed first;"
exit 498
}
// Verify that graph2 is installed
capture findfile graph2.ado
if "`r(fn)'" == "" {
di as error "user-written package 'graph2' needs to be installed first;"
exit 498
}
// Verify that timedummy exists
capture findfile timedummy.ado
if "`r(fn)'" == "" {
di as error "user-written package 'timedummy' needs to be installed first;"
exit 498
}
// Verify that no variable called 'myevent' exists
if "`generate'" == "generate" {
capture confirm variable myevent
if !_rc {
di "{err}Please drop variable 'myevent'. Evstudy uses this object to temporary store information"
exit
}
}
// Check if absorb is empty
local abscount = 0
foreach var in `absorb'{
local `abscount++'
}
// Check if bys is empty
local byscount = 0
foreach var in `bys'{
local `byscount++'
}
// Check if cl is empty
local clcount = 0
foreach var in `cl'{
local `clcount++'
}
// Check that connected and kernel not used together
if "`connected'" == "connected" & "`kernel'" == "kernel" {
di "{err} 'connected' and 'kernel' cannot both be specified"
exit
}
// Check if datevar is empty
local datecount = 0
foreach var in `datevar'{
local `datecount++'
}
// Check if othervar is empty
local othervarcount = 0
foreach var in `othervar'{
local `othervarcount++'
}
// Verify that if option generate was selected, then bys and datevar specified
if "`generate'" == "generate"{
if `byscount' == 0 | `datecount' == 0{
di "{err}Need to specify bys and datevar if option generate is specified"
exit
}
}
// If user specified overlap, check if user also specified mevents
// Verify that overlap is a positive integer
if "`overlap'" != "" & "`mevents'" == "" {
di "{err}Need to specify 'mevents' if 'overlap' is specified"
exit
// Verify that periods is a positive integer
if `overlap' <= 0{
di "{err}overlap has to be a positive integer"
exit
}
if `overlap' != int(`periods'){
di "{err}overlap has to be an integer"
exit
}
}
// If user specified omit_graph, check that filename is not specified
if "`omit_graph'" == "omit_graph" & "`file'" != "" {
di "{err}Cannot specify 'file' with option 'omit_graph'"
exit
}
// Warn user that in kernel graphs only time coefficients are plotted
if "`kernel'" == "kernel" {
if "`surround'" == "surround" | `othervarcount' > 0 {
di as error "`surround' `othervar' will be included in estimation but the coefficients will NOT be ploted"
}
}
// Verify that periods is a positive integer
if "`maxperiods'" != "" {
if `maxperiods' <= 0{
di "{err}overlap has to be a positive integer"
exit
}
}
// Warn user if panel is unbalanced
sort `bys' `datevar'
tempvar diffdate
by `bys': gen `diffdate' = `datevar' - `datevar'[_n-1]
qui su `diffdate'
local max = r(max)
if `max' > 1 {
di as error "Warning: panel might be unbalanced"
}
drop `diffdate'
*----------------------- Checks ---------------------------------------------
*----------------------- Definitions ----------------------------------------
// Create local for absorb
if `abscount' >0{
local abslocal "absorb(`absorb')"
}
else {
local abslocal "noabsorb"
}
// Define cluster variable
if `clcount' > 0 {
local cluster "cl(`cl')"
}
// Optionally connect graph
if "`connected'" == "connected" {
local connected "recast(connected)"
}
// Define capture (for use with force option)
if "`force'" == "force" & "`generate'" == ""{
di "{err}Option force can only be specified with option generate"
exit
}
if "`nolabel'" == "" {
// Obtain name of dependant variable
local depvar `: word 1 of `varlist''
// Store label
local label_loc : var label `depvar'
local ylabel_loc "ytitle(`label_loc')"
}
// Define tline if one is specified
if "`tline'" != "" {
local tlineval "tline(`tline', lp(solid) lc(red))"
}
// Define periods to the left, if not specified
if "`leftperiods'" == "" {
local leftperiods = `periods'
}
*----------------------- Definitions ----------------------------------------
*----------------------- Generate variables----------------------------------
// Optionally build leads and lags
if "`generate'" == "generate"{ // BEGIN GENERATE LEADS AND LAGS
if "`overlap'" != "" { // If overlap was specified
capture confirm variable overlap // check if overlap was already defined
if "`force'" == "" { // If option force was not specified and variable overlap exists throw error
if !_rc {
di "{err}Variable 'overlap' is already defined."
di "{err}Option 1: drop variable 'overlap'."
di "{err}Option 2: omit 'generate' option."
di "{err}Option 3: specify 'force option'."
exit
}
}
if _rc { // If variable overlap doesn't exist, create it
local overlaploc "overlap(`overlap')"
}
}
// Determine if it's necessary to generate periods dummies or not
// Check whether user specified 'generate', but not 'force', and still the variable exists
local period_dummies_required "FALSE"
forvalues i = 1(1)`leftperiods' {
capture confirm variable `varstem'_f`i'
if _rc { // If it doesn't exist flag to generate
local period_dummies_required "TRUE"
}
else if "`force'" == ""{
di "{err}Variable `varstem'_f`i' already specified"
exit
}
}
forvalues i = 1(1)`periods' {
capture confirm variable `varstem'_l`i'
if _rc { // If it doesn't exist flag to generate
local period_dummies_required "TRUE"
}
else if "`force'" == ""{
di "{err}Variable `varstem'_l`i' already specified"
exit
}
}
if "`surround'" == "surround" {
foreach type in pre post {
capture confirm variable `varstem'_`type'
if _rc {
local period_dummies_required "TRUE"
}
else if "`force'" == ""{
di "{err}Variable `varstem'_`type' already specified"
exit
}
}
}
if "`period_dummies_required'" == "TRUE" {
if "`maxperiods'" == "" { // Use heuristic to determine nr of leads and lags if user did not specify
// Compute the absolute maximum number of leads and lags that we could need
tempvar counts maxcounts
bys `bys': gen `counts' = _n
by `bys' : egen `maxcounts' = max(`counts')
qui su `maxcounts'
local maxperiods = r(max)
drop `counts' `maxcounts'
}
// Construct periods to/from event ---------------------------------------------------------------
tsperiods , bys(`bys') datevar(`datevar') maxperiods(`maxperiods') ///
periods(1) event(`varstem') `mevents' name(myevent) `overlaploc'
// Construct periods to/from event ---------------------------------------------------------------
// Prevent STATA from storing myevent variable
tempvar myevent
qui gen `myevent' = myevent
qui drop myevent
timedummy, varstem(`varstem') periods(`periods') leftperiods(`leftperiods') ///
`surround' epoch(`myevent')
qui drop `myevent'
}
} // END GENERATE LEADS AND LAGS
// Include control for overlap if user specified it
if "`overlap'" != "" {
local overlapctrl "overlap"
}
// Build regression parameters in loop
local conditions ""
local regressors ""
// Leads of the RHS correspond to "pre-trend" = before treatment
if `othervarcount' > 0 {
tokenize `othervar'
if "`kernel'" == "" { // Only include in graph if kernel was not selected
local conditions "`conditions' (`1': _b[`1'] - _b[`basevar'])"
}
local regressors "`regressors' `1'"
}
// Include pre and post controls if selected
if "`surround'" == "surround" {
if "`kernel'" == "" { // Only include in graph if kernel was not selected
local conditions "`conditions' (`varstem'_pre: _b[`varstem'_pre] - _b[`basevar'])"
}
local regressors "`regressors' `varstem'_pre"
}
forvalues i = `leftperiods'(-1)1{
local conditions "`conditions' (`varstem'_f`i':_b[`varstem'_f`i']-_b[`basevar'])"
local regressors "`regressors' `varstem'_f`i'"
}
local conditions "`conditions' (`varstem':_b[`varstem']-_b[`basevar'])"
local regressors "`regressors' `varstem'"
// Lags correspond to "post-trends" = after treatment
forvalues i = 1(1)`periods'{
local conditions "`conditions' (`varstem'_l`i':_b[`varstem'_l`i']-_b[`basevar'])"
local regressors "`regressors' `varstem'_l`i'"
}
// Include pre and post controls if selected
if "`surround'" == "surround" {
if "`kernel'" == "" { // Only include in graph if kernel was not selected
local conditions "`conditions' (`varstem'_post: _b[`varstem'_post] - _b[`basevar'])"
}
local regressors "`regressors' `varstem'_post"
}
if `othervarcount' > 0 {
if "`kernel'" == "" { // Only include in graph if kernel was not selected
local conditions "`conditions' (`2':_b[`2']-_b[`basevar'])"
}
local regressors "`regressors' `2'"
}
// ------------------------ Regression -----------------------------------------
`qui' reghdfe `varlist' `regressors' `overlapctrl' `if', `abslocal' `cluster' `regopts'
// Check if any variables were omitted
local numcoef = `periods' + `leftperiods'
forvalues i = 1(1)`numcoef'{
if !missing(r(label`i')) {
if r(label`i') == "(omitted)"{
di "{err}One or more coefficients were omitted"
exit
}
}
}
// Normalize coefficients
`qui' nlcom `conditions', post
est store NL_EVresults
if "`omit_graph'" == "" {
if "`kernel'" == "kernel"{
preserve
regsave
tempvar days post
qui gen `days' = _n
qui replace `days' = `days' - `periods' - 1
qui gen `post' = (`days' >= 0)
graph twoway (scatter coef `days' if !`post', msize(small) graphregion(color(white)) graphregion(lwidth(vthick))) ///
(lpolyci coef `days' if !`post', lcolor(navy) ciplot(rline) `kopts') ///
(scatter coef `days' if `post', msize(small) color(cranberry*0.5)) ///
(lpolyci coef `days' if `post', `tlineval' lcolor(cranberry) ciplot(rline) `kopts') , ///
legend(off) `twopts' `ylabel_loc'
restore
}
else {
coefplot (NL_EVresults, keep(`varstem'_pre) mcolor(dknavy*0.4) ciopts(color(dknavy*0.4))) ///
(NL_EVresults, drop(`varstem'_pre `varstem'_post) mcolor(dknavy) ciopts(color(dknavy))) ///
(NL_EVresults, keep(`varstem'_post) mcolor(dknavy*0.4) ciopts(color(dknavy*0.4))), ///
ci(90) legend(off) offset(0) scale(1.1) yline(0, lp(solid) lc(black*0.4%80)) `tlineval' xsize(8) `connected' ///
vertical xlabel(, angle(vertical)) graphregion(color(white)) `twopts' `ylabel_loc'
}
if "`file'" != "" {
graph2 , file("`file'") `debug'
}
}
end