├── CITATION.cff ├── LICENSE ├── Makefile ├── README.md ├── df.dta ├── eventbaseline.ado ├── eventbaseline.pkg ├── eventbaseline.sthlp ├── eventbaseline.zip ├── eventstudy_correct.png ├── eventstudy_kink.png ├── example.do ├── example.log ├── files.txt ├── packager.py ├── poetry.lock ├── pyproject.toml ├── smcl.lua ├── stata.toc ├── testdata.dta └── tsline.png /CITATION.cff: -------------------------------------------------------------------------------- 1 | 2 | cff-version: 1.2.0 3 | title: EVENTBASELINE - Correct Event Study After XTHDIDREGRESS 4 | message: >- 5 | If you use this software, please cite it using the 6 | metadata from this file. 7 | type: software 8 | authors: 9 | - given-names: 10 | family-names: 'Koren, Miklós (https://koren.mk)' 11 | identifiers: 12 | - type: url 13 | value: 'https://github.com/codedthinking/eventbaseline' 14 | version: 0.7.0 15 | date-released: '2024-01-23' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Coded Thinking OÜ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FILES := $(shell cat files.txt) 2 | TARGET := eventbaseline.zip 3 | 4 | .PHONY: tag 5 | 6 | $(TARGET): $(FILES) tag 7 | zip $(TARGET) $(FILES) 8 | tag: 9 | $(eval VERSION=$(shell grep 'version:' README.md | cut -d ' ' -f 2 | sed 's/^/v/')) 10 | git add $(FILES) 11 | git commit -m "Version $(VERSION)" 12 | git tag $(VERSION) 13 | eventbaseline.sthlp: README.md smcl.lua 14 | pandoc -f gfm -t smcl.lua $< > $@ 15 | smcl.lua: 16 | curl -sLo $@ "https://raw.githubusercontent.com/korenmiklos/pandoc-smcl/master/smcl.lua" 17 | %.pkg: %.ado README.md packager.py 18 | poetry run python packager.py README.md $* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Koren, Miklós (https://koren.mk) 3 | date: 2024-01-23 4 | version: 0.7.0 5 | title: EVENTBASELINE - Correct Event Study After XTHDIDREGRESS 6 | description: | 7 | `eventbaseline` transforms the coefficients estimated by `xthdidregress` into a correct event study relative to a baseline. The reported coefficients are the average treatment effects on the treated (ATT) for each period relative to the baseline. The baseline can be either a period before the treatment or the average of the pre-treatment periods. 8 | url: https://github.com/codedthinking/eventbaseline 9 | requires: Stata version 18 10 | --- 11 | # `eventbaseline` Correct event study after `xthdidregress` 12 | 13 | # Syntax 14 | 15 | - `eventbaseline`, [**pre**(#) **post**(#) **baseline**(*string*) **graph** **generate**(*name*)] 16 | 17 | `eventbaseline` transforms the coefficients estimated by `xthdidregress` into a correct event study relative to a baseline. The reported coefficients are the average treatment effects on the treated (ATT) for each period relative to the baseline. The baseline can be either a period before the treatment or the average of the pre-treatment periods. 18 | 19 | 20 | The package can be installed with 21 | ``` 22 | net install eventbaseline, from(https://raw.githubusercontent.com/codedthinking/eventbaseline/main/) 23 | ``` 24 | 25 | If you require a specific version, use 26 | ``` 27 | net install eventbaseline, from(https://github.com/korenmiklos/codedthinking/eventbaseline/raw/v0.7.0/) 28 | ``` 29 | 30 | # Options 31 | ## Options 32 | Option | Description 33 | -------|------------ 34 | **pre** | Number of periods before treatment to include in the estimation (default 1) 35 | **post** | Number of periods after treatment to include in the estimation (default 3) 36 | **baseline** | Either a negative number between `-pre` and `-1` or `average`, or `atet`. If `-k`, the baseline is the kth period before the treatment. If `average`, the baseline is the average of the pre-treatment periods. If `atet`, the regression table reports the average of the post-treatment periods minus the average of the pre-treatment periods. Default is `-1`. 37 | **graph** (optional) | Plot the event study graph with the default settings of `hetdid_coefplot`. 38 | **generate** (optional) | Name of the frame to store the coefficients and their confidence interval. 39 | 40 | # Background 41 | `xthdidregress` returns ATET between `t` and `t-1` whenever `t` is before the treatment. That is, pretrends are reported as first differences, whereas actual treatment effects are reported as difference relative to the period before treatment. This can lead to misleading event study plots (Roth 2024a). The `eventbaseline` command transforms the coefficients into a correct event study relative to a baseline. 42 | 43 | # Remarks 44 | The command can only be run after `xthdidregress`. 45 | 46 | The command also returns, as part of `e()`, the coefficients and standard errors. See `ereturn list` after running the command. Typical post-estimation commands can be used, such as `outreg2` or `estout`. 47 | 48 | The reported number of observations is also corrected to exclude the treated periods outside the reported event window. 49 | 50 | If the `generate` option is used, the returned frame contains the following variables: 51 | - `time`: the time period relative to the baseline 52 | - `coef`: the estimated coefficient 53 | - `lower`: the lower bound of the 95% confidence interval 54 | - `upper`: the upper bound of the 95% confidence interval 55 | 56 | The frame is `tsset` by `time`, so `tsline` can be used to plot the event study. 57 | 58 | # Examples 59 | See `example.do` and `example.log` for a full example. 60 | 61 | ``` 62 | . use "df.dta" 63 | . replace t = t + 100 64 | . xtset i t 65 | . xthdidregress ra (y) (d), group(i) 66 | note: variable _did_cohort, containing cohort indicators formed by treatment 67 | variable d and group variable i, was added to the dataset. 68 | 69 | 70 | 71 | . eventbaseline, pre(5) post(5) baseline(-1) graph 72 | 73 | Time variable: time, -5 to 5 74 | Delta: 1 unit 75 | 76 | Event study relative to -1 Number of obs = 1,850 77 | 78 | ------------------------------------------------------------------------------ 79 | y | ATET Std. err. z P>|z| [95% conf. interval] 80 | -------------+---------------------------------------------------------------- 81 | -5 | -2.31541 .2415591 -9.59 0.000 -2.788857 -1.841963 82 | -4 | -1.310102 .2551332 -5.13 0.000 -1.810153 -.8100498 83 | -3 | -1.256003 .284372 -4.42 0.000 -1.813362 -.6986446 84 | -2 | -.4307123 .2619239 -1.64 0.100 -.9440736 .082649 85 | -1 | 0 (omitted) 86 | 0 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 87 | 1 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 88 | 2 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 89 | 3 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 90 | 4 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 91 | 5 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 92 | ------------------------------------------------------------------------------ 93 | ``` 94 | 95 | ![](eventstudy_correct.png) 96 | 97 | 98 | ``` 99 | . xthdidregress ra (y) (d), group(i) 100 | note: variable _did_cohort, containing cohort indicators formed by treatment 101 | variable d and group variable i, was added to the dataset. 102 | 103 | 104 | 105 | . eventbaseline, pre(5) post(5) baseline(atet) 106 | 107 | Event study relative to atet Number of obs = 1,850 108 | 109 | ------------------------------------------------------------------------------ 110 | y | ATET Std. err. z P>|z| [95% conf. interval] 111 | -------------+---------------------------------------------------------------- 112 | ATET | 2.835658 .1134013 25.01 0.000 2.613396 3.05792 113 | ------------------------------------------------------------------------------ 114 | ``` 115 | 116 | 117 | 118 | # Authors 119 | - Miklós Koren (Central European University, https://koren.mk), *maintainer* 120 | 121 | # License and Citation 122 | You are free to use this package under the terms of its [license](LICENSE). If you use it, please the software package in your work: 123 | 124 | - Koren, Miklós. 2024. "EVENTBASELINE: Correct Event Study After XTHDIDREGRESS. [software]" Available at https://github.com/codedthinking/eventbaseline. 125 | 126 | # References 127 | - Roth, Jonathan. 2024a. "Interpreting Event-Studies from Recent Difference-in-Differences Methods." Available at https://www.jonathandroth.com/assets/files/HetEventStudies.pdf. Last accessed January 23, 2024. 128 | - Roth, Jonathan. 2024b. "Test Data for >Interpreting Event-Studies from Recent Difference-in-Differences Methods< [data set]." Available at https://github.com/jonathandroth/HetEventStudies/raw/master/output/df.dta Last accessed January 23, 2024. -------------------------------------------------------------------------------- /df.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedthinking/eventstudy/5d14efe611e2ca8248ab8ee3851df2b70dd5a96c/df.dta -------------------------------------------------------------------------------- /eventbaseline.ado: -------------------------------------------------------------------------------- 1 | *! version 0.7.0 23jan2024 2 | program eventbaseline, eclass 3 | syntax [, pre(integer 1) post(integer 3) baseline(string) generate(string) level(real 95)] [graph] 4 | if ("`level'" == "") { 5 | local level 95 6 | } 7 | if ("`baseline'" == "") { 8 | local baseline "-1" 9 | } 10 | if (e(cmd) != "xthdidregress") { 11 | display in red "eventstudy can only be used after xthdidregress" 12 | error 198 13 | } 14 | local T1 = `pre'-1 15 | local K = `pre'+`post'+1 16 | 17 | local depvar = e(depvar) 18 | local cohortvar = e(cohortvar) 19 | local timevar = e(timevar) 20 | tempname bad_coef bad_Var Wcum W0 b V Nobs coefplot tlabels 21 | 22 | tempvar exclude esample 23 | * exclude observations outside of the event window 24 | quietly generate `exclude' = cond(`cohortvar' == 0, 0, !inrange(`timevar' - `cohortvar', -`pre', `post')) 25 | quietly generate `esample' = e(sample) & (`exclude' == 0) 26 | quietly count if `esample' 27 | local Nobs = r(N) 28 | 29 | quietly estat aggregation, dynamic(-`T1'/`post') 30 | matrix `bad_coef' = r(b) 31 | matrix `bad_Var' = r(V) 32 | 33 | matrix `Wcum' = I(`K') 34 | forvalues i = 1/`pre' { 35 | forvalues j = 1/`i' { 36 | matrix `Wcum'[`j', `i'] = -1.0 37 | } 38 | } 39 | matrix `Wcum' = `Wcum'[1..., 1..`pre'-1], `Wcum'[1..., `pre'+1..`pre'+`post'+1] 40 | 41 | if ("`baseline'" == "average") { 42 | matrix `W0' = I(`K') - (J(`K', `pre', 1/`pre'), J(`K', `post'+1, 0)) 43 | } 44 | else if ("`baseline'" == "atet") { 45 | matrix `W0' = (J(1, `pre', -1/`pre'), J(1, `post'+1, 1/(`post'+1))) 46 | } 47 | else { 48 | if (!inrange(`baseline', -`pre', -1)) { 49 | display in red "Baseline must be between -`pre' and -1" 50 | error 198 51 | } 52 | matrix `W0' = I(`K') 53 | local bl = `pre' + `baseline' + 1 54 | forvalues i = 1/`K' { 55 | matrix `W0'[`i', `bl'] = `W0'[`i', `bl'] - 1.0 56 | } 57 | } 58 | matrix `b' = `bad_coef' * `Wcum'' * `W0'' 59 | matrix `V' = `W0' * `Wcum' * `bad_Var' * `Wcum'' * `W0'' 60 | 61 | if ("`baseline'" == "atet") { 62 | local colnames "ATET" 63 | } 64 | else { 65 | * label coefficients 66 | forvalues t = -`pre'/`post' { 67 | local colnames `colnames' `t' 68 | } 69 | } 70 | matrix colname `b' = `colnames' 71 | matrix colname `V' = `colnames' 72 | matrix rowname `V' = `colnames' 73 | 74 | matrix `coefplot' = J(`K', 4, .) 75 | matrix colname `coefplot' = xvar b ll ul 76 | local tlabels "" 77 | forvalues t = -`pre'/`post' { 78 | local tlabels `tlabels' `t' 79 | local i = `t' + `pre' + 1 80 | matrix `coefplot'[`i', 1] = `t'' 81 | matrix `coefplot'[`i', 2] = `b'[1, `i'] 82 | matrix `coefplot'[`i', 3] = `b'[1, `i'] + invnormal((100-`level')/200) * sqrt(`V'[`i', `i']) 83 | matrix `coefplot'[`i', 4] = `b'[1, `i'] - invnormal((100-`level')/200) * sqrt(`V'[`i', `i']) 84 | } 85 | 86 | tempname coef lower upper 87 | if ("`generate'" != "") { 88 | capture frame drop `generate' 89 | frame create `generate' time coef lower upper 90 | forvalues t = -`pre'/`post' { 91 | local i = `t' + `pre' + 1 92 | scalar `coef' = `b'[1, `i'] 93 | scalar `lower' = `b'[1, `i'] + invnormal((100-`level')/200) * sqrt(`V'[`i', `i']) 94 | scalar `upper' = `b'[1, `i'] - invnormal((100-`level')/200) * sqrt(`V'[`i', `i']) 95 | frame post `generate' (`t') (`coef') (`lower') (`upper') 96 | } 97 | frame `generate': tsset time 98 | frame `generate': format coef lower upper %9.3f 99 | } 100 | 101 | ereturn post `b' `V', obs(`Nobs') esample(`esample') 102 | ereturn local depvar `depvar' 103 | ereturn local cmd eventstudy 104 | ereturn local cmdline eventstudy `0' 105 | 106 | _coef_table_header, title(Event study relative to `baseline') width(62) 107 | display 108 | _coef_table, bmat(e(b)) vmat(e(V)) level(`level') /// 109 | depname(`depvar') coeftitle(ATET) 110 | 111 | if ("`graph'" == "graph") { 112 | hetdid_coefplot, mat(`coefplot') title(Event study relative to `baseline') /// 113 | ylb(`depvar') xlb("Length of exposure to the treatment") /// 114 | yline(0) legend(off) level(`level') yline(0, extend) ytick(0, add) ylabel(0, add) xlabel(`tlabels') 115 | } 116 | end 117 | 118 | -------------------------------------------------------------------------------- /eventbaseline.pkg: -------------------------------------------------------------------------------- 1 | 2 | v 3 3 | 4 | d EVENTBASELINE - Correct Event Study After XTHDIDREGRESS 5 | d 6 | d Authors: Koren, Miklós (https://koren.mk) 7 | d Support: 8 | d 9 | d `eventbaseline` transforms the coefficients estimated by `xthdidregress` into a correct event study relative to a baseline. The reported coefficients are the average treatment effects on the treated (ATT) for each period relative to the baseline. The baseline can be either a period before the treatment or the average of the pre-treatment periods. 10 | d 11 | d For details, see website: 12 | d https://github.com/codedthinking/eventbaseline 13 | d 14 | d Requires: Stata version 18 15 | d 16 | d Distribution-Date: 20240123 17 | d 18 | 19 | 20 | f stata.toc 21 | f eventbaseline.pkg 22 | f eventbaseline.ado 23 | f eventbaseline.sthlp 24 | f df.dta 25 | f example.do 26 | f eventstudy_kink.png 27 | f eventstudy_correct.png -------------------------------------------------------------------------------- /eventbaseline.sthlp: -------------------------------------------------------------------------------- 1 | {smcl} 2 | 3 | 4 | {marker eventbaseline-correct-event-study-after-xthdidregress}{...} 5 | {title:{cmd:eventbaseline} Correct event study after {cmd:xthdidregress}} 6 | 7 | 8 | {marker syntax}{...} 9 | {title:Syntax} 10 | 11 | {text}{phang2}{cmd:eventbaseline}, [{bf:pre}(#) {bf:post}(#) {bf:baseline}({it:string}) {bf:graph} {bf:generate}({it:name})]{p_end} 12 | 13 | 14 | {pstd}{cmd:eventbaseline} transforms the coefficients estimated by {cmd:xthdidregress} into a correct event study relative to a baseline. The reported coefficients are the average treatment effects on the treated (ATT) for each period relative to the baseline. The baseline can be either a period before the treatment or the average of the pre-treatment periods.{p_end} 15 | 16 | {pstd}The package can be installed with{p_end} 17 | 18 | {phang2}{cmd}. net install eventbaseline, from(https://raw.githubusercontent.com/codedthinking/eventbaseline/main/) 19 | 20 | {pstd}If you require a specific version, use{p_end} 21 | 22 | {phang2}{cmd}. net install eventbaseline, from(https://github.com/korenmiklos/codedthinking/eventbaseline/raw/v0.7.0/) 23 | 24 | 25 | {marker options}{...} 26 | {title:Options} 27 | 28 | 29 | {marker options-1}{...} 30 | {dlgtab:Options} 31 | 32 | {synoptset tabbed}{...} 33 | {synopthdr:Option} 34 | {synoptline} 35 | {synopt:{bf:pre}}Number of periods before treatment to include in the estimation (default 1){p_end} 36 | {synopt:{bf:post}}Number of periods after treatment to include in the estimation (default 3){p_end} 37 | {synopt:{bf:baseline}}Either a negative number between {cmd:-pre} and {cmd:-1} or {cmd:average}, or {cmd:atet}. If {cmd:-k}, the baseline is the kth period before the treatment. If {cmd:average}, the baseline is the average of the pre-treatment periods. If {cmd:atet}, the regression table reports the average of the post-treatment periods minus the average of the pre-treatment periods. Default is {cmd:-1}.{p_end} 38 | {synopt:{bf:graph} (optional)}Plot the event study graph with the default settings of {cmd:hetdid_coefplot}.{p_end} 39 | {synopt:{bf:generate} (optional)}Name of the frame to store the coefficients and their confidence interval.{p_end} 40 | {synoptline} 41 | 42 | 43 | {marker background}{...} 44 | {title:Background} 45 | 46 | {pstd}{cmd:xthdidregress} returns ATET between {cmd:t} and {cmd:t-1} whenever {cmd:t} is before the treatment. That is, pretrends are reported as first differences, whereas actual treatment effects are reported as difference relative to the period before treatment. This can lead to misleading event study plots (Roth 2024a). The {cmd:eventbaseline} command transforms the coefficients into a correct event study relative to a baseline.{p_end} 47 | 48 | 49 | {marker remarks}{...} 50 | {title:Remarks} 51 | 52 | {pstd}The command can only be run after {cmd:xthdidregress}.{p_end} 53 | 54 | {pstd}The command also returns, as part of {cmd:e()}, the coefficients and standard errors. See {cmd:ereturn list} after running the command. Typical post-estimation commands can be used, such as {cmd:outreg2} or {cmd:estout}.{p_end} 55 | 56 | {pstd}The reported number of observations is also corrected to exclude the treated periods outside the reported event window.{p_end} 57 | 58 | {pstd}If the {cmd:generate} option is used, the returned frame contains the following variables:{p_end} 59 | 60 | {text}{phang2}{cmd:time}: the time period relative to the baseline{p_end} 61 | {phang2}{cmd:coef}: the estimated coefficient{p_end} 62 | {phang2}{cmd:lower}: the lower bound of the 95% confidence interval{p_end} 63 | {phang2}{cmd:upper}: the upper bound of the 95% confidence interval{p_end} 64 | 65 | 66 | {pstd}The frame is {cmd:tsset} by {cmd:time}, so {cmd:tsline} can be used to plot the event study.{p_end} 67 | 68 | 69 | {marker examples}{...} 70 | {title:Examples} 71 | 72 | {pstd}See {cmd:example.do} and {cmd:example.log} for a full example.{p_end} 73 | 74 | {phang2}{cmd}. . use "df.dta" 75 | . replace t = t + 100 76 | . xtset i t 77 | . xthdidregress ra (y) (d), group(i) 78 | note: variable _did_cohort, containing cohort indicators formed by treatment 79 | variable d and group variable i, was added to the dataset. 80 | 81 | 82 | 83 | . eventbaseline, pre(5) post(5) baseline(-1) graph 84 | 85 | Time variable: time, -5 to 5 86 | Delta: 1 unit 87 | 88 | Event study relative to -1 Number of obs = 1,850 89 | 90 | ------------------------------------------------------------------------------ 91 | y | ATET Std. err. z P>|z| [95% conf. interval] 92 | -------------+---------------------------------------------------------------- 93 | -5 | -2.31541 .2415591 -9.59 0.000 -2.788857 -1.841963 94 | -4 | -1.310102 .2551332 -5.13 0.000 -1.810153 -.8100498 95 | -3 | -1.256003 .284372 -4.42 0.000 -1.813362 -.6986446 96 | -2 | -.4307123 .2619239 -1.64 0.100 -.9440736 .082649 97 | -1 | 0 (omitted) 98 | 0 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 99 | 1 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 100 | 2 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 101 | 3 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 102 | 4 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 103 | 5 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 104 | ------------------------------------------------------------------------------ 105 | 106 | 107 | 108 | {phang2}{cmd}. . xthdidregress ra (y) (d), group(i) 109 | note: variable _did_cohort, containing cohort indicators formed by treatment 110 | variable d and group variable i, was added to the dataset. 111 | 112 | 113 | 114 | . eventbaseline, pre(5) post(5) baseline(atet) 115 | 116 | Event study relative to atet Number of obs = 1,850 117 | 118 | ------------------------------------------------------------------------------ 119 | y | ATET Std. err. z P>|z| [95% conf. interval] 120 | -------------+---------------------------------------------------------------- 121 | ATET | 2.835658 .1134013 25.01 0.000 2.613396 3.05792 122 | ------------------------------------------------------------------------------ 123 | 124 | 125 | {marker authors}{...} 126 | {title:Authors} 127 | 128 | {text}{phang2}Miklós Koren (Central European University, {browse "https://koren.mk":https://koren.mk}), {it:maintainer}{p_end} 129 | 130 | 131 | 132 | {marker license-and-citation}{...} 133 | {title:License and Citation} 134 | 135 | {pstd}You are free to use this package under the terms of its {browse "LICENSE":license}. If you use it, please the software package in your work:{p_end} 136 | 137 | {text}{phang2}Koren, Miklós. 2024. "EVENTBASELINE: Correct Event Study After XTHDIDREGRESS. [software]" Available at {browse "https://github.com/codedthinking/eventbaseline":https://github.com/codedthinking/eventbaseline}.{p_end} 138 | 139 | 140 | 141 | {marker references}{...} 142 | {title:References} 143 | 144 | {text}{phang2}Roth, Jonathan. 2024a. "Interpreting Event-Studies from Recent Difference-in-Differences Methods." Available at {browse "https://www.jonathandroth.com/assets/files/HetEventStudies.pdf":https://www.jonathandroth.com/assets/files/HetEventStudies.pdf}. Last accessed January 23, 2024.{p_end} 145 | {phang2}Roth, Jonathan. 2024b. "Test Data for >Interpreting Event-Studies from Recent Difference-in-Differences Methods< [data set]." Available at {browse "https://github.com/jonathandroth/HetEventStudies/raw/master/output/df.dta":https://github.com/jonathandroth/HetEventStudies/raw/master/output/df.dta} Last accessed January 23, 2024.{p_end} 146 | -------------------------------------------------------------------------------- /eventbaseline.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedthinking/eventstudy/5d14efe611e2ca8248ab8ee3851df2b70dd5a96c/eventbaseline.zip -------------------------------------------------------------------------------- /eventstudy_correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedthinking/eventstudy/5d14efe611e2ca8248ab8ee3851df2b70dd5a96c/eventstudy_correct.png -------------------------------------------------------------------------------- /eventstudy_kink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedthinking/eventstudy/5d14efe611e2ca8248ab8ee3851df2b70dd5a96c/eventstudy_kink.png -------------------------------------------------------------------------------- /example.do: -------------------------------------------------------------------------------- 1 | clear all 2 | use "df.dta" 3 | 4 | * time index has to be non-negative for xtset 5 | replace t = t + 100 6 | xtset i t 7 | 8 | * illustrate event study plot with kink 9 | xthdidregress ra (y) (d), group(i) 10 | estat aggregation, dynamic(-4/5) graph 11 | graph export "eventstudy_kink.png", replace 12 | 13 | * event study plot with correct baseline 14 | xthdidregress ra (y) (d), group(i) 15 | eventbaseline, pre(5) post(5) baseline(-1) graph 16 | graph export "eventstudy_correct.png", replace 17 | 18 | * ATET as difference of average of after and before 19 | xthdidregress ra (y) (d), group(i) 20 | eventbaseline, pre(5) post(5) baseline(atet) 21 | -------------------------------------------------------------------------------- /example.log: -------------------------------------------------------------------------------- 1 | 2 | ___ ____ ____ ____ ____ ® 3 | /__ / ____/ / ____/ 18.0 4 | ___/ / /___/ / /___/ MP—Parallel Edition 5 | 6 | Statistics and Data Science Copyright 1985-2023 StataCorp LLC 7 | StataCorp 8 | 4905 Lakeway Drive 9 | College Station, Texas 77845 USA 10 | 800-STATA-PC https://www.stata.com 11 | 979-696-4600 stata@stata.com 12 | 13 | Stata license: Single-user 2-core perpetual 14 | Serial number: 501806323834 15 | Licensed to: Miklós Koren 16 | Central European University 17 | 18 | Notes: 19 | 1. Stata is running in batch mode. 20 | 2. Unicode is supported; see help unicode_advice. 21 | 3. More than 2 billion observations are allowed; see help obs_advice. 22 | 4. Maximum number of variables is set to 5,000 but can be increased; 23 | see help set_maxvar. 24 | 25 | . do example.do 26 | 27 | . clear all 28 | 29 | . use "df.dta" 30 | 31 | . 32 | . * time index has to be non-negative for xtset 33 | . replace t = t + 100 34 | (2,600 real changes made) 35 | 36 | . xtset i t 37 | 38 | Panel variable: i (strongly balanced) 39 | Time variable: t, 85 to 110 40 | Delta: 1 unit 41 | 42 | . 43 | . * illustrate event study plot with kink 44 | . xthdidregress ra (y) (d), group(i) 45 | note: variable _did_cohort, containing cohort indicators formed by treatment 46 | variable d and group variable i, was added to the dataset. 47 | 48 | Computing ATET for each cohort and time: 49 | Cohort 101 (25): ..........10..........20..... done 50 | 51 | Treatment and time information 52 | 53 | Time variable: t 54 | Time interval: 85 to 110 55 | Control: _did_cohort = 0 56 | Treatment: _did_cohort > 0 57 | ------------------------------- 58 | | _did_cohort 59 | ------------------+------------ 60 | Number of cohorts | 2 61 | ------------------+------------ 62 | Number of obs | 63 | Never treated | 1300 64 | 101 | 1300 65 | ------------------------------- 66 | 67 | Heterogeneous-treatment-effects regression Number of obs = 2,600 68 | Number of panels = 100 69 | Estimator: Regression adjustment 70 | Panel variable: i 71 | Treatment level: i 72 | Control group: Never treated 73 | 74 | (Std. err. adjusted for 100 clusters in i) 75 | ------------------------------------------------------------------------------ 76 | | Robust 77 | Cohort | ATET std. err. z P>|z| [95% conf. interval] 78 | -------------+---------------------------------------------------------------- 79 | t | 80 | 86 | .6438261 .2782756 2.31 0.021 .0984159 1.189236 81 | 87 | .5881773 .2564366 2.29 0.022 .0855708 1.090784 82 | 88 | .2322243 .2531081 0.92 0.359 -.2638585 .7283072 83 | 89 | .6410027 .2732471 2.35 0.019 .1054483 1.176557 84 | 90 | .5457171 .2926433 1.86 0.062 -.0278533 1.119288 85 | 91 | .3474518 .2739187 1.27 0.205 -.189419 .8843226 86 | 92 | .650674 .3148242 2.07 0.039 .03363 1.267718 87 | 93 | .5101111 .2984348 1.71 0.087 -.0748103 1.095033 88 | 94 | .4599449 .2912541 1.58 0.114 -.1109026 1.030792 89 | 95 | .2097397 .2575765 0.81 0.415 -.2951009 .7145803 90 | 96 | .4799807 .2724344 1.76 0.078 -.0539809 1.013942 91 | 97 | 1.005308 .2642817 3.80 0.000 .4873255 1.523291 92 | 98 | .0540982 .2977005 0.18 0.856 -.5293841 .6375804 93 | 99 | .8252911 .2793316 2.95 0.003 .2778113 1.372771 94 | 100 | .4307123 .2619239 1.64 0.100 -.082649 .9440736 95 | 101 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 96 | 102 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 97 | 103 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 98 | 104 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 99 | 105 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 100 | 106 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 101 | 107 | 3.588873 .2599594 13.81 0.000 3.079362 4.098384 102 | 108 | 3.733354 .2695227 13.85 0.000 3.2051 4.261609 103 | 109 | 4.487729 .2540163 17.67 0.000 3.989866 4.985592 104 | 110 | 4.716976 .2821311 16.72 0.000 4.164009 5.269943 105 | ------------------------------------------------------------------------------ 106 | 107 | . estat aggregation, dynamic(-4/5) graph 108 | 109 | Duration of exposure ATET Number of obs = 2,600 110 | 111 | (Std. err. adjusted for 100 clusters in i) 112 | ------------------------------------------------------------------------------ 113 | | Robust 114 | Exposure | ATET std. err. z P>|z| [95% conf. interval] 115 | -------------+---------------------------------------------------------------- 116 | -4 | 1.005308 .2642817 3.80 0.000 .4873255 1.523291 117 | -3 | .0540982 .2977005 0.18 0.856 -.5293841 .6375804 118 | -2 | .8252911 .2793316 2.95 0.003 .2778113 1.372771 119 | -1 | .4307123 .2619239 1.64 0.100 -.082649 .9440736 120 | 0 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 121 | 1 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 122 | 2 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 123 | 3 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 124 | 4 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 125 | 5 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 126 | ------------------------------------------------------------------------------ 127 | Note: Exposure is the number of periods since the first treatment time. 128 | 129 | . graph export "eventstudy_kink.png", replace 130 | file eventstudy_kink.png written in PNG format 131 | 132 | . 133 | . * event study plot with correct baseline 134 | . xthdidregress ra (y) (d), group(i) 135 | note: variable _did_cohort, containing cohort indicators formed by treatment 136 | variable d and group variable i, was added to the dataset. 137 | 138 | Computing ATET for each cohort and time: 139 | Cohort 101 (25): ..........10..........20..... done 140 | 141 | Treatment and time information 142 | 143 | Time variable: t 144 | Time interval: 85 to 110 145 | Control: _did_cohort = 0 146 | Treatment: _did_cohort > 0 147 | ------------------------------- 148 | | _did_cohort 149 | ------------------+------------ 150 | Number of cohorts | 2 151 | ------------------+------------ 152 | Number of obs | 153 | Never treated | 1300 154 | 101 | 1300 155 | ------------------------------- 156 | 157 | Heterogeneous-treatment-effects regression Number of obs = 2,600 158 | Number of panels = 100 159 | Estimator: Regression adjustment 160 | Panel variable: i 161 | Treatment level: i 162 | Control group: Never treated 163 | 164 | (Std. err. adjusted for 100 clusters in i) 165 | ------------------------------------------------------------------------------ 166 | | Robust 167 | Cohort | ATET std. err. z P>|z| [95% conf. interval] 168 | -------------+---------------------------------------------------------------- 169 | t | 170 | 86 | .6438261 .2782756 2.31 0.021 .0984159 1.189236 171 | 87 | .5881773 .2564366 2.29 0.022 .0855708 1.090784 172 | 88 | .2322243 .2531081 0.92 0.359 -.2638585 .7283072 173 | 89 | .6410027 .2732471 2.35 0.019 .1054483 1.176557 174 | 90 | .5457171 .2926433 1.86 0.062 -.0278533 1.119288 175 | 91 | .3474518 .2739187 1.27 0.205 -.189419 .8843226 176 | 92 | .650674 .3148242 2.07 0.039 .03363 1.267718 177 | 93 | .5101111 .2984348 1.71 0.087 -.0748103 1.095033 178 | 94 | .4599449 .2912541 1.58 0.114 -.1109026 1.030792 179 | 95 | .2097397 .2575765 0.81 0.415 -.2951009 .7145803 180 | 96 | .4799807 .2724344 1.76 0.078 -.0539809 1.013942 181 | 97 | 1.005308 .2642817 3.80 0.000 .4873255 1.523291 182 | 98 | .0540982 .2977005 0.18 0.856 -.5293841 .6375804 183 | 99 | .8252911 .2793316 2.95 0.003 .2778113 1.372771 184 | 100 | .4307123 .2619239 1.64 0.100 -.082649 .9440736 185 | 101 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 186 | 102 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 187 | 103 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 188 | 104 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 189 | 105 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 190 | 106 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 191 | 107 | 3.588873 .2599594 13.81 0.000 3.079362 4.098384 192 | 108 | 3.733354 .2695227 13.85 0.000 3.2051 4.261609 193 | 109 | 4.487729 .2540163 17.67 0.000 3.989866 4.985592 194 | 110 | 4.716976 .2821311 16.72 0.000 4.164009 5.269943 195 | ------------------------------------------------------------------------------ 196 | 197 | . eventbaseline, pre(5) post(5) baseline(-1) generate(eventstudy_correct) 198 | 199 | Time variable: time, -5 to 5 200 | Delta: 1 unit 201 | 202 | Event study relative to -1 Number of obs = 1,850 203 | 204 | ------------------------------------------------------------------------------ 205 | y | ATET Std. err. z P>|z| [95% conf. interval] 206 | -------------+---------------------------------------------------------------- 207 | -5 | -2.31541 .2415591 -9.59 0.000 -2.788857 -1.841963 208 | -4 | -1.310102 .2551332 -5.13 0.000 -1.810153 -.8100498 209 | -3 | -1.256003 .284372 -4.42 0.000 -1.813362 -.6986446 210 | -2 | -.4307123 .2619239 -1.64 0.100 -.9440736 .082649 211 | -1 | 0 (omitted) 212 | 0 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 213 | 1 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 214 | 2 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 215 | 3 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 216 | 4 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 217 | 5 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 218 | ------------------------------------------------------------------------------ 219 | 220 | . frame eventstudy_correct: tsline upper coef lower 221 | 222 | . graph export "eventstudy_correct.png", replace 223 | (file eventstudy_correct.png not found) 224 | file eventstudy_correct.png written in PNG format 225 | 226 | . 227 | . * ATET as difference of average of after and before 228 | . xthdidregress ra (y) (d), group(i) 229 | note: variable _did_cohort, containing cohort indicators formed by treatment 230 | variable d and group variable i, was added to the dataset. 231 | 232 | Computing ATET for each cohort and time: 233 | Cohort 101 (25): ..........10..........20..... done 234 | 235 | Treatment and time information 236 | 237 | Time variable: t 238 | Time interval: 85 to 110 239 | Control: _did_cohort = 0 240 | Treatment: _did_cohort > 0 241 | ------------------------------- 242 | | _did_cohort 243 | ------------------+------------ 244 | Number of cohorts | 2 245 | ------------------+------------ 246 | Number of obs | 247 | Never treated | 1300 248 | 101 | 1300 249 | ------------------------------- 250 | 251 | Heterogeneous-treatment-effects regression Number of obs = 2,600 252 | Number of panels = 100 253 | Estimator: Regression adjustment 254 | Panel variable: i 255 | Treatment level: i 256 | Control group: Never treated 257 | 258 | (Std. err. adjusted for 100 clusters in i) 259 | ------------------------------------------------------------------------------ 260 | | Robust 261 | Cohort | ATET std. err. z P>|z| [95% conf. interval] 262 | -------------+---------------------------------------------------------------- 263 | t | 264 | 86 | .6438261 .2782756 2.31 0.021 .0984159 1.189236 265 | 87 | .5881773 .2564366 2.29 0.022 .0855708 1.090784 266 | 88 | .2322243 .2531081 0.92 0.359 -.2638585 .7283072 267 | 89 | .6410027 .2732471 2.35 0.019 .1054483 1.176557 268 | 90 | .5457171 .2926433 1.86 0.062 -.0278533 1.119288 269 | 91 | .3474518 .2739187 1.27 0.205 -.189419 .8843226 270 | 92 | .650674 .3148242 2.07 0.039 .03363 1.267718 271 | 93 | .5101111 .2984348 1.71 0.087 -.0748103 1.095033 272 | 94 | .4599449 .2912541 1.58 0.114 -.1109026 1.030792 273 | 95 | .2097397 .2575765 0.81 0.415 -.2951009 .7145803 274 | 96 | .4799807 .2724344 1.76 0.078 -.0539809 1.013942 275 | 97 | 1.005308 .2642817 3.80 0.000 .4873255 1.523291 276 | 98 | .0540982 .2977005 0.18 0.856 -.5293841 .6375804 277 | 99 | .8252911 .2793316 2.95 0.003 .2778113 1.372771 278 | 100 | .4307123 .2619239 1.64 0.100 -.082649 .9440736 279 | 101 | .4105212 .2379038 1.73 0.084 -.0557617 .8768041 280 | 102 | .9888228 .2764365 3.58 0.000 .4470172 1.530629 281 | 103 | 1.859271 .2727555 6.82 0.000 1.32468 2.393862 282 | 104 | 1.865648 .2840544 6.57 0.000 1.308911 2.422384 283 | 105 | 2.591579 .2831633 9.15 0.000 2.036589 3.146569 284 | 106 | 2.923434 .2730864 10.71 0.000 2.388195 3.458674 285 | 107 | 3.588873 .2599594 13.81 0.000 3.079362 4.098384 286 | 108 | 3.733354 .2695227 13.85 0.000 3.2051 4.261609 287 | 109 | 4.487729 .2540163 17.67 0.000 3.989866 4.985592 288 | 110 | 4.716976 .2821311 16.72 0.000 4.164009 5.269943 289 | ------------------------------------------------------------------------------ 290 | 291 | . eventbaseline, pre(5) post(5) baseline(atet) 292 | 293 | Event study relative to atet Number of obs = 1,850 294 | 295 | ------------------------------------------------------------------------------ 296 | y | ATET Std. err. z P>|z| [95% conf. interval] 297 | -------------+---------------------------------------------------------------- 298 | ATET | 2.835658 .1134013 25.01 0.000 2.613396 3.05792 299 | ------------------------------------------------------------------------------ 300 | 301 | . 302 | end of do-file 303 | -------------------------------------------------------------------------------- /files.txt: -------------------------------------------------------------------------------- 1 | stata.toc 2 | eventbaseline.pkg 3 | eventbaseline.ado 4 | eventbaseline.sthlp 5 | df.dta 6 | example.do 7 | eventstudy_kink.png 8 | eventstudy_correct.png 9 | -------------------------------------------------------------------------------- /packager.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import frontmatter 3 | import jinja2 4 | import datetime 5 | 6 | pkg_template = ''' 7 | v 3 8 | 9 | d {{title}} 10 | d 11 | d Authors: {{author}} 12 | d Support: 13 | d {% for line in description.split('\n') %} 14 | d {{line}}{% endfor %} 15 | d For details, see website: 16 | d {{url}} 17 | d 18 | d Requires: {{requires}} 19 | d 20 | d Distribution-Date: {{stata_date}} 21 | d 22 | 23 | {% for f in files %} 24 | f {{f}}{% endfor %} 25 | ''' 26 | 27 | toc_template = ''' 28 | v 3 29 | d {{author}} 30 | d {{url}} 31 | p {{command}} {{title}} 32 | ''' 33 | 34 | citation_template = ''' 35 | cff-version: 1.2.0 36 | title: {{title}} 37 | message: >- 38 | If you use this software, please cite it using the 39 | metadata from this file. 40 | type: software 41 | authors: 42 | - given-names: 43 | family-names: '{{author}}' 44 | identifiers: 45 | - type: url 46 | value: '{{url}}' 47 | version: {{version}} 48 | date-released: '{{date}}' 49 | ''' 50 | 51 | def render_pkg(config): 52 | return jinja2.Template(pkg_template).render(config) 53 | 54 | def render_toc(config): 55 | return jinja2.Template(toc_template).render(config) 56 | 57 | def render_citation(config): 58 | return jinja2.Template(citation_template).render(config) 59 | 60 | def stata_date(dt: datetime.date): 61 | return f'{dt.year}{dt.month:02}{dt.day:02}' 62 | 63 | def version_date(dt: datetime.date): 64 | # 2019-10-09 -> 09oct2019 65 | # 2023-03-01 -> 01mar2023 66 | return f'{dt.day:02}{dt.strftime("%b").lower()}{dt.year}' 67 | 68 | def replace_version(config, text): 69 | version = f'*! version {config["version"]} {version_date(config["date"])}' 70 | lines = [version if line[0:2] == '*!' else line for line in text.split('\n')] 71 | return '\n'.join(lines) 72 | 73 | 74 | if __name__ == '__main__': 75 | config = frontmatter.load(sys.argv[1]) 76 | command = sys.argv[2] 77 | config['command'] = command 78 | config['stata_date'] = stata_date(config['date']) 79 | with open('files.txt', 'rt') as f: 80 | files = f.readlines() 81 | config['files'] = [f.strip() for f in files] 82 | with open(f'{command}.ado', 'rt') as f: 83 | adotext = f.read() 84 | with open(f'{command}.pkg', 'wt') as f: 85 | f.write(render_pkg(config)) 86 | with open(f'stata.toc', 'wt') as f: 87 | f.write(render_toc(config)) 88 | with open('CITATION.cff', 'wt') as f: 89 | f.write(render_citation(config)) 90 | with open(f'{command}.ado', 'wt') as f: 91 | f.write(replace_version(config, adotext)) 92 | 93 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "jinja2" 5 | version = "3.1.2" 6 | description = "A very fast and expressive template engine." 7 | optional = false 8 | python-versions = ">=3.7" 9 | files = [ 10 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 11 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 12 | ] 13 | 14 | [package.dependencies] 15 | MarkupSafe = ">=2.0" 16 | 17 | [package.extras] 18 | i18n = ["Babel (>=2.7)"] 19 | 20 | [[package]] 21 | name = "markupsafe" 22 | version = "2.1.3" 23 | description = "Safely add untrusted strings to HTML/XML markup." 24 | optional = false 25 | python-versions = ">=3.7" 26 | files = [ 27 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, 28 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, 29 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, 30 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, 31 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, 32 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, 33 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, 34 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, 35 | {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, 36 | {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, 37 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, 38 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, 39 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, 40 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, 41 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, 42 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, 43 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, 44 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, 45 | {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, 46 | {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, 47 | {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, 48 | {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, 49 | {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, 50 | {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, 51 | {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, 52 | {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, 53 | {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, 54 | {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, 55 | {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, 56 | {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, 57 | {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, 58 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, 59 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, 60 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, 61 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, 62 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, 63 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, 64 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, 65 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, 66 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, 67 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, 68 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, 69 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, 70 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, 71 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, 72 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, 73 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, 74 | {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, 75 | {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, 76 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, 77 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, 78 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, 79 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, 80 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, 81 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, 82 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, 83 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, 84 | {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, 85 | {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, 86 | {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, 87 | ] 88 | 89 | [[package]] 90 | name = "python-frontmatter" 91 | version = "1.0.0" 92 | description = "Parse and manage posts with YAML (or other) frontmatter" 93 | optional = false 94 | python-versions = "*" 95 | files = [ 96 | {file = "python-frontmatter-1.0.0.tar.gz", hash = "sha256:e98152e977225ddafea6f01f40b4b0f1de175766322004c826ca99842d19a7cd"}, 97 | {file = "python_frontmatter-1.0.0-py3-none-any.whl", hash = "sha256:766ae75f1b301ffc5fe3494339147e0fd80bc3deff3d7590a93991978b579b08"}, 98 | ] 99 | 100 | [package.dependencies] 101 | PyYAML = "*" 102 | 103 | [package.extras] 104 | docs = ["sphinx"] 105 | test = ["pyaml", "pytest", "toml"] 106 | 107 | [[package]] 108 | name = "pyyaml" 109 | version = "6.0.1" 110 | description = "YAML parser and emitter for Python" 111 | optional = false 112 | python-versions = ">=3.6" 113 | files = [ 114 | {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, 115 | {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, 116 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, 117 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, 118 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, 119 | {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, 120 | {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, 121 | {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, 122 | {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, 123 | {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, 124 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, 125 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, 126 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, 127 | {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, 128 | {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, 129 | {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, 130 | {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, 131 | {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, 132 | {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, 133 | {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, 134 | {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, 135 | {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, 136 | {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, 137 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, 138 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, 139 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, 140 | {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, 141 | {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, 142 | {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, 143 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, 144 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, 145 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, 146 | {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, 147 | {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, 148 | {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, 149 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, 150 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, 151 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, 152 | {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, 153 | {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, 154 | {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, 155 | {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, 156 | {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, 157 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, 158 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, 159 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, 160 | {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, 161 | {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, 162 | {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, 163 | {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, 164 | ] 165 | 166 | [metadata] 167 | lock-version = "2.0" 168 | python-versions = "^3.11" 169 | content-hash = "dbc4f30a015c14d03446a9a6dd8534f0557cf1f56abe226846c1f7abdbd2bdf7" 170 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "eventstudy" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Miklós Koren "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.11" 10 | python-frontmatter = "^1.0.0" 11 | Jinja2 = "^3.1.2" 12 | 13 | 14 | [build-system] 15 | requires = ["poetry-core"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /smcl.lua: -------------------------------------------------------------------------------- 1 | -- This is a sample custom writer for pandoc. It produces output 2 | -- that is very similar to that of pandoc's HTML writer. 3 | -- There is one new feature: code blocks marked with class 'dot' 4 | -- are piped through graphviz and images are included in the HTML 5 | -- output using 'data:' URLs. 6 | -- 7 | -- Invoke with: pandoc -t sample.lua 8 | -- 9 | -- Note: you need not have lua installed on your system to use this 10 | -- custom writer. However, if you do have lua installed, you can 11 | -- use it to test changes to the script. 'lua sample.lua' will 12 | -- produce informative error messages if your code contains 13 | -- syntax errors. 14 | 15 | -- Character escaping 16 | local function escape(s, in_attribute) 17 | return s:gsub("[<>&\"']", 18 | function(x) 19 | --if x == '<' then 20 | -- return '<' 21 | --elseif x == '>' then 22 | -- return '>' 23 | --elseif x == '&' then 24 | -- return '&' 25 | --else 26 | return x 27 | --end 28 | end) 29 | end 30 | 31 | -- Helper function to convert an attributes table into 32 | -- a string that can be put into HTML tags. 33 | local function attributes(attr) 34 | local attr_table = {} 35 | for x,y in pairs(attr) do 36 | if y and y ~= "" then 37 | table.insert(attr_table, ' ' .. x .. '="' .. escape(y,true) .. '"') 38 | end 39 | end 40 | return table.concat(attr_table) 41 | end 42 | 43 | -- Helper function that extracts the "id" element 44 | -- of an attributes table and returns it. 45 | local function attributesExtractID(attr) 46 | for x,y in pairs(attr) do 47 | if x=="id" and y and y ~= "" then 48 | return escape(y,true) 49 | end 50 | end 51 | end 52 | 53 | -- Run cmd on a temporary file containing inp and return result. 54 | local function pipe(cmd, inp) 55 | local tmp = os.tmpname() 56 | local tmph = io.open(tmp, "w") 57 | tmph:write(inp) 58 | tmph:close() 59 | local outh = io.popen(cmd .. " " .. tmp,"r") 60 | local result = outh:read("*all") 61 | outh:close() 62 | os.remove(tmp) 63 | return result 64 | end 65 | 66 | -- Table to store footnotes, so they can be included at the end. 67 | local notes = {} 68 | 69 | -- Blocksep is used to separate block elements. 70 | function Blocksep() 71 | return "\n\n" 72 | end 73 | 74 | -- This function is called once for the whole document. Parameters: 75 | -- body is a string, metadata is a table, variables is a table. 76 | -- This gives you a fragment. You could use the metadata table to 77 | -- fill variables in a custom lua template. Or, pass `--template=...` 78 | -- to pandoc, and pandoc will add do the template processing as 79 | -- usual. 80 | function Doc(body, metadata, variables) 81 | local buffer = {} 82 | local function add(s) 83 | table.insert(buffer, s) 84 | end 85 | add('{smcl}\n') 86 | add(body) 87 | if #notes > 0 then 88 | for _,note in pairs(notes) do 89 | add('{phang}') 90 | add(note) 91 | add('{p_end}') 92 | end 93 | end 94 | return table.concat(buffer,'\n') 95 | end 96 | 97 | -- The functions that follow render corresponding pandoc elements. 98 | -- s is always a string, attr is always a table of attributes, and 99 | -- items is always an array of strings (the items in a list). 100 | -- Comments indicate the types of other variables. 101 | 102 | function Str(s) 103 | return escape(s) 104 | end 105 | 106 | function Space() 107 | return " " 108 | end 109 | 110 | function LineBreak() 111 | return "{break}" 112 | end 113 | 114 | function Emph(s) 115 | return "{it:" .. s .. "}" 116 | end 117 | 118 | function Strong(s) 119 | return "{bf:" .. s .. "}" 120 | end 121 | 122 | function Subscript(s) 123 | return "" .. s .. "" 124 | end 125 | 126 | function Superscript(s) 127 | return "" .. s .. "" 128 | end 129 | 130 | function SmallCaps(s) 131 | return '' .. s .. '' 132 | end 133 | 134 | function Strikeout(s) 135 | return '' .. s .. '' 136 | end 137 | 138 | function Link(s, src, tit) 139 | return "{browse \"" .. escape(src,true) .. "\":" .. s .. "}" 140 | --return "" .. s .. "" 142 | end 143 | 144 | function Image(s, src, tit) 145 | return "" 147 | end 148 | 149 | function Code(s, attr) 150 | return "{cmd:" .. escape(s) .. "}" 151 | end 152 | 153 | function InlineMath(s) 154 | return "\\(" .. escape(s) .. "\\)" 155 | end 156 | 157 | function DisplayMath(s) 158 | return "\\[" .. escape(s) .. "\\]" 159 | end 160 | 161 | function Note(s) 162 | local num = #notes + 1 163 | -- insert the back reference right before the final closing tag. 164 | s = string.gsub(s, 165 | '(.*)' .. s .. '') 168 | -- return the footnote reference, linked to the note. 169 | return '' .. num .. '' 171 | end 172 | 173 | function Span(s, attr) 174 | return "" .. s .. "" 175 | end 176 | 177 | function Cite(s, cs) 178 | local ids = {} 179 | for _,cit in ipairs(cs) do 180 | table.insert(ids, cit.citationId) 181 | end 182 | return "" .. s .. "" 184 | end 185 | 186 | function Plain(s) 187 | return s 188 | end 189 | 190 | function Para(s) 191 | local pdirective = string.match(s, '^{p[^}]*}') 192 | if (pdirective == nil) then 193 | return "{pstd}" .. s .. "{p_end}" 194 | else 195 | return s .. "{p_end}" 196 | end 197 | end 198 | 199 | -- lev is an integer, the header level. 200 | function Header(lev, s, attr) 201 | if (lev==1) then 202 | return "\n{marker " .. attributesExtractID(attr) .. "}{...}\n{title:" .. s .. "}" 203 | elseif (lev==2) then 204 | return "\n{marker " .. attributesExtractID(attr) .. "}{...}\n{dlgtab:" .. s .. "}" 205 | else 206 | return "\n{marker " .. attributesExtractID(attr) .. "}{...}\n{syntab:" .. s .. "}" 207 | end 208 | end 209 | 210 | function BlockQuote(s) 211 | return "
\n" .. s .. "\n
" 212 | end 213 | 214 | function HorizontalRule() 215 | return "{hline}" 216 | end 217 | 218 | function CodeBlock(s, attr) 219 | -- If code block has class 'dot', pipe the contents through dot 220 | -- and base64, and include the base64-encoded png as a data: URL. 221 | if attr.class and string.match(' ' .. attr.class .. ' ',' dot ') then 222 | local png = pipe("base64", pipe("dot -Tpng", s)) 223 | return '' 224 | -- otherwise treat as code (one could pipe through a highlighter) 225 | else 226 | return "{phang2}{cmd}. " .. escape(s) 227 | end 228 | end 229 | 230 | function BulletList(items) 231 | local buffer = {} 232 | for _, item in pairs(items) do 233 | table.insert(buffer, "{phang2}" .. item .. "{p_end}") 234 | end 235 | return "{text}" .. table.concat(buffer, "\n") .. "\n" 236 | end 237 | 238 | function OrderedList(items) 239 | local buffer = {} 240 | for _, item in pairs(items) do 241 | table.insert(buffer, "
  • " .. item .. "
  • ") 242 | end 243 | return "
      \n" .. table.concat(buffer, "\n") .. "\n
    " 244 | end 245 | 246 | -- Revisit association list STackValue instance. 247 | function DefinitionList(items) 248 | local buffer = {} 249 | for _,item in pairs(items) do 250 | for k, v in pairs(item) do 251 | table.insert(buffer,"
    " .. k .. "
    \n
    " .. 252 | table.concat(v,"
    \n
    ") .. "
    ") 253 | end 254 | end 255 | return "
    \n" .. table.concat(buffer, "\n") .. "\n
    " 256 | end 257 | 258 | -- Convert pandoc alignment to something HTML can use. 259 | -- align is AlignLeft, AlignRight, AlignCenter, or AlignDefault. 260 | function html_align(align) 261 | if align == 'AlignLeft' then 262 | return 'left' 263 | elseif align == 'AlignRight' then 264 | return 'right' 265 | elseif align == 'AlignCenter' then 266 | return 'center' 267 | else 268 | return 'left' 269 | end 270 | end 271 | 272 | -- Caption is a string, aligns is an array of strings, 273 | -- widths is an array of floats, headers is an array of 274 | -- strings, rows is an array of arrays of strings. 275 | function Table(caption, aligns, widths, headers, rows) 276 | local buffer = {} 277 | local function add(s) 278 | table.insert(buffer, s) 279 | end 280 | if not (string.match(caption, 'col1width=(%d+)')==nil) then 281 | add("{synoptset " .. string.match(caption, 'col1width=(%d+)') .. " tabbed}{...}") 282 | else 283 | add("{synoptset tabbed}{...}") 284 | end 285 | local header_row = {} 286 | local empty_header = true 287 | for i, h in pairs(headers) do 288 | if (i==1) and not (h=="") then 289 | add('{synopthdr:' .. h .. '}\n{synoptline}') 290 | end 291 | empty_header = empty_header and h == "" 292 | end 293 | for _, row in pairs(rows) do 294 | if (#row>=2) then 295 | add('{synopt:' .. row[1] .. '}' .. row[2] .. '{p_end}') 296 | end 297 | end 298 | if not empty_header then 299 | add('{synoptline}') 300 | end 301 | return table.concat(buffer,'\n') 302 | end 303 | 304 | function Div(s, attr) 305 | return "\n" .. s .. "" 306 | end 307 | 308 | -- The following code will produce runtime warnings when you haven't defined 309 | -- all of the functions you need for the custom writer, so it's useful 310 | -- to include when you're working on a writer. 311 | local meta = {} 312 | meta.__index = 313 | function(_, key) 314 | io.stderr:write(string.format("WARNING: Undefined function '%s'\n",key)) 315 | return function() return "" end 316 | end 317 | setmetatable(_G, meta) 318 | 319 | -------------------------------------------------------------------------------- /stata.toc: -------------------------------------------------------------------------------- 1 | 2 | v 3 3 | d Koren, Miklós (https://koren.mk) 4 | d https://github.com/codedthinking/eventbaseline 5 | p eventbaseline EVENTBASELINE - Correct Event Study After XTHDIDREGRESS -------------------------------------------------------------------------------- /testdata.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedthinking/eventstudy/5d14efe611e2ca8248ab8ee3851df2b70dd5a96c/testdata.dta -------------------------------------------------------------------------------- /tsline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codedthinking/eventstudy/5d14efe611e2ca8248ab8ee3851df2b70dd5a96c/tsline.png --------------------------------------------------------------------------------