forked from economic-research/open-ado
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtsperiods.ado
More file actions
423 lines (334 loc) · 10.9 KB
/
tsperiods.ado
File metadata and controls
423 lines (334 loc) · 10.9 KB
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
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
program define tsperiods , rclass
version 14
syntax , bys(varlist min=1) datevar(varlist min=1 max=1) ///
periods(string) ///
[event(varlist min=1 max=1) eventdate(varlist min=1 max=1) ///
ignore_panel maxperiods(string) mevents name(string) ///
overlap(string) symmetric]
// Sort data by ID and date
sort `bys' `datevar'
*** I Checks
// Check that eventnr and overlap variables do not exist in database
capture confirm variable eventnr
if !_rc { // If 'eventnr' exists and 'mevents' selected, throw exception
if "`mevents'" == "mevents" {
di "{err}Please drop variable 'eventnr'. tsperiods uses this variable to store the nr. of period."
exit
}
else {
di as error "'eventnr' already defined, but not created with this command. Caution is adviced."
}
}
// If user specified overlap, check that variable doesn't exist yet
if "`overlap'" != "" {
capture confirm variable overlap
if !_rc {
di "{err}Please drop variable 'overlap' or omit option 'overlap' from command."
exit
}
}
// Check that user provided a valid panel
if "`ignore_panel'" == "" {
tempvar nvals
by `bys' `datevar': gen `nvals' = _n
qui count if `nvals' > 1
local counts = r(N)
if `counts' > 0 {
di "{err}`bys' and `datevar' do not uniquely identify observations"
exit
}
}
else { // the check of multiple events fails if we are not working with a 'real' panel
if "`mevents'" == "" {
di "{err}'ignore_panel' requires 'mevents'"
exit
}
}
// Verify that periods is a positive integer
if `periods' <= 0{
di "{err}periods has to be a positive integer"
exit
}
if `periods' != int(`periods'){
di "{err}periods has to be an integer"
exit
}
// Confirm if user specified eventdate
tempvar anyevent
local datecount = 0
foreach var in `eventdate'{
local `datecount++'
// Used for checking whether all `bys' have at least one event
tempvar eventdatetemp
qui gen `eventdatetemp' = 0
qui replace `eventdatetemp' = 1 if !missing(`eventdate')
by `bys': egen `anyevent' = max(`eventdatetemp')
drop `eventdatetemp'
}
// Confirm if user specified an event
local eventcount = 0
foreach var in `event'{
local `eventcount++'
// Used for checking whether all `bys' have at least one event
by `bys': egen `anyevent' = max(`event')
}
// Check whether no event or eventdate were specified
if `datecount' == 0 & `eventcount' == 0{
di "{err}Specify either event or eventdate"
exit 102
}
// Check that user didn't specify event AND eventdate
if `datecount' > 0 & `eventcount' > 0{
di "{err}Can only specify one of two event/eventdate"
exit 103
}
// Check if event has either 0, 1 or missing (if event specified)
if `eventcount' > 0 {
qui count if `event' == 1 | `event' == 0 | missing(`event')
local counter1 = r(N)
qui count
local counter2 = r(N)
if `counter1' != `counter2'{
di "{err}Event dummy can only have 0, 1 or missing values"
exit 175
}
}
// Check that eventdate has no missing values if specified
if `datecount' > 0 {
qui count if `eventdate' == .
local counts = r(N)
if `counts' > 0{
di "{err}`eventdate' cannot have missing values"
exit
}
}
// Check that there's at most one event per ID if mevents wasn't specified
if "`mevents'" == ""{
tempvar maxdate mindate
if `eventcount' > 0{ // If user specified an event
tempvar datetemp
gen `datetemp' = `datevar' if `event' == 1
bys `bys': egen `mindate' = min(`datetemp')
bys `bys': egen `maxdate' = max(`datetemp')
qui count if `mindate' != `maxdate'
local counts = r(N)
if `counts' != 0 {
di "{err}More than one event specified by ID. This warning can be turned off with option mevents."
exit
}
drop `datetemp' `mindate' `maxdate' // STATA doesn't always drop temporary objects
}
else{ // If user specified a date
bys `bys': egen `mindate' = min(`eventdate')
bys `bys': egen `maxdate' = max(`eventdate')
qui count if `mindate' != `maxdate'
local counts = r(N)
if `counts' != 0 {
di "{err}More than one eventdate specified by ID. This warning can be turned off with option mevents."
exit
}
drop `maxdate' `mindate'
}
}
// If user specified overlap, check if user also specified mevents
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
}
}
*** II compute days to/from event
tempvar datediff
if `eventcount' > 0{ // If user specified event
preserve
tempfile count_events
qui keep if `event' == 1
keep `bys' `datevar'
if "`ignore_panel'" == "ignore_panel" {
duplicates drop `bys' `datevar', force
}
sort `bys' `datevar'
by `bys': gen nvals = _n // identifies order of events within panel ID
save `count_events'
restore
qui merge m:1 `bys' `datevar' using `count_events' , nogen
qui su nvals
local max = r(max)
local varlist
qui gen `datediff' = .
forvalues i = 1(1)`max'{
tempvar date`i' eventdate`i'
qui gen `date`i'' = `datevar' if `event' == 1 & nvals == `i'
bys `bys': egen `eventdate`i'' = min(`date`i'') // column with date of event nr. i by ID
qui gen datediff`i' = `datevar' - `eventdate`i'' // date difference WRT event date nr. i
qui gen datediff`i'_abs = abs(datediff`i')
local varlist "`varlist' datediff`i'_abs"
drop `date`i'' `eventdate`i''
}
tempvar datemin
egen `datemin' = rowmin(`varlist')
forvalues i = 1(1)`max'{
qui replace `datediff' = datediff`i' if datediff`i'_abs == `datemin'
drop datediff`i' datediff`i'_abs
}
drop `datemin' nvals
}
else { // If user provided an eventvar
qui gen `datediff' = `datevar' - `eventdate'
}
*** III Generate periods to/from variables
// Set name for new variable
if "`name'" == ""{
local name epoch
}
// If user didn't select maxperiods, compute optimal number
// works optimally if panel is balanced
if "`maxperiods'" == "" {
local maxperiods_selected "FALSE"
}
else {
local maxperiods_selected "TRUE"
}
if "`maxperiods_selected'" == "FALSE" {
local j = 1
local counts = 99
while `counts' > 0 {
qui count if (`datediff' >= -`j'*`periods' ///
& `datediff' <= -`periods'*(`j' - 1) - 1)
local total = r(N)
qui count if (`datediff' >= `j' * `periods' ///
& `datediff' <= `periods' * (`j'+1) - 1)
local counts = `total' + r(N)
local `j++'
}
local maxperiods = `j' + 1
di as error "Consider specifying maxperiods if you believe the panel is unbalanced"
}
if "`symmetric'" == "" { // t-0 covers [0,periods)
qui gen `name' = 0 if (`datediff' >= 0 & `datediff' <= `periods'-1)
forvalues i=1(1)`maxperiods'{
qui replace `name' = -`i' if (`datediff' >= -`i'*`periods' ///
& `datediff' <= -`periods'*(`i' - 1) - 1)
qui replace `name' = `i' if (`datediff' >= `i' * `periods' ///
& `datediff' <= `periods' * (`i'+1) - 1)
}
}
else{ // t-0 covers [-periods/2, periods/2]
if mod(`periods',2) != 0{
di "{err}Periods must be an even number if option symmetric selected"
exit 7
}
qui gen `name' = 0 if (`datediff' >= -`periods'/2 & `datediff' <= `periods'/2)
forvalues i=1(1)`maxiter'{
qui replace `name' = -`i' if (`datediff' >= -(`i'+1/2)*`periods'-`i' ///
& `datediff' <= -(`i'-1/2)*`periods'-`i')
qui replace `name' = `i' if (`datediff' >= (`i'-1/2)*`periods' + `i' ///
& `datediff' <= (`i'+1/2)*`periods'+`i')
}
}
// If mevents was selected compute overlapping windows and generate event-ID indicators
if "`mevents'" == "mevents" {
// If ignore_panel is selected, assume that ID x datevar do not uniquely
// identify observations. We solve this by forcing a 'real' panel
// where ID x datevar identifies observations. Then we compute overlapping
// windows and event-ID indicators for this panel and merge to original at end.
if "`ignore_panel'" == "ignore_panel" {
tempfile savepoint
save `savepoint'
if `datecount' > 0 {
local other_keep `eventdate'
}
keep `bys' `datevar' `name' `other_keep'
duplicates drop `bys' `datevar' `name', force
}
tempvar diff startevent
sort `bys' `datevar'
by `bys': gen `diff' = `name' - `name'[_n-1]
if "`periods'" != "1" {
qui gen `startevent' = (`diff' < 0)
}
else {
qui gen `startevent' = (`diff' <= 0)
}
by `bys': gen eventnr = sum(`startevent')
qui replace eventnr = eventnr + 1
if "`overlap'" != "" { // If user specified an overlap window generate dummy for overlap
// Create a local that contains the list of lags to consider
local list_lags ""
forvalues lagnum = 1(1)`overlap'{
local list_lags "`list_lags' lag`lagnum'"
}
if `datecount' > 0 { // if user specified an event date, create event dummy
tempvar event
gen `event' = (`eventdate' == `datevar')
}
// Create dummies for event variable
tempvar `list_lags'
forvalues lagnum = 1(1)`overlap'{
by `bys': gen lag`lagnum' = `event'[_n-`lagnum']
}
// Add dummies together to compute whether there was a nearby event
egen overlap = rowtotal(`list_lags')
qui replace overlap = (overlap > 0 & `name' <= 0)
drop `list_lags'
if `datecount' > 0 {
drop `event'
}
}
if "`ignore_panel'" == "ignore_panel" {
merge 1:m `bys' `datevar' using `savepoint', nogen
}
drop `diff' `startevent'
}
// Generate 'eventnr' variable if 'mevents' was NOT specified,
// for those ID's with no event
if "`mevents'" == "" {
qui gen eventnr = 1 if !missing(`name')
}
// Check that epoch has no missing values and provide guidance as to why that would be the case
qui count if missing(`name')
local missing_epoch = r(N)
qui count if missing(`name') & `anyevent' == 0
local missing_epoch_no_event = r(N)
if `missing_epoch' > 0 {
di as error "`missing_epoch' missing values in `name' detected."
if "`maxperiods_selected'" == "FALSE" {
if `missing_epoch' == `missing_epoch_no_event' {
di as error "This is caused by one or more `bys' that do not have any event"
}
else {
di "{err} Unknown error caused `name' to have missing values"
}
}
if "`maxperiods_selected'" == "TRUE" {
if `missing_epoch' == `missing_epoch_no_event' {
di as error "This is caused by one or more `bys' that do not have any event"
}
else {
di as error "Consider increasing maxperiods"
}
}
}
// descriptive stats
// For epoch
qui su `name'
local mean = r(mean)
local max = r(max)
local min = r(min)
di "Descriptive stats for `name', mean: `mean', min: `min', max: `max'"
// For event number
qui su eventnr
local mean = r(mean)
local max = r(max)
local min = r(min)
di "Descriptive stats for eventnr, mean: `mean', min: `min', max: `max'"
drop `datediff' `anyevent'
sort `bys' `datevar'
end