├── .Rbuildignore ├── vignettes ├── assets │ ├── reg1.aux │ ├── reg1a.aux │ ├── reg2.aux │ ├── main.pdf │ ├── reg1.pdf │ ├── reg1.png │ ├── reg1a.pdf │ ├── reg2.pdf │ ├── reg2.png │ ├── reg3.png │ ├── reg1.synctex.gz │ ├── reg1a.synctex.gz │ ├── reg2.synctex.gz │ ├── vignette_3_data.dta │ ├── vignette_1_Stata_fig1.jpg │ ├── vignette_1_Stata_fig2.jpg │ ├── vignette_1_Stata_fig3.jpg │ ├── reg2.tex │ ├── reg1.tex │ ├── reg1a.tex │ ├── reg1.fls │ ├── reg2.fls │ ├── reg1a.fls │ ├── reg1.fdb_latexmk │ ├── reg2.fdb_latexmk │ ├── reg1a.fdb_latexmk │ ├── reg1.log │ ├── reg2.log │ └── reg1a.log ├── src │ ├── vignette_3_R.R │ ├── vignette_3_Stata.do │ ├── vignette_2_Stata.do │ ├── vignette_1_Stata.do │ └── vignette_1_R.R └── vignette_2.md ├── R ├── data │ └── favara_imbs.RData ├── LICENSE ├── README.md ├── R │ ├── progressBar.R │ ├── rnames.R │ ├── did_multiplegt_dyn_by_check.R │ ├── favara_imbs-data.R │ ├── did_save_sample.R │ ├── did_multiplegt_dyn_normweights.R │ ├── did_multiplegt_dyn_graph.R │ ├── did_multiplegt_dyn_dfs.R │ ├── did_multiplegt_by_path.R │ ├── did_multiplegt_dyn_design.R │ ├── did_multiplegt_bootstrap.R │ └── print.R ├── man │ ├── rnames.did_multiplegt_dyn.Rd │ ├── print.did_multiplegt_dyn.Rd │ ├── summary.did_multiplegt_dyn.Rd │ └── favara_imbs.Rd ├── NAMESPACE ├── DESCRIPTION └── NEWS.md ├── did_multiplegt_dyn_all_pl ├── R │ ├── LICENSE │ ├── NAMESPACE │ ├── R │ │ ├── print.did_multiplegt_dyn_all_pl.R │ │ ├── add_periods.R │ │ └── did_multiplegt_dyn_all_pl.R │ ├── man │ │ ├── print.did_multiplegt_dyn_all_pl.Rd │ │ └── did_multiplegt_dyn_all_pl.Rd │ └── DESCRIPTION └── Stata │ ├── did_multiplegt_dyn_all_pl.pkg │ ├── stata.toc │ └── did_multiplegt_dyn_all_pl.ado ├── dist ├── DIDmultiplegtDYN_1.0.0.tar.gz ├── DIDmultiplegtDYN_1.0.1.tar.gz ├── DIDmultiplegtDYN_1.0.2.tar.gz ├── DIDmultiplegtDYN_1.0.3.tar.gz ├── DIDmultiplegtDYN_1.0.4.tar.gz ├── DIDmultiplegtDYN_1.0.5.tar.gz ├── DIDmultiplegtDYN_1.0.6.tar.gz ├── DIDmultiplegtDYN_1.0.7.tar.gz ├── DIDmultiplegtDYN_1.0.8.tar.gz ├── DIDmultiplegtDYN_1.0.9.tar.gz ├── DIDmultiplegtDYN_2.0.0.tar.gz ├── DIDmultiplegtDYN_2.1.0.tar.gz ├── DIDmultiplegtDYN_2.1.1.tar.gz ├── DIDmultiplegtDYN_2.1.2.tar.gz ├── DIDmultiplegtDYN_1.0.10.tar.gz ├── DIDmultiplegtDYN_1.0.11.tar.gz ├── DIDmultiplegtDYN_1.0.12.tar.gz ├── DIDmultiplegtDYN_1.0.13.tar.gz ├── DIDmultiplegtDYN_1.0.14.tar.gz ├── DIDmultiplegtDYN_1.0.15.tar.gz └── DIDmultiplegtDYNallPL_1.0.1.tar.gz ├── no_xlsx ├── DIDmultiplegtDYN │ ├── LICENSE │ ├── R │ │ ├── progressBar.R │ │ ├── rnames.R │ │ ├── did_multiplegt_dyn_by_check.R │ │ ├── joint_trends.R │ │ ├── did_save_sample.R │ │ ├── did_multiplegt_dyn_normweights.R │ │ ├── did_multiplegt_dyn_graph.R │ │ ├── did_multiplegt_by_path.R │ │ ├── did_multiplegt_dyn_dfs.R │ │ ├── did_multiplegt_dyn_design.R │ │ ├── did_multiplegt_bootstrap.R │ │ └── print.R │ ├── man │ │ ├── rnames.did_multiplegt_dyn.Rd │ │ ├── print.did_multiplegt_dyn.Rd │ │ └── summary.did_multiplegt_dyn.Rd │ ├── NAMESPACE │ ├── NEWS.md │ └── DESCRIPTION └── README.md ├── Examples did_multiplegt_dyn on various designs └── cleaned_data │ ├── .DS_Store │ ├── deryugina_2017.dta │ ├── east_et_al_2023.dta │ ├── gentzkowetal_didtextbook.dta │ └── hollingsworth_et_al_2022.dta ├── Python ├── example.py └── README.md ├── Examples ├── Examples_Stata.do ├── Examples_R.R └── R_vs_Stata.R ├── .github └── workflows │ └── R-CMD-check.yaml └── Stata └── prior_versions └── submission ssc 2024-02-16 └── bootstrap did_multiplegt_dyn.do /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^\.github$ 2 | -------------------------------------------------------------------------------- /vignettes/assets/reg1.aux: -------------------------------------------------------------------------------- 1 | \relax 2 | \gdef \@abspage@last{1} 3 | -------------------------------------------------------------------------------- /vignettes/assets/reg1a.aux: -------------------------------------------------------------------------------- 1 | \relax 2 | \gdef \@abspage@last{1} 3 | -------------------------------------------------------------------------------- /vignettes/assets/reg2.aux: -------------------------------------------------------------------------------- 1 | \relax 2 | \gdef \@abspage@last{1} 3 | -------------------------------------------------------------------------------- /R/data/favara_imbs.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/R/data/favara_imbs.RData -------------------------------------------------------------------------------- /R/LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2024 2 | COPYRIGHT HOLDER: Diego Ciccia, Felix Knau, Mélitine Malezieux, Doulo Sow, Clément de Chaisemartin 3 | -------------------------------------------------------------------------------- /vignettes/assets/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/main.pdf -------------------------------------------------------------------------------- /vignettes/assets/reg1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg1.pdf -------------------------------------------------------------------------------- /vignettes/assets/reg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg1.png -------------------------------------------------------------------------------- /vignettes/assets/reg1a.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg1a.pdf -------------------------------------------------------------------------------- /vignettes/assets/reg2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg2.pdf -------------------------------------------------------------------------------- /vignettes/assets/reg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg2.png -------------------------------------------------------------------------------- /vignettes/assets/reg3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg3.png -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2024 2 | COPYRIGHT HOLDER: Diego Ciccia, Felix Knau, Doulo Sow, Clément de Chaisemartin 3 | -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.0.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.1.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.2.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.3.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.4.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.5.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.6.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.6.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.7.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.8.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.8.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.9.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.9.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_2.0.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_2.0.0.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_2.1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_2.1.0.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_2.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_2.1.1.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_2.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_2.1.2.tar.gz -------------------------------------------------------------------------------- /vignettes/assets/reg1.synctex.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg1.synctex.gz -------------------------------------------------------------------------------- /vignettes/assets/reg1a.synctex.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg1a.synctex.gz -------------------------------------------------------------------------------- /vignettes/assets/reg2.synctex.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/reg2.synctex.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.10.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.10.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.11.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.11.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.12.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.12.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.13.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.13.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.14.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.14.tar.gz -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYN_1.0.15.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYN_1.0.15.tar.gz -------------------------------------------------------------------------------- /vignettes/assets/vignette_3_data.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/vignette_3_data.dta -------------------------------------------------------------------------------- /dist/DIDmultiplegtDYNallPL_1.0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/dist/DIDmultiplegtDYNallPL_1.0.1.tar.gz -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2024 2 | COPYRIGHT HOLDER: Diego Ciccia, Felix Knau, Mélitine Malezieux, Doulo Sow, Clément de Chaisemartin 3 | -------------------------------------------------------------------------------- /vignettes/assets/vignette_1_Stata_fig1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/vignette_1_Stata_fig1.jpg -------------------------------------------------------------------------------- /vignettes/assets/vignette_1_Stata_fig2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/vignette_1_Stata_fig2.jpg -------------------------------------------------------------------------------- /vignettes/assets/vignette_1_Stata_fig3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/vignettes/assets/vignette_1_Stata_fig3.jpg -------------------------------------------------------------------------------- /Examples did_multiplegt_dyn on various designs/cleaned_data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/Examples did_multiplegt_dyn on various designs/cleaned_data/.DS_Store -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,did_multiplegt_dyn_all_pl) 4 | export(did_multiplegt_dyn_all_pl) 5 | import(DIDmultiplegtDYN) 6 | import(dplyr) 7 | -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/Stata/did_multiplegt_dyn_all_pl.pkg: -------------------------------------------------------------------------------- 1 | v 1.0.0 2 | d 'did_multiplegt_dyn_all_pl': did_multiplegt_dyn extension to retrieve all feasible placebos. 3 | d 4 | d 5 | F did_multiplegt_dyn_all_pl.ado 6 | -------------------------------------------------------------------------------- /Examples did_multiplegt_dyn on various designs/cleaned_data/deryugina_2017.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/Examples did_multiplegt_dyn on various designs/cleaned_data/deryugina_2017.dta -------------------------------------------------------------------------------- /Examples did_multiplegt_dyn on various designs/cleaned_data/east_et_al_2023.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/Examples did_multiplegt_dyn on various designs/cleaned_data/east_et_al_2023.dta -------------------------------------------------------------------------------- /Examples did_multiplegt_dyn on various designs/cleaned_data/gentzkowetal_didtextbook.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/Examples did_multiplegt_dyn on various designs/cleaned_data/gentzkowetal_didtextbook.dta -------------------------------------------------------------------------------- /Examples did_multiplegt_dyn on various designs/cleaned_data/hollingsworth_et_al_2022.dta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/HEAD/Examples did_multiplegt_dyn on various designs/cleaned_data/hollingsworth_et_al_2022.dta -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/Stata/stata.toc: -------------------------------------------------------------------------------- 1 | v 1.0.0 2 | d ChaisemartinPackages 3 | d https://github.com/chaisemartinPackages/did_multiplegt_dyn/Stata 4 | 5 | d 'did_multiplegt_dyn_all_pl': did_multiplegt_dyn extension to retrieve all feasible placebos. 6 | 7 | p did_multiplegt_dyn_all_pl 8 | -------------------------------------------------------------------------------- /R/README.md: -------------------------------------------------------------------------------- 1 | We have been making changes and correcting bugs to the did_multiplegt_dyn Stata command. We are in the process of incorporating those same changes into the R command, and are momentarily taking down that command from GitHub while we do so. We will put it back on GitHub as soon as those changes have been incorporated. Thank you for your patience. 2 | -------------------------------------------------------------------------------- /no_xlsx/README.md: -------------------------------------------------------------------------------- 1 | ## Info - 23 Apr 2024 2 | 3 | This version of did_multiplegt_dyn is based off the 1.0.8 version available on CRAN. 4 | This version addresses rJava issues in some Linux and MacOS terminals by shutting down the the excel print features built in the **design** and **date_first_switch** options. 5 | These features were based on the 'xlsx' library, which depends on rJava. 6 | The output of these two options can be still saved in xlsx format using other libraries and retrieving the tables from the assigned object. -------------------------------------------------------------------------------- /R/R/progressBar.R: -------------------------------------------------------------------------------- 1 | #' Internal function to generate a progress bar for the bootstrap option 2 | #' @param int integer 3 | #' @param tot tot 4 | #' @returns No returns, just prints dots for each bootstrap replication 5 | #' @noRd 6 | progressBar <- function( 7 | int, 8 | tot 9 | ) { 10 | cat(".") 11 | if (int %% 5 == 0) { 12 | cat(sprintf("%.0f", int)) 13 | } 14 | if (int %% 70 == 0) { 15 | cat("\n") 16 | } 17 | if (int == tot) { 18 | cat("\n") 19 | } 20 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/progressBar.R: -------------------------------------------------------------------------------- 1 | #' Internal function to generate a progress bar for the bootstrap option 2 | #' @param int integer 3 | #' @param tot tot 4 | #' @returns No returns, just prints dots for each bootstrap replication 5 | #' @noRd 6 | progressBar <- function( 7 | int, 8 | tot 9 | ) { 10 | cat(".") 11 | if (int %% 5 == 0) { 12 | cat(sprintf("%.0f", int)) 13 | } 14 | if (int %% 70 == 0) { 15 | cat("\n") 16 | } 17 | if (int == tot) { 18 | cat("\n") 19 | } 20 | } -------------------------------------------------------------------------------- /R/R/rnames.R: -------------------------------------------------------------------------------- 1 | #' @title rnames method for did_multiplegt_dyn 2 | #' @name rnames.did_multiplegt_dyn 3 | #' @description A customized rnames method for did_multiplegt_dyn output 4 | #' @param obj A did_multiplegt_dyn object 5 | #' @param ignore Sublists to be ignored 6 | #' @param ... Undocumented 7 | #' @import rnames 8 | #' @returns The same output as rnames. 9 | #' @export 10 | rnames.did_multiplegt_dyn <- function(obj, ignore = c("plot", "args"), ...) { 11 | class(obj) <- "list" 12 | return(rnames(obj = obj, ignore = c("plot", "args"))) 13 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/rnames.R: -------------------------------------------------------------------------------- 1 | #' @title rnames method for did_multiplegt_dyn 2 | #' @name rnames.did_multiplegt_dyn 3 | #' @description A customized rnames method for did_multiplegt_dyn output 4 | #' @param obj A did_multiplegt_dyn object 5 | #' @param ignore Sublists to be ignored 6 | #' @param ... Undocumented 7 | #' @import rnames 8 | #' @returns The same output as rnames. 9 | #' @export 10 | rnames.did_multiplegt_dyn <- function(obj, ignore = c("plot", "args"), ...) { 11 | class(obj) <- "list" 12 | return(rnames(obj = obj, ignore = c("plot", "args"))) 13 | } -------------------------------------------------------------------------------- /R/R/did_multiplegt_dyn_by_check.R: -------------------------------------------------------------------------------- 1 | #' Checks whether the variable specified in the by option is time-variant 2 | #' The program allows only time-variant variables in the by option 3 | #' @param df df 4 | #' @param group group 5 | #' @param by by 6 | #' @import data.table 7 | #' @returns A logical value. 8 | #' @noRd 9 | did_multiplegt_dyn_by_check <- function( 10 | df, 11 | group, 12 | by 13 | ) { 14 | sd_by <- NULL 15 | df <- data.table(df) 16 | df[, sd_by := sd(get(by), na.rm = TRUE), by = c(group)] 17 | return(mean(df$sd_by, na.rm = TRUE) == 0) 18 | } -------------------------------------------------------------------------------- /R/man/rnames.did_multiplegt_dyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rnames.R 3 | \name{rnames.did_multiplegt_dyn} 4 | \alias{rnames.did_multiplegt_dyn} 5 | \title{rnames method for did_multiplegt_dyn} 6 | \usage{ 7 | \method{rnames}{did_multiplegt_dyn}(obj, ignore = c("plot", "args"), ...) 8 | } 9 | \arguments{ 10 | \item{obj}{A did_multiplegt_dyn object} 11 | 12 | \item{ignore}{Sublists to be ignored} 13 | 14 | \item{...}{Undocumented} 15 | } 16 | \value{ 17 | The same output as rnames. 18 | } 19 | \description{ 20 | A customized rnames method for did_multiplegt_dyn output 21 | } 22 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_dyn_by_check.R: -------------------------------------------------------------------------------- 1 | #' Checks whether the variable specified in the by option is time-variant 2 | #' The program allows only time-variant variables in the by option 3 | #' @param df df 4 | #' @param group group 5 | #' @param by by 6 | #' @import dplyr 7 | #' @importFrom magrittr %>% 8 | #' @importFrom rlang .data 9 | #' @returns A logical value. 10 | #' @noRd 11 | did_multiplegt_dyn_by_check <- function( 12 | df, 13 | group, 14 | by 15 | ) { 16 | df <- df %>% group_by(.data[[group]]) %>% 17 | mutate(sd_by = sd(.data[[by]], na.rm = TRUE)) 18 | return(mean(df$sd_by) == 0) 19 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/man/rnames.did_multiplegt_dyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rnames.R 3 | \name{rnames.did_multiplegt_dyn} 4 | \alias{rnames.did_multiplegt_dyn} 5 | \title{rnames method for did_multiplegt_dyn} 6 | \usage{ 7 | \method{rnames}{did_multiplegt_dyn}(obj, ignore = c("plot", "args"), ...) 8 | } 9 | \arguments{ 10 | \item{obj}{A did_multiplegt_dyn object} 11 | 12 | \item{ignore}{Sublists to be ignored} 13 | 14 | \item{...}{Undocumented} 15 | } 16 | \value{ 17 | The same output as rnames. 18 | } 19 | \description{ 20 | A customized rnames method for did_multiplegt_dyn output 21 | } 22 | -------------------------------------------------------------------------------- /vignettes/src/vignette_3_R.R: -------------------------------------------------------------------------------- 1 | #install_github("chaisemartinPackages/did_multiplegt_dyn/R", force = TRUE) 2 | #install_github("chaisemartinPackages/did_multiplegt_dyn/did_multiplegt_dyn_all_pl/R", force = TRUE) 3 | 4 | library(devtools) 5 | 6 | library(haven) 7 | library(DIDmultiplegtDYN) 8 | library(DIDmultiplegtDYNallPL) 9 | data <- read_dta("https://raw.githubusercontent.com/chaisemartinPackages/did_multiplegt_dyn/main/vignettes/assets/vignette_3_data.dta") 10 | did <- did_multiplegt_dyn_all_pl( 11 | df = data, 12 | outcome = "Y", 13 | group = "G", 14 | time = "T", 15 | treatment = "D", 16 | effects = 1, 17 | placebo = 2 18 | ) 19 | print(did) 20 | -------------------------------------------------------------------------------- /R/man/print.did_multiplegt_dyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{print.did_multiplegt_dyn} 4 | \alias{print.did_multiplegt_dyn} 5 | \title{A print method for did_multiplegt_dyn} 6 | \usage{ 7 | \method{print}{did_multiplegt_dyn}(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A did_multiplegt_dyn object} 11 | 12 | \item{...}{Undocumented} 13 | } 14 | \value{ 15 | No return, custom print method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 16 | } 17 | \description{ 18 | A customized printed display for did_multiplegt_dyn output 19 | } 20 | -------------------------------------------------------------------------------- /Python/example.py: -------------------------------------------------------------------------------- 1 | import rpy2 2 | import rpy2.robjects as robjects 3 | from rpy2.robjects.packages import importr 4 | from rpy2.robjects import pandas2ri 5 | 6 | import wooldridge 7 | 8 | pandas2ri.activate() 9 | wagepan = pandas2ri.py2ri(wooldridge.data('wagepan')) 10 | #wagepan = pandas2ri.py2rpy(wooldridge.data('wagepan')) 11 | 12 | DIDmultiplegtDYN = importr('DIDmultiplegtDYN') 13 | did = DIDmultiplegtDYN.did_multiplegt_dyn(df = wagepan, outcome = 'lwage', group = 'nr', time = 'year', treatment = 'union', effects = 5.0, normalized = True, design = [1, "console"], controls = robjects.StrVector(["married", "hours"])) 14 | 15 | # Display the results 16 | print(did) 17 | 18 | # Retrieve the ATE 19 | print(did[1][3][0]) -------------------------------------------------------------------------------- /R/man/summary.did_multiplegt_dyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{summary.did_multiplegt_dyn} 4 | \alias{summary.did_multiplegt_dyn} 5 | \title{A summary method for did_multiplegt_dyn} 6 | \usage{ 7 | \method{summary}{did_multiplegt_dyn}(object, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A did_multiplegt_dyn object} 11 | 12 | \item{...}{Undocumented} 13 | } 14 | \value{ 15 | No return, custom summary method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 16 | } 17 | \description{ 18 | A customized summary display for did_multiplegt_dyn output 19 | } 20 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/man/print.did_multiplegt_dyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{print.did_multiplegt_dyn} 4 | \alias{print.did_multiplegt_dyn} 5 | \title{A print method for did_multiplegt_dyn} 6 | \usage{ 7 | \method{print}{did_multiplegt_dyn}(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A did_multiplegt_dyn object} 11 | 12 | \item{...}{Undocumented} 13 | } 14 | \value{ 15 | No return, custom print method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 16 | } 17 | \description{ 18 | A customized printed display for did_multiplegt_dyn output 19 | } 20 | -------------------------------------------------------------------------------- /R/R/favara_imbs-data.R: -------------------------------------------------------------------------------- 1 | #' Favara and Imbs (2015) 2 | #' 3 | #' Favara and Imbs (2015) use 1994-to-2005 4 | #' county-level data to estimate the effect 5 | #' of the number of regulations lifted on the 6 | #' growth of mortgages originated by banks, 7 | #' and on the growth of houses prices. Their 8 | #' findings are revisited in de Chaisemartin and D'Haultfoeuille (2024a). 9 | #' This dataset includes only the first 10 states. 10 | #' The full dataset is available on GitHub. 11 | #' 12 | #' @docType data 13 | #' 14 | #' @usage data(favara_imbs) 15 | #' 16 | #' @keywords datasets 17 | #' 18 | #' @references Favara and Imbs (2015) 19 | #' (\href{https://www.aeaweb.org/articles?id=10.1257/aer.20121416}{AER}) 20 | #' 21 | "favara_imbs" -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/R/print.did_multiplegt_dyn_all_pl.R: -------------------------------------------------------------------------------- 1 | #' Custom print method for did_multiplegt_dyn_all_pl objects 2 | #' @name print.did_multiplegt_dyn_all_pl 3 | #' @description A customized printed display for did_multiplegt_dyn_all_pl output 4 | #' @param x A did_multiplegt_dyn_all_pl object 5 | #' @param ... Undocumented 6 | #' @returns No return, custom print method for did_multiplegt_dyn_all_pl objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn_all_pl command. 7 | #' @export 8 | print.did_multiplegt_dyn_all_pl <- function(x, ...) { 9 | class(x) <- "did_multiplegt_dyn" 10 | print(x) 11 | cat("\nMod: Two-stage estimation of placebos.\n") 12 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/man/summary.did_multiplegt_dyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{summary.did_multiplegt_dyn} 4 | \alias{summary.did_multiplegt_dyn} 5 | \title{A summary method for did_multiplegt_dyn} 6 | \usage{ 7 | \method{summary}{did_multiplegt_dyn}(object, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A did_multiplegt_dyn object} 11 | 12 | \item{...}{Undocumented} 13 | } 14 | \value{ 15 | No return, custom summary method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 16 | } 17 | \description{ 18 | A customized summary display for did_multiplegt_dyn output 19 | } 20 | -------------------------------------------------------------------------------- /vignettes/src/vignette_3_Stata.do: -------------------------------------------------------------------------------- 1 | clear 2 | set seed 123 3 | set obs 100 4 | gen G = mod(_n-1, 25) + 1 5 | bys G: gen T = _n 6 | sum T 7 | gen D = uniform() > 0.5 & T == r(max) 8 | gen Y = uniform() * (1 + D) 9 | 10 | qui do "C:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/Stata/did_multiplegt_dyn.ado" 11 | qui do "C:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/Stata/did_multiplegt_dyn_all_pl.ado" 12 | 13 | est clear 14 | did_multiplegt_dyn Y G T D, effects(1) placebo(1) graph_off 15 | eststo 16 | did_multiplegt_dyn_all_pl Y G T D, effects(1) placebo(2) 17 | eststo 18 | 19 | esttab, order(Effect_* Placebo_* Av_*) se html -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/man/print.did_multiplegt_dyn_all_pl.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.did_multiplegt_dyn_all_pl.R 3 | \name{print.did_multiplegt_dyn_all_pl} 4 | \alias{print.did_multiplegt_dyn_all_pl} 5 | \title{Custom print method for did_multiplegt_dyn_all_pl objects} 6 | \usage{ 7 | \method{print}{did_multiplegt_dyn_all_pl}(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A did_multiplegt_dyn_all_pl object} 11 | 12 | \item{...}{Undocumented} 13 | } 14 | \value{ 15 | No return, custom print method for did_multiplegt_dyn_all_pl objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn_all_pl command. 16 | } 17 | \description{ 18 | A customized printed display for did_multiplegt_dyn_all_pl output 19 | } 20 | -------------------------------------------------------------------------------- /Examples/Examples_Stata.do: -------------------------------------------------------------------------------- 1 | //ssc install did_multiplegt_dyn 2 | 3 | 4 | qui bcuse wagepan, clear 5 | sort nr year 6 | gen clus = mod(nr, 3) 7 | gen over_gr = mod(nr, 2) 8 | 9 | did_multiplegt_dyn lwage nr year union, effects(5) graph_off placebo(2) controls(married exper) predict_het(clus, ) switchers(in) 10 | 11 | did_multiplegt_dyn lwage nr year union, effects(5) graph_off placebo(2) trends_nonparam(over_gr) weight(hours) dont_drop_larger_lower 12 | 13 | did_multiplegt_dyn lwage nr year union, effects(5) graph_off placebo(2) normalized drop_if_d_miss_before_first_switch normalized_weights cluster(clus) 14 | 15 | did_multiplegt_dyn lwage nr year union, effects(5) graph_off placebo(2) continuous(pol, 1) controls(hours) trends_lin 16 | 17 | did_multiplegt_dyn lwage nr year union, effects(5) effects_equal placebo(1) graph_off design(1, console) date_first_switch(by_baseline_treat, console) same_switchers same_switchers_pl -------------------------------------------------------------------------------- /R/man/favara_imbs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/favara_imbs-data.R 3 | \docType{data} 4 | \name{favara_imbs} 5 | \alias{favara_imbs} 6 | \title{Favara and Imbs (2015)} 7 | \format{ 8 | An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 1157 rows and 7 columns. 9 | } 10 | \usage{ 11 | data(favara_imbs) 12 | } 13 | \description{ 14 | Favara and Imbs (2015) use 1994-to-2005 15 | county-level data to estimate the effect 16 | of the number of regulations lifted on the 17 | growth of mortgages originated by banks, 18 | and on the growth of houses prices. Their 19 | findings are revisited in de Chaisemartin and D'Haultfoeuille (2024a). 20 | This dataset includes only the first 10 states. 21 | The full dataset is available on GitHub. 22 | } 23 | \references{ 24 | Favara and Imbs (2015) 25 | (\href{https://www.aeaweb.org/articles?id=10.1257/aer.20121416}{AER}) 26 | } 27 | \keyword{datasets} 28 | -------------------------------------------------------------------------------- /R/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,did_multiplegt_dyn) 4 | S3method(rnames,did_multiplegt_dyn) 5 | S3method(summary,did_multiplegt_dyn) 6 | export(did_multiplegt_dyn) 7 | import(cowplot) 8 | import(data.table) 9 | import(ggplot2) 10 | import(lmtest) 11 | import(rnames) 12 | import(sandwich) 13 | importFrom(car,linearHypothesis) 14 | importFrom(dplyr,filter) 15 | importFrom(dplyr,lag) 16 | importFrom(dplyr,n_distinct) 17 | importFrom(grDevices,colors) 18 | importFrom(haven,read_dta) 19 | importFrom(matlib,Ginv) 20 | importFrom(openxlsx,write.xlsx) 21 | importFrom(plm,make.pbalanced) 22 | importFrom(plm,pdata.frame) 23 | importFrom(stats,as.formula) 24 | importFrom(stats,df.residual) 25 | importFrom(stats,lm) 26 | importFrom(stats,nobs) 27 | importFrom(stats,pchisq) 28 | importFrom(stats,qnorm) 29 | importFrom(stats,qt) 30 | importFrom(stats,relevel) 31 | importFrom(stats,sd) 32 | importFrom(stats,weighted.mean) 33 | importFrom(utils,write.csv) 34 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,did_multiplegt_dyn) 4 | S3method(rnames,did_multiplegt_dyn) 5 | S3method(summary,did_multiplegt_dyn) 6 | export(did_multiplegt_dyn) 7 | import(cowplot) 8 | import(dplyr) 9 | import(ggplot2) 10 | import(lmtest) 11 | import(rnames) 12 | import(sandwich) 13 | importFrom(car,linearHypothesis) 14 | importFrom(data.table,setnames) 15 | importFrom(data.table,shift) 16 | importFrom(grDevices,colors) 17 | importFrom(haven,read_dta) 18 | importFrom(magrittr,"%>%") 19 | importFrom(matlib,Ginv) 20 | importFrom(plm,make.pbalanced) 21 | importFrom(plm,pdata.frame) 22 | importFrom(rlang,":=") 23 | importFrom(rlang,.data) 24 | importFrom(stats,as.formula) 25 | importFrom(stats,df.residual) 26 | importFrom(stats,lm) 27 | importFrom(stats,nobs) 28 | importFrom(stats,pchisq) 29 | importFrom(stats,qnorm) 30 | importFrom(stats,qt) 31 | importFrom(stats,relevel) 32 | importFrom(stats,sd) 33 | importFrom(stats,weighted.mean) 34 | importFrom(utils,write.csv) 35 | -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: DIDmultiplegtDYNallPL 2 | Title: Retrieving more placebos from DIDmultiplegtDYN 3 | Version: 1.0.1 4 | Authors@R: 5 | c(person("Diego", "Ciccia", role = c("aut", "cre"), email = "diego.ciccia@sciencespo.fr"), 6 | person("Felix", "Knau", role = "aut", email = "felix.knau@sciencespo.fr"), 7 | person("Mélitine", "Malezieux", role = "aut", email = "melitine.malezieux@sciencespo.fr"), 8 | person("Doulo", "Sow", role = "aut", email = "doulo.sow@ensae.fr"), 9 | person("Clément", "de Chaisemartin", role = "aut", email = "clement.dechaisemartin@sciencespo.fr")) 10 | Maintainer: Diego Ciccia 11 | Description: Auxiliary package to overcome DIDmultiplegtDYN restrictions about placebo estimation. 12 | License: MIT + file LICENSE 13 | Author: Diego Ciccia [aut, cre], 14 | Felix Knau [aut], 15 | Doulo Sow [aut], 16 | Clément de Chaisemartin [aut] 17 | Encoding: UTF-8 18 | Imports: DIDmultiplegtDYN (>= 1.0.12), dplyr 19 | Roxygen: list(markdown = TRUE) 20 | RoxygenNote: 7.2.3 21 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/NEWS.md: -------------------------------------------------------------------------------- 1 | # DIDmultiplegtDYN 2 | 3 | ## Changes in version 1.0.0 4 | 5 | First official R release. 6 | 7 | ## Changes in version 1.0.1 8 | 9 | + Stronger syntax checks. 10 | 11 | + Fixed minor bugs related to predict_het and same_switchers_pl options. 12 | 13 | ## Changes in version 1.0.2 14 | 15 | + Added returns descriptions to Rd files. 16 | 17 | ## Changes in version 1.0.3 18 | 19 | + Minor changes to in-console printing statements. 20 | 21 | ## Changes in version 1.0.4 22 | 23 | + Fixed bugs for unbalanced panels. 24 | 25 | ## Changes in version 1.0.5 26 | 27 | + Added option to compute bootstrap standard errors 28 | 29 | + Fixed minor bugs 30 | 31 | ## Changes in version 1.0.6 32 | 33 | + Added by_path option. 34 | 35 | ## Changes in version 1.0.7 36 | 37 | + Added average number of cumulative effects 38 | 39 | + Added compatibility with rnames to enhance results browsing experience 40 | 41 | ## Changes in version 1.0.8 42 | 43 | + Changed display with weight option 44 | 45 | + Integration with honestdid via the "coef" output in the assigned object 46 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/joint_trends.R: -------------------------------------------------------------------------------- 1 | #' Internal function of did_multiplegt_dyn that adds to general purpose conditioning (dplyr) vector-wise conditioning (a necessary feature for treends_nonparam) 2 | #' When we use group_by(...) in piped statements, the argument of the option is a set of vars in .data. The trends_nonparam option is specified as an atomic char or a vector of chars. The need for this function arises in this second case. As it is not uncommon that we need to group by trends_nonparam and other variables, this function handles this aggregation, yielding the same df with a new variable that groups any set of variables with the trends_nonparam argument. 3 | #' @param df df 4 | #' @param var var 5 | #' @param trends_nonparam trends_nonparam 6 | #' @import dplyr 7 | #' @importFrom magrittr %>% 8 | #' @returns A dataframe. 9 | #' @noRd 10 | joint_trends <- function( 11 | df, 12 | var, 13 | trends_nonparam 14 | ) { 15 | df <- df %>% dplyr::select(-any_of(c("joint_trends_XX"))) 16 | joint_vars <- c(var, trends_nonparam) 17 | df$joint_trends_XX <- df[joint_vars] 18 | df 19 | } -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [main, master] 4 | pull_request: 5 | branches: [main, master] 6 | 7 | name: R-CMD-check 8 | 9 | jobs: 10 | R-CMD-check: 11 | runs-on: ${{ matrix.config.os }} 12 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | config: 18 | - {os: macos-latest, r: 'release'} 19 | - {os: ubuntu-latest, r: 'release'} 20 | - {os: windows-latest, r: 'release'} 21 | env: 22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 23 | R_KEEP_PKG_SOURCE: yes 24 | RGL_USE_NULL: true 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | with: 31 | r-version: ${{ matrix.config.r }} 32 | use-public-rspm: true 33 | 34 | - uses: r-lib/actions/setup-r-dependencies@v2 35 | with: 36 | extra-packages: any::rcmdcheck 37 | needs: check 38 | working-directory: ./R 39 | 40 | - uses: r-lib/actions/check-r-package@v2 41 | with: 42 | working-directory: ./R 43 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: DIDmultiplegtDYN 2 | Title: Estimation in Difference-in-Difference Designs with Multiple Groups and Periods 3 | Version: 1.0.8 4 | Authors@R: 5 | c(person("Diego", "Ciccia", role = c("aut", "cre"), email = "diego.ciccia@sciencespo.fr"), 6 | person("Felix", "Knau", role = "aut", email = "felix.knau@sciencespo.fr"), 7 | person("Mélitine", "Malezieux", role = "aut", email = "melitine.malezieux@sciencespo.fr"), 8 | person("Doulo", "Sow", role = "aut", email = "doulo.sow@ensae.fr"), 9 | person("Clément", "de Chaisemartin", role = "aut", email = "clement.dechaisemartin@sciencespo.fr")) 10 | Maintainer: Diego Ciccia 11 | Description: Estimation of event-study Difference-in-Difference (DID) estimators in designs with multiple groups and periods, and with a potentially non-binary treatment that may increase or decrease multiple times. 12 | License: MIT + file LICENSE 13 | Author: Diego Ciccia [aut, cre], 14 | Felix Knau [aut], 15 | Mélitine Malezieux [aut], 16 | Doulo Sow [aut], 17 | Clément de Chaisemartin [aut] 18 | Encoding: UTF-8 19 | Imports: data.table, dplyr, ggplot2, magrittr, matlib, plm, rlang, stats, car, lmtest, sandwich, haven, cowplot, rnames 20 | Roxygen: list(markdown = TRUE) 21 | RoxygenNote: 7.2.3 22 | -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/R/add_periods.R: -------------------------------------------------------------------------------- 1 | #' Inner program to fill panel structure and retrieve placebos 2 | #' @param df df 3 | #' @param outcome outcome 4 | #' @param group group 5 | #' @param time time 6 | #' @param treatment treatment 7 | #' @param add add 8 | #' @import dplyr 9 | #' @noRd 10 | add_periods <- function( 11 | df, 12 | outcome, 13 | group, 14 | time, 15 | treatment, 16 | add 17 | ) { 18 | 19 | df <- df[order(df[[group]], df[[time]]), ] 20 | df <- df %>% group_by(.data[[group]]) %>% mutate(G_AP = cur_group_id()) 21 | df <- df %>% group_by(.data[[time]]) %>% mutate(T_AP = cur_group_id()) 22 | max_T <- max(df$T_AP, na.rm = T) 23 | max_G <- max(df$G_AP, na.rm = T) 24 | 25 | df <- df [order(df$T_AP, df$G_AP), ] 26 | for (j in 1:max_G) { 27 | N_temp <- nrow(df) 28 | to_add <- as.data.frame(matrix(NA,nrow=add,ncol=ncol(df))) 29 | colnames(to_add) <- colnames(df) 30 | df <- rbind(df,to_add) 31 | df[[outcome]][is.na(df[[outcome]])] <- 0 32 | df[[group]][is.na(df[[group]])] <- j 33 | df[[time]][is.na(df[[time]])] <- max_T + (1:add) 34 | df[[treatment]][is.na(df[[treatment]])] <- df[[treatment]][j] 35 | } 36 | df$T_AP <- df$G_AP <- NULL 37 | df <- df[order(df[[group]], df[[time]]), ] 38 | return(df) 39 | } -------------------------------------------------------------------------------- /R/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: DIDmultiplegtDYN 2 | Title: Estimation in Difference-in-Difference Designs with Multiple Groups and Periods 3 | Version: 2.1.2 4 | Authors@R: 5 | c(person("Diego", "Ciccia", role = c("aut", "cre"), email = "diego.ciccia@kellogg.northwestern.edu"), 6 | person("Felix", "Knau", role = "aut", email = "felix.knau@sciencespo.fr"), 7 | person("Mélitine", "Malezieux", role = "aut", email = "melitine.malezieux@sciencespo.fr"), 8 | person("Doulo", "Sow", role = "aut", email = "doulo.sow@ensae.fr"), 9 | person("Clément", "de Chaisemartin", role = "aut", email = "clement.dechaisemartin@sciencespo.fr")) 10 | Maintainer: Diego Ciccia 11 | Description: Estimation of heterogeneity-robust difference-in-differences estimators, with a binary, discrete, or continuous treatment, in designs where past treatments may affect the current outcome. 12 | License: MIT + file LICENSE 13 | LazyData: true 14 | URL: https://github.com/chaisemartinPackages/did_multiplegt_dyn 15 | Author: Diego Ciccia [aut, cre], 16 | Felix Knau [aut], 17 | Mélitine Malezieux [aut], 18 | Doulo Sow [aut], 19 | Clément de Chaisemartin [aut] 20 | Encoding: UTF-8 21 | Imports: data.table, dplyr, ggplot2, matlib, plm, openxlsx, stats, car, lmtest, sandwich, haven, cowplot, rnames 22 | Roxygen: list(markdown = TRUE) 23 | RoxygenNote: 7.3.2 24 | -------------------------------------------------------------------------------- /Examples/Examples_R.R: -------------------------------------------------------------------------------- 1 | library(DIDmultiplegtDYN) 2 | library(wooldridge) 3 | 4 | data("wagepan") 5 | wagepan$over_gr <- as.numeric(wagepan$nr %% 2) 6 | wagepan$clus <- as.numeric(wagepan$nr %% 3) 7 | 8 | # Some brute force consistency checks for the package # 9 | summary(did_multiplegt_dyn(df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", effects = 5, placebo = 2, graph_off = TRUE, controls = c("married", "exper"), predict_het = list("clus",-1), switchers = "in")) 10 | 11 | summary(did_multiplegt_dyn(df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", effects = 5, placebo = 2, graph_off = TRUE, trends_nonparam = "over_gr", weight = "hours", dont_drop_larger_lower = TRUE)) 12 | 13 | summary(did_multiplegt_dyn(df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", effects = 5, placebo = 2, graph_off = TRUE, normalized = TRUE, normalized_weights = TRUE, drop_if_d_miss_before_first_switch = TRUE, cluster = "clus")) 14 | 15 | summary(did_multiplegt_dyn(df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", effects = 5, placebo = 2, graph_off = TRUE, continuous = 1, trends_lin = TRUE, controls = "hours")) 16 | 17 | summary(did_multiplegt_dyn(df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", effects = 5, graph_off = TRUE, placebo = 2, design = list(1, "console"), date_first_switch = list("by_baseline_treat", "console"), same_switchers = TRUE, same_switchers_pl = TRUE, effects_equal = TRUE)) -------------------------------------------------------------------------------- /Stata/prior_versions/submission ssc 2024-02-16/bootstrap did_multiplegt_dyn.do: -------------------------------------------------------------------------------- 1 | //Install command and load example data 2 | net get did_multiplegt_dyn 3 | use favara_imbs_did_multiplegt_dyn.dta, clear 4 | 5 | *** Wagepan to test for now 6 | use "C:\Users\fe-kn\C DE CHAISEMARTIN Dropbox\RAs De Chaisemartin\RAs Really Credible DID-TWFE\did_multiplegt\test_dofiles_and_datasets\WAGEPAN.DTA", clear 7 | 8 | // do "did_multiplegt_dyn.do" 9 | // Initialize number of bootsrap repetitions (adapt to your desired number) 10 | local b_reps=10 11 | local n_effects=5 12 | local n_placebos=2 13 | 14 | //qui levelsof county 15 | qui levelsof nr 16 | local lvl_g=r(r) 17 | di `lvl_g' 18 | 19 | //Store the coefficients to bootstrap 20 | local coefs " " 21 | forvalues i=1/`n_effects'{ 22 | local coefs = "`coefs' e(Effect_`i')" 23 | } 24 | forvalues i=1/`n_placebos'{ 25 | local coefs = "`coefs' e(Placebo_`i')" 26 | } 27 | //Run the bootstrap replications (adapt to your specification) 28 | set seed 1234 29 | //bootstrap "`coefs'", reps(`b_reps') cluster(county) size(`lvl_g'): did_multiplegt_dyn Dl_vloans_b county year inter_bra, effects(`n_effects') placebo(`n_placebos') continuous(1) graph_off 30 | bootstrap "`coefs'", reps(`b_reps') cluster(nr) size(`lvl_g'): did_multiplegt_dyn lwage nr year union, effects(`n_effects') placebo(`n_placebos') continuous(1) graph_off //by(black) 31 | 32 | // Note: The treatment in this example dataset is not continuous, therefore you get a warning message when running this example. -------------------------------------------------------------------------------- /R/NEWS.md: -------------------------------------------------------------------------------- 1 | # DIDmultiplegtDYN 2 | 3 | ## Changes in version 1.0.0 4 | 5 | First official R release. 6 | 7 | ## Changes in version 1.0.1 8 | 9 | + Stronger syntax checks. 10 | 11 | + Fixed minor bugs related to predict_het and same_switchers_pl options. 12 | 13 | ## Changes in version 1.0.2 14 | 15 | + Added returns descriptions to Rd files. 16 | 17 | ## Changes in version 1.0.3 18 | 19 | + Minor changes to in-console printing statements. 20 | 21 | ## Changes in version 1.0.4 22 | 23 | + Fixed bugs for unbalanced panels. 24 | 25 | ## Changes in version 1.0.5 26 | 27 | + Added option to compute bootstrap standard errors 28 | 29 | + Fixed minor bugs 30 | 31 | ## Changes in version 1.0.6 32 | 33 | + Added by_path option. 34 | 35 | ## Changes in version 1.0.7 36 | 37 | + Added average number of cumulative effects 38 | 39 | + Added compatibility with rnames to enhance results browsing experience 40 | 41 | ## Changes in version 1.0.8 42 | 43 | + Changed display with weight option 44 | 45 | + Integration with honestdid via the "coef" output in the assigned object 46 | 47 | ## Changes in version 1.0.9 48 | 49 | + Added only_never_switchers option 50 | 51 | ## Changes in version 1.0.14 52 | 53 | + Added joint test on dynamic effects 54 | 55 | ## Changes in version 2.0.0 56 | 57 | + Switched from dplyr to data.table (way faster by group operations) 58 | 59 | ## Changes in version 2.1.0 60 | 61 | + Switched from xlsx (rJava dependent) to openxlsx (not rJava dependent) 62 | 63 | ## Changes in version 2.1.1 64 | 65 | + New helpfile 66 | 67 | ## Changes in version 2.1.2 68 | 69 | + New graph option and preset 70 | -------------------------------------------------------------------------------- /vignettes/assets/reg2.tex: -------------------------------------------------------------------------------- 1 | % 13 May 2024 15:43:22 2 | \documentclass[varwidth]{standalone} 3 | \usepackage{booktabs} 4 | \begin{document} 5 | 6 | { 7 | \def\sym#1{\ifmmode^{#1}\else\(^{#1}\)\fi} 8 | \begin{tabular}{l*{1}{c}} 9 | \toprule 10 | &\multicolumn{1}{c}{(1)}\\ 11 | &\multicolumn{1}{c}{} \\ 12 | \midrule 13 | Y & \\ 14 | Effect\_1 & 0.5269\sym{***}\\ 15 | & (0.0491) \\ 16 | \addlinespace 17 | Effect\_2 & 0.3303\sym{***}\\ 18 | & (0.0670) \\ 19 | \addlinespace 20 | Effect\_3 & 0.2992\sym{**} \\ 21 | & (0.0995) \\ 22 | \addlinespace 23 | Avg\_Tot\_Effect& 0.5779\sym{***}\\ 24 | & (0.0901) \\ 25 | \midrule 26 | Effect\_1 & \\ 27 | H1 & -0.0345 \\ 28 | & (0.1194) \\ 29 | \addlinespace 30 | H2 & 0.0025 \\ 31 | & (0.0063) \\ 32 | \midrule 33 | Effect\_2 & \\ 34 | H1 & -0.1328 \\ 35 | & (0.1406) \\ 36 | \addlinespace 37 | H2 & 0.0067 \\ 38 | & (0.0070) \\ 39 | \midrule 40 | Effect\_3 & \\ 41 | H1 & -0.1417 \\ 42 | & (0.1599) \\ 43 | \addlinespace 44 | H2 & -0.0014 \\ 45 | & (0.0082) \\ 46 | \bottomrule 47 | \multicolumn{2}{l}{\footnotesize Standard errors in parentheses}\\ 48 | \multicolumn{2}{l}{\footnotesize \sym{*} \(p<0.05\), \sym{**} \(p<0.01\), \sym{***} \(p<0.001\)}\\ 49 | \end{tabular} 50 | } 51 | 52 | \end{document} 53 | 54 | -------------------------------------------------------------------------------- /vignettes/src/vignette_2_Stata.do: -------------------------------------------------------------------------------- 1 | clear 2 | global dir0 : pwd 3 | global dir1 = subinstr("$dir0", "\vignettes\src", "", .) 4 | 5 | qui do "$dir1/did_multiplegt_dyn_13_05_24.do" 6 | clear 7 | set seed 123 8 | scalar TT = 20 9 | scalar GG = 1000 10 | set obs `= TT * GG' 11 | gen G = mod(_n-1,GG) + 1 12 | gen T = floor((_n-1)/GG) 13 | sort G T 14 | 15 | gen D = uniform() > 0.5 16 | gen X = uniform() * T 17 | gen Y = uniform() * (1 + D + X) 18 | 19 | est clear 20 | did_multiplegt_dyn Y G T D, effects(5) graph_off effects_equal 21 | estadd scalar p_joint = e(p_equality_effects) 22 | estadd local controls = "No" 23 | est sto model_1 24 | 25 | did_multiplegt_dyn Y G T D, effects(5) placebo(3) effects_equal controls(X) graph_off 26 | estadd scalar p_joint = e(p_equality_effects) 27 | estadd scalar p_placebo = e(p_jointplacebo) 28 | estadd local controls = "Yes" 29 | est sto model_2 30 | 31 | // Basic usage 32 | esttab model_* using "$dir1/vignettes/assets/reg1.tex", replace booktabs se s(p_joint p_placebo controls) standalone 33 | 34 | // Refined format 35 | esttab model_* using "$dir1/vignettes/assets/reg1a.tex", replace booktabs se s(control p_joint p_placebo, label("Controls" "Joint Eq. Effects" "Joint Sig. Placebo")) b(%9.5fc) coeflabels(Effect_1 "$\hat{\delta}_1$" Effect_2 "$\hat{\delta}_2$" Effect_3 "$\hat{\delta}_3$" Effect_4 "$\hat{\delta}_4$" Effect_5 "$\hat{\delta}_5$" Avg_Tot_Effect "$\hat{\delta}$" Placebo_1 "$\hat{\delta}_1^{pl}$" Placebo_2 "$\hat{\delta}_2^{pl}$" Placebo_3 "$\hat{\delta}_3^{pl}$") substitute(\_ _) mlabels(,none) collabels(,none) standalone 36 | 37 | est clear 38 | 39 | gen H1 = G/GG 40 | gen H2 = mod(G, TT) 41 | 42 | // Predict Het 43 | did_multiplegt_dyn Y G T D, predict_het(H1 H2, all) graph_off effects(3) 44 | est sto model_1 45 | 46 | esttab model_* using "$dir1/vignettes/assets/reg2.tex", replace booktabs se noobs standalone -------------------------------------------------------------------------------- /R/R/did_save_sample.R: -------------------------------------------------------------------------------- 1 | #' Option that allows to see which groups were included in the estimation and if they were switcher in/out ot control 2 | #' @param data data 3 | #' @param Gn Gn 4 | #' @param Tn Tn 5 | #' @import data.table 6 | #' @returns The input dataframe df plus two added columns. 7 | #' @noRd 8 | did_save_sample <- function( 9 | data, 10 | Gn, 11 | Tn 12 | ) { 13 | df <- data$df 14 | suppressWarnings({ 15 | ## keeping only group, time and switcher status (if not missing) 16 | df_save <- subset(df, !is.na(df$group) & !is.na(df$time)) 17 | df_save <- subset(df_save, select = c("group", "time", "S_g_XX", "switchers_tag_XX")) 18 | ## redefine S_g_XX to show if group is switcher in/out or control 19 | df_save <- data.table::setnames(df_save, old = c("group", "time", "S_g_XX", "switchers_tag_XX"), new = c(Gn, Tn, "did_sample", "did_effect")) 20 | df_save$did_sample <- ifelse(df_save$did_sample == 0, -1, df_save$did_sample) 21 | df_save$did_sample <- ifelse(is.na(df_save$did_sample), 0, df_save$did_sample) 22 | df_save$did_sample <- factor(df_save$did_sample, levels = c(0,1,-1), labels = c("Control", "Switcher-in", "Switcher-out")) 23 | }) 24 | # Return dataset to be merged to main data 25 | return(df_save) 26 | } 27 | 28 | #' Adjustment of save_sample output in case of by option 29 | #' @param obj A did_multiplegt_dyn object 30 | #' @returns The input dataframe df plus two added columns. 31 | #' @noRd 32 | adj_save_sample <- function(obj) { 33 | saved_sample <- obj$by_level_1$save_sample 34 | obj$by_level_1$save_sample <- NULL 35 | if (length(obj$by_levels) > 1) { 36 | for (j in 2:length(obj$by_levels)) { 37 | saved_sample <- rbind(saved_sample, obj[[paste0("by_level_", j)]]$save_sample) 38 | obj[[paste0("by_level_", j)]]$save_sample <- NULL 39 | } 40 | } 41 | saved_sample <- saved_sample[order(saved_sample[[obj$args$group]], saved_sample[[obj$args$time]]), , drop = FALSE] 42 | rownames(saved_sample) <- NULL 43 | obj <- append(obj, list(saved_sample)) 44 | return(obj) 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /vignettes/assets/reg1.tex: -------------------------------------------------------------------------------- 1 | % 13 May 2024 15:43:21 2 | \documentclass[varwidth]{standalone} 3 | \usepackage{booktabs} 4 | \begin{document} 5 | 6 | { 7 | \def\sym#1{\ifmmode^{#1}\else\(^{#1}\)\fi} 8 | \begin{tabular}{l*{2}{c}} 9 | \toprule 10 | &\multicolumn{1}{c}{(1)}&\multicolumn{1}{c}{(2)}\\ 11 | &\multicolumn{1}{c}{} &\multicolumn{1}{c}{} \\ 12 | \midrule 13 | Effect\_1 & 0.527\sym{***}& 0.517\sym{***}\\ 14 | & (0.0491) & (0.0436) \\ 15 | \addlinespace 16 | Effect\_2 & 0.330\sym{***}& 0.300\sym{***}\\ 17 | & (0.0670) & (0.0610) \\ 18 | \addlinespace 19 | Effect\_3 & 0.299\sym{**} & 0.314\sym{***}\\ 20 | & (0.0995) & (0.0830) \\ 21 | \addlinespace 22 | Effect\_4 & 0.150 & 0.272\sym{*} \\ 23 | & (0.135) & (0.131) \\ 24 | \addlinespace 25 | Effect\_5 & -0.0155 & 0.0508 \\ 26 | & (0.205) & (0.187) \\ 27 | \addlinespace 28 | Avg\_Tot\_Effect& 0.436\sym{**} & 0.489\sym{***}\\ 29 | & (0.144) & (0.131) \\ 30 | \addlinespace 31 | Placebo\_1 & & 0.0703 \\ 32 | & & (0.0640) \\ 33 | \addlinespace 34 | Placebo\_2 & & 0.122 \\ 35 | & & (0.124) \\ 36 | \addlinespace 37 | Placebo\_3 & & -0.0340 \\ 38 | & & (0.256) \\ 39 | \midrule 40 | p\_joint & 0.00175 & 0.000370 \\ 41 | p\_placebo & & 0.488 \\ 42 | controls & No & Yes \\ 43 | \bottomrule 44 | \multicolumn{3}{l}{\footnotesize Standard errors in parentheses}\\ 45 | \multicolumn{3}{l}{\footnotesize \sym{*} \(p<0.05\), \sym{**} \(p<0.01\), \sym{***} \(p<0.001\)}\\ 46 | \end{tabular} 47 | } 48 | 49 | \end{document} 50 | 51 | -------------------------------------------------------------------------------- /vignettes/assets/reg1a.tex: -------------------------------------------------------------------------------- 1 | % 13 May 2024 15:43:21 2 | \documentclass[varwidth]{standalone} 3 | \usepackage{booktabs} 4 | \begin{document} 5 | 6 | { 7 | \def\sym#1{\ifmmode^{#1}\else\(^{#1}\)\fi} 8 | \begin{tabular}{l*{2}{c}} 9 | \toprule 10 | &\multicolumn{1}{c}{(1)} &\multicolumn{1}{c}{(2)} \\ 11 | \midrule 12 | $\hat{\delta}_1$& 0.52691\sym{***}& 0.51666\sym{***}\\ 13 | & (0.04906) & (0.04361) \\ 14 | \addlinespace 15 | $\hat{\delta}_2$& 0.33026\sym{***}& 0.30043\sym{***}\\ 16 | & (0.06695) & (0.06097) \\ 17 | \addlinespace 18 | $\hat{\delta}_3$& 0.29919\sym{**} & 0.31371\sym{***}\\ 19 | & (0.09947) & (0.08300) \\ 20 | \addlinespace 21 | $\hat{\delta}_4$& 0.14995 & 0.27208\sym{*} \\ 22 | & (0.13541) & (0.13073) \\ 23 | \addlinespace 24 | $\hat{\delta}_5$& -0.01549 & 0.05075 \\ 25 | & (0.20492) & (0.18652) \\ 26 | \addlinespace 27 | $\hat{\delta}$& 0.43566\sym{**} & 0.48863\sym{***}\\ 28 | & (0.14371) & (0.13127) \\ 29 | \addlinespace 30 | $\hat{\delta}_1^{pl}$& & 0.07033 \\ 31 | & & (0.06400) \\ 32 | \addlinespace 33 | $\hat{\delta}_2^{pl}$& & 0.12237 \\ 34 | & & (0.12428) \\ 35 | \addlinespace 36 | $\hat{\delta}_3^{pl}$& & -0.03396 \\ 37 | & & (0.25635) \\ 38 | \midrule 39 | Controls & & \\ 40 | Joint Eq. Effects& 0.00175 & 0.00037 \\ 41 | Joint Sig. Placebo& & 0.48840 \\ 42 | \bottomrule 43 | \multicolumn{3}{l}{\footnotesize Standard errors in parentheses}\\ 44 | \multicolumn{3}{l}{\footnotesize \sym{*} \(p<0.05\), \sym{**} \(p<0.01\), \sym{***} \(p<0.001\)}\\ 45 | \end{tabular} 46 | } 47 | 48 | \end{document} 49 | 50 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_save_sample.R: -------------------------------------------------------------------------------- 1 | #' Option that allows to see which groups were included in the estimation and if they were switcher in/out ot control 2 | #' @param data data 3 | #' @param Gn Gn 4 | #' @param Tn Tn 5 | #' @import dplyr 6 | #' @importFrom magrittr %>% 7 | #' @importFrom rlang .data 8 | #' @importFrom data.table setnames 9 | #' @returns The input dataframe df plus two added columns. 10 | #' @noRd 11 | did_save_sample <- function( 12 | data, 13 | Gn, 14 | Tn 15 | ) { 16 | df <- data$df 17 | suppressWarnings({ 18 | ## keeping only group, time and switcher status (if not missing) 19 | df_save <- subset(df, !is.na(df$group) & !is.na(df$time)) 20 | df_save <- df_save %>% dplyr::select(.data$group, .data$time, .data$S_g_XX, .data$switchers_tag_XX) %>% ungroup() 21 | ## redefine S_g_XX to show if group is switcher in/out or control 22 | df_save <- data.table::setnames(df_save, old = c("group", "time", "S_g_XX", "switchers_tag_XX"), new = c(Gn, Tn, "did_sample", "did_effect")) 23 | df_save$did_sample <- ifelse(df_save$did_sample == 0, -1, df_save$did_sample) 24 | df_save$did_sample <- ifelse(is.na(df_save$did_sample), 0, df_save$did_sample) 25 | df_save$did_sample <- factor(df_save$did_sample, levels = c(0,1,-1), labels = c("Control", "Switcher-in", "Switchers-out")) 26 | }) 27 | # Return dataset to be merged to main data 28 | return(df_save) 29 | } 30 | 31 | #' Adjustment of save_sample output in case of by option 32 | #' @param obj A did_multiplegt_dyn object 33 | #' @returns The input dataframe df plus two added columns. 34 | #' @noRd 35 | adj_save_sample <- function(obj) { 36 | saved_sample <- obj$by_level_1$save_sample 37 | obj$by_level_1$save_sample <- NULL 38 | if (length(obj$by_levels) > 1) { 39 | for (j in 2:length(obj$by_levels)) { 40 | saved_sample <- rbind(saved_sample, obj[[paste0("by_level_", j)]]$save_sample) 41 | obj[[paste0("by_level_", j)]]$save_sample <- NULL 42 | } 43 | } 44 | saved_sample <- saved_sample[order(saved_sample[[obj$args$group]], saved_sample[[obj$args$time]]), , drop = FALSE] 45 | rownames(saved_sample) <- NULL 46 | obj <- append(obj, list(saved_sample)) 47 | return(obj) 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/man/did_multiplegt_dyn_all_pl.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/did_multiplegt_dyn_all_pl.R 3 | \name{did_multiplegt_dyn_all_pl} 4 | \alias{did_multiplegt_dyn_all_pl} 5 | \title{Core function of DIDmultiplegtDYNallPL} 6 | \usage{ 7 | did_multiplegt_dyn_all_pl( 8 | df, 9 | outcome, 10 | group, 11 | time, 12 | treatment, 13 | effects = 1, 14 | placebo = 0, 15 | switchers = "", 16 | only_never_switchers = FALSE 17 | ) 18 | } 19 | \arguments{ 20 | \item{df}{(dataframe) the estimation dataset. It has to be a \if{html}{\out{}}balanced panel\if{html}{\out{}} with a least \if{html}{\out{}}one never-switcher\if{html}{\out{}} per baseline treatment.} 21 | 22 | \item{outcome}{(char) is the outcome variable.} 23 | 24 | \item{group}{(char) is the group variable.} 25 | 26 | \item{time}{(char) is the time period variable.} 27 | 28 | \item{treatment}{(char) is the treatment variable.} 29 | 30 | \item{effects}{(int) gives the number of event-study effects to be estimated.} 31 | 32 | \item{placebo}{(int) gives the number of placebo estimators to be computed.} 33 | 34 | \item{switchers}{(char in ("", "in", "out")) one may be interested in estimating separately the treatment effect of switchers-in, whose average treatment after they switch is larger than their baseline treatment, and of switchers-out, whose average treatment after they switch is lower than their baseline treatment. In that case, one should run the command first with the \code{switchers = "in"} option, and then with the \code{switchers = "out"} option.} 35 | 36 | \item{only_never_switchers}{(logical) if this option is specified, the command estimates the event-study effects using only never-switchers as control units.} 37 | } 38 | \value{ 39 | The same output as did_multiplegt_dyn. 40 | } 41 | \description{ 42 | Auxiliary function of DIDmultiplegtDYN to retrieve more placebos. 43 | } 44 | \section{Overview}{ 45 | 46 | The following \href{https://github.com/chaisemartinPackages/did_multiplegt_dyn/blob/main/vignettes/vignette_3.md}{vignette} provides an overview of the motivation and the usage of this function. 47 | } 48 | 49 | \examples{ 50 | GG <- 10; TT <- 4; 51 | set.seed(0) 52 | df <- as.data.frame(matrix(nrow =GG * TT, ncol = 0)) 53 | df$G <- floor((1:nrow(df)-1)/TT) + 1 54 | df$T <- ((1:nrow(df)-1) \%\% TT) + 1 55 | df <- df[order(df$G, df$T), ] 56 | df$D <- ifelse(df$T == TT & runif(n = nrow(df)) > 0.5, 1, 0) 57 | df$Y <- runif(n = nrow(df)) * (1 + df$D) 58 | did <- did_multiplegt_dyn_all_pl(df, "Y", "G", "T", "D", effects = 1, placebo = 2) 59 | } 60 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_dyn_normweights.R: -------------------------------------------------------------------------------- 1 | #' Option that shows a table with weights attached to each normalized effect 2 | #' @param data data 3 | #' @param normalized normalized 4 | #' @param normopt normopt 5 | #' @param same_switchers same_switchers 6 | #' @param continuous continuous 7 | #' @import dplyr 8 | #' @importFrom magrittr %>% 9 | #' @importFrom rlang := 10 | #' @importFrom rlang .data 11 | #' @returns A matrix with the normalized_weights option output. 12 | #' @noRd 13 | did_multiplegt_dyn_normweights <- function( 14 | data, 15 | normalized, 16 | normopt, 17 | same_switchers, 18 | continuous 19 | ) { 20 | # Inherited Globals # 21 | df <- data$df 22 | l_XX <- data$l_XX 23 | for (v in names(data$delta)) { 24 | assign(v, data$delta[[v]]) 25 | } 26 | 27 | suppressWarnings({ 28 | 29 | ## Set up the matrix for the output table 30 | weight_mat <- matrix(NA, nrow = l_XX, ncol = l_XX) 31 | coln <- c() 32 | rown <- c() 33 | for (i in 1:l_XX) { 34 | coln <- c(coln, paste0("\U2113","=",i)) 35 | df[[paste0("N_gt_",i,"_temp_XX")]] <- ifelse(df$time_XX == df$F_g_XX - 1 + i, df$N_gt_XX, NA) 36 | df <- df %>% group_by(.data$group_XX) %>% mutate(!!paste0("N_gt_",i,"_XX") := mean(.data[[paste0("N_gt_",i,"_temp_XX")]], na.rm = TRUE)) %>% ungroup() 37 | df[[paste0("N_gt_",i,"_temp_XX")]] <- NULL 38 | for (k in 0:(i-1)) { 39 | 40 | # Visualization by k 41 | row <- k + 1 42 | 43 | ## Compute the delta_l_k, if the continuous option is specified the original treatment values are used 44 | if (is.null(continuous)) { 45 | df[paste0("delta_",i,"_",k)] <- ifelse(df$time_XX == df$F_g_XX - 1 + i - k & df$F_g_XX - 1 + i <= df$T_g_XX, abs(df$treatment_XX - df$d_sq_XX), NA) 46 | } else { 47 | df[paste0("delta_",i,"_",k)] <- ifelse(df$time_XX == df$F_g_XX - 1 + i - k & df$F_g_XX - 1 + i <= df$T_g_XX, abs(df$treatment_XX_orig - df$d_sq_XX_orig), NA) 48 | } 49 | 50 | if (same_switchers == TRUE) { 51 | df[[paste0("delta_",i,"_",k)]] <- ifelse(df$F_g_XX - 1 + l_XX > df$T_g_XX, 0, df[[paste0("delta_",i,"_",k)]]) 52 | } 53 | 54 | df[[paste0("delta_",i,"_",k)]] <- df[[paste0("delta_",i,"_",k)]] * df[[paste0("N_gt_",i,"_XX")]] 55 | weight_mat[row, i] <- (sum(df[[paste0("delta_",i,"_",k)]], na.rm = TRUE) / get(paste0("delta_D_",i,"_global_XX"))) / data$mat_res_XX[i,6] 56 | } 57 | df[[paste0("N_gt_",i,"_XX")]] <- NULL 58 | } 59 | 60 | ## Generating the row names 61 | for (j in 1:l_XX) { 62 | rown <- c(rown, paste0("k=",j-1)) 63 | } 64 | 65 | ## Fill the values for the displayed table 66 | mat_total <- weight_mat 67 | mat_total[is.na(mat_total)] <- 0 68 | total <- matrix(1,nrow=1,ncol=l_XX) %*% mat_total 69 | weight_mat <- rbind(weight_mat, total) 70 | rownames(weight_mat) <- c(rown, "Total") 71 | colnames(weight_mat) <- coln 72 | weight_mat[ , ] <- sprintf("%s", format(round(weight_mat[ , ], 3), big.mark=",", scientific=FALSE, trim=TRUE)) 73 | 74 | return(list(norm_weight_mat = noquote(weight_mat))) 75 | }) 76 | 77 | } -------------------------------------------------------------------------------- /R/R/did_multiplegt_dyn_normweights.R: -------------------------------------------------------------------------------- 1 | #' Option that shows a table with weights attached to each normalized effect 2 | #' @param data data 3 | #' @param normalized normalized 4 | #' @param normopt normopt 5 | #' @param same_switchers same_switchers 6 | #' @param continuous continuous 7 | #' @import data.table 8 | #' @returns A matrix with the normalized_weights option output. 9 | #' @noRd 10 | did_multiplegt_dyn_normweights <- function( 11 | data, 12 | normalized, 13 | normopt, 14 | same_switchers, 15 | continuous 16 | ) { 17 | # Inherited Globals # 18 | df <- data$df 19 | l_XX <- data$l_XX 20 | for (v in names(data$delta)) { 21 | assign(v, data$delta[[v]]) 22 | } 23 | 24 | group_XX <- NULL 25 | 26 | suppressWarnings({ 27 | 28 | ## Set up the matrix for the output table 29 | weight_mat <- matrix(NA, nrow = l_XX, ncol = l_XX) 30 | coln <- c() 31 | rown <- c() 32 | for (i in 1:l_XX) { 33 | coln <- c(coln, paste0("\U2113","=",i)) 34 | 35 | df[[paste0("N_gt_",i,"_temp_XX")]] <- ifelse( 36 | df$time_XX == df$F_g_XX - 1 + i 37 | & i <= df$L_g_XX 38 | & !is.na(df[[paste0("N_gt_control_",i,"_XX")]]) 39 | & df[[paste0("N_gt_control_",i,"_XX")]] >0 40 | & !is.na(df[[paste0("diff_y_",i,"_XX")]]), 41 | df$N_gt_XX, NA) 42 | 43 | df[, paste0("N_gt_",i,"_XX") := mean(get(paste0("N_gt_",i,"_temp_XX")), na.rm = TRUE), by = group_XX] 44 | df[[paste0("N_gt_",i,"_temp_XX")]] <- NULL 45 | for (k in 0:(i-1)) { 46 | 47 | # Visualization by k 48 | row <- k + 1 49 | 50 | ## Compute the delta_l_k, if the continuous option is specified the original treatment values are used 51 | if (is.null(continuous)) { 52 | df[[paste0("delta_",i,"_",k)]] <- ifelse(df$time_XX == df$F_g_XX - 1 + i - k & df$F_g_XX - 1 + i <= df$T_g_XX, abs(df$treatment_XX - df$d_sq_XX), NA) 53 | } else { 54 | df[[paste0("delta_",i,"_",k)]] <- ifelse(df$time_XX == df$F_g_XX - 1 + i - k & df$F_g_XX - 1 + i <= df$T_g_XX, abs(df$treatment_XX_orig - df$d_sq_XX_orig), NA) 55 | } 56 | 57 | if (same_switchers == TRUE) { 58 | df[[paste0("delta_",i,"_",k)]] <- ifelse(df$F_g_XX - 1 + l_XX > df$T_g_XX, 0, df[[paste0("delta_",i,"_",k)]]) 59 | } 60 | 61 | df[[paste0("delta_",i,"_",k)]] <- df[[paste0("delta_",i,"_",k)]] * df[[paste0("N_gt_",i,"_XX")]] 62 | weight_mat[row, i] <- (sum(df[[paste0("delta_",i,"_",k)]], na.rm = TRUE) / get(paste0("delta_D_",i,"_global_XX"))) / data$mat_res_XX[i,ncol(data$mat_res_XX)-1] 63 | } 64 | df[[paste0("N_gt_",i,"_XX")]] <- NULL 65 | } 66 | 67 | ## Generating the row names 68 | for (j in 1:l_XX) { 69 | rown <- c(rown, paste0("k=",j-1)) 70 | } 71 | 72 | ## Fill the values for the displayed table 73 | mat_total <- weight_mat 74 | mat_total[is.na(mat_total)] <- 0 75 | total <- matrix(1,nrow=1,ncol=l_XX) %*% mat_total 76 | weight_mat <- rbind(weight_mat, total) 77 | rownames(weight_mat) <- c(rown, "Total") 78 | colnames(weight_mat) <- coln 79 | weight_mat[ , ] <- sprintf("%s", format(round(weight_mat[ , ], 3), big.mark=",", scientific=FALSE, trim=TRUE)) 80 | 81 | return(list(norm_weight_mat = noquote(weight_mat))) 82 | }) 83 | 84 | } -------------------------------------------------------------------------------- /Examples/R_vs_Stata.R: -------------------------------------------------------------------------------- 1 | rm(list = ls()) 2 | library(devtools) 3 | #devtools::install_github('lbraglia/RStata') 4 | library(RStata) 5 | 6 | diff_print <- function(robj, sobj) { 7 | stata_effects <- subset(sobj, sobj$time_to_treat > 0) 8 | stata_effects <- stata_effects[order(stata_effects$time_to_treat), ] 9 | stata_effects$time_to_treat <- NULL 10 | print(round(abs(stata_effects - robj$results$Effects), 4)) 11 | 12 | stata_placebo <- subset(sobj, sobj$time_to_treat < 0) 13 | stata_placebo$time_to_treat <- -stata_placebo$time_to_treat 14 | stata_placebo <- stata_placebo[order(stata_placebo$time_to_treat), ] 15 | stata_placebo$time_to_treat <- NULL 16 | print(round(abs(stata_placebo - robj$results$Placebo), 4)) 17 | } 18 | 19 | setwd(gsub("Test", "DIDmultiplegtDYN", dirname(sys.frame(1)$ofile))) 20 | devtools::load_all() 21 | options("RStata.StataPath" = "\"C:\\Program Files\\Stata18\\StataMP-64\"") 22 | options("RStata.StataVersion" = 18) 23 | 24 | ## Test 1: Example from Favara-Imbs 25 | setwd(gsub("/DIDmultiplegtDYN", "", getwd())) 26 | repo <- "chaisemartinPackages/ApplicationData/main" 27 | file <- "favara_imbs_did_multiplegt_dyn.dta" 28 | url <- paste("https://raw.githubusercontent.com", repo, file, sep = "/") 29 | data <- haven::read_dta(url) 30 | did_multiplegt_dyn <- did_multiplegt_dyn( 31 | df = data, Y = "Dl_vloans_b", G = "county", T = "year", D = "inter_bra", 32 | effects = 8, placebo = 3, graph_off = TRUE) 33 | #print(did_multiplegt_dyn) 34 | stata_mat <- stata("qui do did_multiplegt_dyn_working_24_01_24.do 35 | qui did_multiplegt_dyn_new Dl_vloans_b county year inter_bra, effects(8) placebo(3) save_results(res) graph_off 36 | qui use res.dta, clear", data.in = data, data.out = TRUE) 37 | diff_print(did_multiplegt_dyn, stata_mat) 38 | stata_mat <- NULL 39 | stata("clear") 40 | 41 | # Test 2: Example from WAGEPAN data 42 | library(wooldridge) 43 | data("wagepan") 44 | setwd(gsub("/DIDmultiplegtDYN", "", getwd())) 45 | wagepan$over_gr <- wagepan$nr %% 3 46 | did_multiplegt_dyn <- did_multiplegt_dyn( 47 | df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", 48 | effects = 5, placebo = 2, graph_off = TRUE, cluster = "over_gr") 49 | stata_mat <- stata("qui do did_multiplegt_dyn_working_24_01_24.do 50 | qui did_multiplegt_dyn_new lwage nr year union, effects(5) placebo(2) save_results(res) graph_off cluster(over_gr) 51 | qui use res.dta, clear", data.in = wagepan, data.out = TRUE) 52 | diff_print(did_multiplegt_dyn, stata_mat) 53 | stata_mat <- NULL 54 | stata("clear") 55 | 56 | library(wooldridge) 57 | data("wagepan") 58 | setwd(gsub("/DIDmultiplegtDYN", "", getwd())) 59 | wagepan$over_gr <- wagepan$nr %% 3 60 | did_multiplegt_dyn <- did_multiplegt_dyn( 61 | df = wagepan, Y = "lwage", G = "nr", T = "year", D = "union", 62 | effects = 5, placebo = 2, graph_off = TRUE, cluster = "over_gr") 63 | stata_mat <- stata("qui do did_multiplegt_dyn_working_24_01_24.do 64 | qui did_multiplegt_dyn_new lwage nr year union, effects(5) placebo(2) save_results(res) graph_off cluster(over_gr) 65 | qui use res.dta, clear", data.in = wagepan, data.out = TRUE) 66 | diff_print(did_multiplegt_dyn, stata_mat) 67 | stata_mat <- NULL 68 | stata("clear") 69 | 70 | unlink("res.dta") 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /vignettes/assets/reg1.fls: -------------------------------------------------------------------------------- 1 | PWD c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets 2 | INPUT c:/texlive/2023/texmf.cnf 3 | INPUT c:/texlive/2023/texmf-dist/web2c/texmf.cnf 4 | INPUT c:/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt 5 | INPUT c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1.tex 6 | OUTPUT reg1.log 7 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 8 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 9 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 10 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 11 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 12 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 13 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 14 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 15 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 16 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 17 | INPUT c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 18 | INPUT c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 19 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex 20 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex 21 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex 22 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 23 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 24 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 25 | INPUT c:/texlive/2023/texmf-dist/tex/latex/preview/preview.sty 26 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 27 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 28 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 29 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 30 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 31 | INPUT c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 32 | INPUT c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 33 | INPUT c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 34 | INPUT c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 35 | INPUT c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 36 | INPUT c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 37 | INPUT ./reg1.aux 38 | INPUT ./reg1.aux 39 | INPUT reg1.aux 40 | OUTPUT reg1.aux 41 | INPUT c:/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map 42 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm 43 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm 44 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm 45 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm 46 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm 47 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm 48 | OUTPUT reg1.pdf 49 | INPUT c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map 50 | INPUT reg1.aux 51 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb 52 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb 53 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb 54 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy6.pfb 55 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb 56 | -------------------------------------------------------------------------------- /vignettes/assets/reg2.fls: -------------------------------------------------------------------------------- 1 | PWD c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets 2 | INPUT c:/texlive/2023/texmf.cnf 3 | INPUT c:/texlive/2023/texmf-dist/web2c/texmf.cnf 4 | INPUT c:/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt 5 | INPUT c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg2.tex 6 | OUTPUT reg2.log 7 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 8 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 9 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 10 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 11 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 12 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 13 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 14 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 15 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 16 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 17 | INPUT c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 18 | INPUT c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 19 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex 20 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex 21 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex 22 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 23 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 24 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 25 | INPUT c:/texlive/2023/texmf-dist/tex/latex/preview/preview.sty 26 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 27 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 28 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 29 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 30 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 31 | INPUT c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 32 | INPUT c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 33 | INPUT c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 34 | INPUT c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 35 | INPUT c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 36 | INPUT c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 37 | INPUT ./reg2.aux 38 | INPUT ./reg2.aux 39 | INPUT reg2.aux 40 | OUTPUT reg2.aux 41 | INPUT c:/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map 42 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm 43 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm 44 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm 45 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm 46 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm 47 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm 48 | OUTPUT reg2.pdf 49 | INPUT c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map 50 | INPUT reg2.aux 51 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb 52 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb 53 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb 54 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy6.pfb 55 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb 56 | -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/R/R/did_multiplegt_dyn_all_pl.R: -------------------------------------------------------------------------------- 1 | #' Core function of DIDmultiplegtDYNallPL 2 | #' @md 3 | #' @description Auxiliary function of DIDmultiplegtDYN to retrieve more placebos. 4 | #' @param df (dataframe) the estimation dataset. It has to be a balanced panel with a least one never-switcher per baseline treatment. 5 | #' @param outcome (char) is the outcome variable. 6 | #' @param group (char) is the group variable. 7 | #' @param time (char) is the time period variable. 8 | #' @param treatment (char) is the treatment variable. 9 | #' @param effects (int) gives the number of event-study effects to be estimated. 10 | #' @param placebo (int) gives the number of placebo estimators to be computed. 11 | #' @param switchers (char in ("", "in", "out")) one may be interested in estimating separately the treatment effect of switchers-in, whose average treatment after they switch is larger than their baseline treatment, and of switchers-out, whose average treatment after they switch is lower than their baseline treatment. In that case, one should run the command first with the \code{switchers = "in"} option, and then with the \code{switchers = "out"} option. 12 | #' @param only_never_switchers (logical) if this option is specified, the command estimates the event-study effects using only never-switchers as control units. 13 | #' @section Overview: 14 | #' The following [vignette](https://github.com/chaisemartinPackages/did_multiplegt_dyn/blob/main/vignettes/vignette_3.md) provides an overview of the motivation and the usage of this function. 15 | #' @import DIDmultiplegtDYN 16 | #' @import dplyr 17 | #' @examples 18 | #' GG <- 10; TT <- 4; 19 | #' set.seed(0) 20 | #' df <- as.data.frame(matrix(nrow =GG * TT, ncol = 0)) 21 | #' df$G <- floor((1:nrow(df)-1)/TT) + 1 22 | #' df$T <- ((1:nrow(df)-1) %% TT) + 1 23 | #' df <- df[order(df$G, df$T), ] 24 | #' df$D <- ifelse(df$T == TT & runif(n = nrow(df)) > 0.5, 1, 0) 25 | #' df$Y <- runif(n = nrow(df)) * (1 + df$D) 26 | #' did <- did_multiplegt_dyn_all_pl(df, "Y", "G", "T", "D", effects = 1, placebo = 2) 27 | #' @returns The same output as did_multiplegt_dyn. 28 | #' @export 29 | did_multiplegt_dyn_all_pl <- function( 30 | df, 31 | outcome, 32 | group, 33 | time, 34 | treatment, 35 | effects = 1, 36 | placebo = 0, 37 | switchers = "", 38 | only_never_switchers = FALSE 39 | ) { 40 | 41 | did1 <- did_multiplegt_dyn( 42 | df = df, 43 | outcome = outcome, 44 | group = group, 45 | time = time, 46 | treatment = treatment, 47 | effects = effects, 48 | switchers = switchers, 49 | only_never_switchers = only_never_switchers, 50 | graph_off = T 51 | ) 52 | 53 | data <- add_periods( 54 | df = df, 55 | outcome = outcome, 56 | group = group, 57 | time = time, 58 | treatment = treatment, 59 | add = did1$results$max_pl_gap 60 | ) 61 | 62 | did2 <- did_multiplegt_dyn( 63 | df = data, 64 | outcome = outcome, 65 | group = group, 66 | time = time, 67 | treatment = treatment, 68 | effects = max(effects, placebo), 69 | placebo = placebo, 70 | switchers = switchers, 71 | only_never_switchers = only_never_switchers, 72 | graph_off = T 73 | ) 74 | 75 | did2$args$effects <- effects 76 | did2$results$N_Effects <- nrow(did1$results$Effects) 77 | did2$results$Effects <- did1$results$Effects 78 | did2$results$ATE <- did1$results$ATE 79 | did2$results$delta_D_avg_total <- did1$results$delta_D_avg_total 80 | did2$max_pl <- did2$max_pl_gap <- NULL 81 | did2$coef <- NULL 82 | did2$plot <- NULL 83 | 84 | class(did2) <- "did_multiplegt_dyn_all_pl" 85 | return(did2) 86 | } -------------------------------------------------------------------------------- /vignettes/assets/reg1a.fls: -------------------------------------------------------------------------------- 1 | PWD c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets 2 | INPUT c:/texlive/2023/texmf.cnf 3 | INPUT c:/texlive/2023/texmf-dist/web2c/texmf.cnf 4 | INPUT c:/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt 5 | INPUT c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1a.tex 6 | OUTPUT reg1a.log 7 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 8 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 9 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 10 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 11 | INPUT c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 12 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 13 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 14 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 15 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 16 | INPUT c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 17 | INPUT c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 18 | INPUT c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 19 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex 20 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex 21 | INPUT c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex 22 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 23 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 24 | INPUT c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 25 | INPUT c:/texlive/2023/texmf-dist/tex/latex/preview/preview.sty 26 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 27 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 28 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 29 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 30 | INPUT c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 31 | INPUT c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 32 | INPUT c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 33 | INPUT c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 34 | INPUT c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 35 | INPUT c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 36 | INPUT c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 37 | INPUT ./reg1a.aux 38 | INPUT ./reg1a.aux 39 | INPUT reg1a.aux 40 | OUTPUT reg1a.aux 41 | INPUT c:/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map 42 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm 43 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm 44 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm 45 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm 46 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm 47 | INPUT c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm 48 | OUTPUT reg1a.pdf 49 | INPUT c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map 50 | INPUT reg1a.aux 51 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb 52 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb 53 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb 54 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb 55 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb 56 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb 57 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy6.pfb 58 | INPUT c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb 59 | -------------------------------------------------------------------------------- /vignettes/assets/reg1.fdb_latexmk: -------------------------------------------------------------------------------- 1 | # Fdb version 4 2 | ["pdflatex"] 1715607817 "c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1.tex" "reg1.pdf" "reg1" 1715607818 0 3 | "c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1.tex" 1715607801 1946 dc8b3fd9d91484b89ccb1b0702033a32 "" 4 | "c:/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map" 1701535936 3524 cb3e574dea2d1052e39280babc910dc8 "" 5 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1701535389 1512 f21f83efb36853c0b70002322c1ab3ad "" 6 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1701535389 1520 eccf95517727cb11801f4f1aee3a21b4 "" 7 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1701535389 1300 b62933e007d01cfd073f79b963c01526 "" 8 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1701535389 1292 21c1c5bfeaebccffdb478fd231a0997d "" 9 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1701535389 1116 933a60c408fc0a863a92debe84b2d294 "" 10 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1701535389 1120 8b7d695260f3cff42e636090a8002094 "" 11 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb" 1701534946 35469 dcf3a5f2fc1862f5952e3ee5eb1d98c4 "" 12 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1701534946 35752 024fb6c41858982481f6968b5fc26508 "" 13 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb" 1701534946 32726 39f0f9e62e84beb801509898a605dbd5 "" 14 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy6.pfb" 1701534946 32587 65067f817f408bc71a7312f3d9828a9b "" 15 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb" 1701534946 32716 08e384dc442464e7285e891af9f45947 "" 16 | "c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty" 1701536238 492 1994775aa15b0d1289725a0b1bbc2d4c "" 17 | "c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty" 1701536238 7237 bdd120a32c8fdb4b433cf9ca2e7cd98a "" 18 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex" 1701538188 2725 1a42bd9e7e57e25fc7763c445f4b785b "" 19 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1701538188 19231 27205ee17aaa2902aea3e0c07a3cfc65 "" 20 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1701538188 7677 9cb1a74d945bc9331f2181c0a59ff34a "" 21 | "c:/texlive/2023/texmf-dist/tex/latex/base/article.cls" 1701536404 20144 fbcf51733730d8f1a62143512ff5b1fe "" 22 | "c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo" 1701536404 8448 74078c4a887f2cdcd57c3ec111622740 "" 23 | "c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty" 1701535244 6078 f1cb470c9199e7110a27851508ed7a5c "" 24 | "c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1701536386 29988 6d57d2b884f7464419678df319af86d2 "" 25 | "c:/texlive/2023/texmf-dist/tex/latex/preview/preview.sty" 1701537161 13873 f20e5546ad38d2ac6052703bc753a668 "" 26 | "c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg" 1701537589 1015 ca4a9fdee3211711bd723951f61ec2fe "" 27 | "c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls" 1701537589 29032 f9277545472a63d812460310b499511e "" 28 | "c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty" 1701537907 4121 f96144e646260251ded7f19bcb6f4e71 "" 29 | "c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty" 1701538067 10894 d359a13923460b2a73d4312d613554c8 "" 30 | "c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1701538188 4937 4ce600ce9bd4ec84d0250eb6892fcf4f "" 31 | "c:/texlive/2023/texmf-dist/web2c/texmf.cnf" 1701534871 41009 84b61f42d16d06bedb915f57aa2374cf "" 32 | "c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1701538279 4754669 d96ac81ce84784930de265f48f40d2ce "" 33 | "c:/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt" 1701538467 8213458 d09614b24db911a26a44c8c5d1755e81 "" 34 | "c:/texlive/2023/texmf.cnf" 1701538257 713 614551405fc44b98efbf516466e3c909 "" 35 | "reg1.aux" 1715607817 32 3985256e7290058c681f74d7a3565a19 "pdflatex" 36 | "reg1.tex" 1715607801 1946 dc8b3fd9d91484b89ccb1b0702033a32 "" 37 | (generated) 38 | "reg1.aux" 39 | "reg1.log" 40 | "reg1.pdf" 41 | (rewritten before read) 42 | -------------------------------------------------------------------------------- /vignettes/assets/reg2.fdb_latexmk: -------------------------------------------------------------------------------- 1 | # Fdb version 4 2 | ["pdflatex"] 1715607803 "c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg2.tex" "reg2.pdf" "reg2" 1715607804 0 3 | "c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg2.tex" 1715607802 1538 0d7e828e6e6af7f121de36a955389f50 "" 4 | "c:/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map" 1701535936 3524 cb3e574dea2d1052e39280babc910dc8 "" 5 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1701535389 1512 f21f83efb36853c0b70002322c1ab3ad "" 6 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1701535389 1520 eccf95517727cb11801f4f1aee3a21b4 "" 7 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1701535389 1300 b62933e007d01cfd073f79b963c01526 "" 8 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1701535389 1292 21c1c5bfeaebccffdb478fd231a0997d "" 9 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1701535389 1116 933a60c408fc0a863a92debe84b2d294 "" 10 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1701535389 1120 8b7d695260f3cff42e636090a8002094 "" 11 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb" 1701534946 35469 dcf3a5f2fc1862f5952e3ee5eb1d98c4 "" 12 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1701534946 35752 024fb6c41858982481f6968b5fc26508 "" 13 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb" 1701534946 32726 39f0f9e62e84beb801509898a605dbd5 "" 14 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy6.pfb" 1701534946 32587 65067f817f408bc71a7312f3d9828a9b "" 15 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb" 1701534946 32716 08e384dc442464e7285e891af9f45947 "" 16 | "c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty" 1701536238 492 1994775aa15b0d1289725a0b1bbc2d4c "" 17 | "c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty" 1701536238 7237 bdd120a32c8fdb4b433cf9ca2e7cd98a "" 18 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex" 1701538188 2725 1a42bd9e7e57e25fc7763c445f4b785b "" 19 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1701538188 19231 27205ee17aaa2902aea3e0c07a3cfc65 "" 20 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1701538188 7677 9cb1a74d945bc9331f2181c0a59ff34a "" 21 | "c:/texlive/2023/texmf-dist/tex/latex/base/article.cls" 1701536404 20144 fbcf51733730d8f1a62143512ff5b1fe "" 22 | "c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo" 1701536404 8448 74078c4a887f2cdcd57c3ec111622740 "" 23 | "c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty" 1701535244 6078 f1cb470c9199e7110a27851508ed7a5c "" 24 | "c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1701536386 29988 6d57d2b884f7464419678df319af86d2 "" 25 | "c:/texlive/2023/texmf-dist/tex/latex/preview/preview.sty" 1701537161 13873 f20e5546ad38d2ac6052703bc753a668 "" 26 | "c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg" 1701537589 1015 ca4a9fdee3211711bd723951f61ec2fe "" 27 | "c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls" 1701537589 29032 f9277545472a63d812460310b499511e "" 28 | "c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty" 1701537907 4121 f96144e646260251ded7f19bcb6f4e71 "" 29 | "c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty" 1701538067 10894 d359a13923460b2a73d4312d613554c8 "" 30 | "c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1701538188 4937 4ce600ce9bd4ec84d0250eb6892fcf4f "" 31 | "c:/texlive/2023/texmf-dist/web2c/texmf.cnf" 1701534871 41009 84b61f42d16d06bedb915f57aa2374cf "" 32 | "c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1701538279 4754669 d96ac81ce84784930de265f48f40d2ce "" 33 | "c:/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt" 1701538467 8213458 d09614b24db911a26a44c8c5d1755e81 "" 34 | "c:/texlive/2023/texmf.cnf" 1701538257 713 614551405fc44b98efbf516466e3c909 "" 35 | "reg2.aux" 1715607804 32 3985256e7290058c681f74d7a3565a19 "pdflatex" 36 | "reg2.tex" 1715607802 1538 0d7e828e6e6af7f121de36a955389f50 "" 37 | (generated) 38 | "reg2.aux" 39 | "reg2.log" 40 | "reg2.pdf" 41 | (rewritten before read) 42 | -------------------------------------------------------------------------------- /did_multiplegt_dyn_all_pl/Stata/did_multiplegt_dyn_all_pl.ado: -------------------------------------------------------------------------------- 1 | cap program drop did_multiplegt_dyn_all_pl 2 | program define did_multiplegt_dyn_all_pl, eclass 3 | version 12.0 4 | syntax varlist(min=4 max=4 numeric) [if] [in] [, effects(integer 1) placebo(integer 1) switchers(string) only_never_switchers] 5 | 6 | di "" 7 | di as text "Step 1: Retrieving dynamic effects" 8 | qui { 9 | qui did_multiplegt_dyn `varlist', effects(`effects') switchers(`switchers') `only_never_switchers' graph_off 10 | 11 | mat b_dyn = e(b)[1, 1..l_XX] 12 | mat b_ATE = e(b)[1, l_XX + 1] 13 | mat V_dyn = e(V)[1..l_XX, 1..l_XX] 14 | mat V_ATE = e(V)[1+l_XX, 1+l_XX] 15 | scalar l_XX_og = l_XX 16 | 17 | cap di max_pl_XX 18 | if _rc != 0 { 19 | noi di as err "did_multiplegt_dyn_all_pl requires a more recent version of did_multiplegt_dyn." 20 | noi di as err "Please update your distribution of did_multiplegt_dyn by running:" 21 | noi di as err "{stata ssc install did_multiplegt_dyn, replace}" 22 | 23 | exit 24 | } 25 | } 26 | 27 | di "{hline 80}" 28 | di _skip(13) "{bf:Estimation of treatment effects: Event-study effects}" 29 | di "{hline 80}" 30 | matlist mat_res_XX[1..l_XX, 1..6] 31 | di "{hline 80}" 32 | cap qui di scalar(p_equality_effects) 33 | if _rc == 0 { 34 | di as text "{it:Test of equality of the effects : p-value =} " scalar(p_equality_effects) 35 | } 36 | 37 | matrix mat_res_avg_XX=mat_res_XX[l_XX+1, 1..6] 38 | matrix mat_res_avg_XX=(mat_res_avg_XX, .z ) 39 | matrix colnames mat_res_avg_XX= "Estimate" "SE" "LB CI" "UB CI" "N" "Switch" "x Periods" 40 | display _newline 41 | di "{hline 80}" 42 | di _skip(15) "{bf:Average cumulative (total) effect per treatment unit}" 43 | di "{hline 80}" 44 | matlist mat_res_avg_XX, nodotz 45 | di "{hline 80}" 46 | 47 | di "" 48 | di as text "Step 2: Running again with `placebo' out of `=max_pl_XX' feasible placebo(s)" 49 | qui { 50 | 51 | preserve 52 | add_periods `varlist', add(`=max_pl_gap_XX') 53 | if `effects' < `placebo' { 54 | local effects = `placebo' 55 | } 56 | 57 | qui did_multiplegt_dyn `varlist', effects(`effects') placebo(`placebo') switchers(`switchers') `only_never_switchers' graph_off 58 | mat b = b_dyn, e(b)[1, l_XX+1..l_XX+l_placebo_XX], b_ATE 59 | mat V = (V_dyn, J(l_XX_og, l_placebo_XX, 0)) \ (J(l_placebo_XX, l_XX_og, 0), e(V)[l_XX+1..l_XX+l_placebo_XX, l_XX+1..l_XX+l_placebo_XX]) 60 | mat V = (V, J(l_XX_og + l_placebo_XX, 1, 0)) \ (J(1, l_XX_og + l_placebo_XX, 0), V_ATE) 61 | restore 62 | 63 | } 64 | 65 | di "{hline 80}" 66 | di _skip(10) "{bf:Testing the parallel trends and no anticipation assumptions}" 67 | di "{hline 80}" 68 | matlist mat_res_XX[l_XX+2...,1..6] 69 | di "{hline 80}" 70 | cap qui di scalar(p_jointplacebo) 71 | if _rc == 0 { 72 | di as text "{it:Test of joint nullity of the placebos : p-value =} " scalar(p_jointplacebo) 73 | } 74 | 75 | local rown "" 76 | forv j = 1/`=scalar(l_XX_og)' { 77 | local rown "`rown' Effect_`j'" 78 | } 79 | forv j = 1/`=scalar(l_placebo_XX)' { 80 | local rown "`rown' Placebo_`j'" 81 | } 82 | local rown "`rown' Av_tot_eff" 83 | mat rownames V = `rown' 84 | mat colnames V = `rown' 85 | mat colnames b = `rown' 86 | ereturn post b V 87 | end 88 | 89 | cap program drop add_periods 90 | program define add_periods, rclass 91 | syntax varlist, add(string) 92 | qui { 93 | if `add' != 0 { 94 | tokenize `varlist' 95 | sum `3' 96 | local top = r(max) 97 | sort `3' `2' 98 | unique `2' 99 | forv j = 1/`=r(unique)' { 100 | local N = _N 101 | insobs `add' 102 | replace `1' = 0 if missing(`2') 103 | replace `4' = `4'[`j'] if missing(`2') 104 | replace `2' = `2'[`j'] if missing(`2') 105 | forv i = 1/`add' { 106 | replace `3' = `top' + `i' in `=`N' + `i'' 107 | } 108 | } 109 | sort `2' `3' 110 | } 111 | } 112 | end -------------------------------------------------------------------------------- /vignettes/assets/reg1a.fdb_latexmk: -------------------------------------------------------------------------------- 1 | # Fdb version 4 2 | ["pdflatex"] 1715603350 "c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1a.tex" "reg1a.pdf" "reg1a" 1715603351 0 3 | "c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1a.tex" 1715603339 1950 1a62d18f7e64479fcd0226603d80aaf3 "" 4 | "c:/texlive/2023/texmf-dist/fonts/map/fontname/texfonts.map" 1701535936 3524 cb3e574dea2d1052e39280babc910dc8 "" 5 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1701535389 1512 f21f83efb36853c0b70002322c1ab3ad "" 6 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1701535389 1520 eccf95517727cb11801f4f1aee3a21b4 "" 7 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1701535389 1300 b62933e007d01cfd073f79b963c01526 "" 8 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1701535389 1292 21c1c5bfeaebccffdb478fd231a0997d "" 9 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1701535389 1116 933a60c408fc0a863a92debe84b2d294 "" 10 | "c:/texlive/2023/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1701535389 1120 8b7d695260f3cff42e636090a8002094 "" 11 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb" 1701534946 36299 5f9df58c2139e7edcf37c8fca4bd384d "" 12 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb" 1701534946 36281 c355509802a035cadc5f15869451dcee "" 13 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb" 1701534946 35469 dcf3a5f2fc1862f5952e3ee5eb1d98c4 "" 14 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1701534946 35752 024fb6c41858982481f6968b5fc26508 "" 15 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb" 1701534946 32762 7fee39e011c23b3589931effd97b9702 "" 16 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb" 1701534946 32726 39f0f9e62e84beb801509898a605dbd5 "" 17 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy6.pfb" 1701534946 32587 65067f817f408bc71a7312f3d9828a9b "" 18 | "c:/texlive/2023/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb" 1701534946 32716 08e384dc442464e7285e891af9f45947 "" 19 | "c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty" 1701536238 492 1994775aa15b0d1289725a0b1bbc2d4c "" 20 | "c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty" 1701536238 7237 bdd120a32c8fdb4b433cf9ca2e7cd98a "" 21 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex" 1701538188 2725 1a42bd9e7e57e25fc7763c445f4b785b "" 22 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex" 1701538188 19231 27205ee17aaa2902aea3e0c07a3cfc65 "" 23 | "c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex" 1701538188 7677 9cb1a74d945bc9331f2181c0a59ff34a "" 24 | "c:/texlive/2023/texmf-dist/tex/latex/base/article.cls" 1701536404 20144 fbcf51733730d8f1a62143512ff5b1fe "" 25 | "c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo" 1701536404 8448 74078c4a887f2cdcd57c3ec111622740 "" 26 | "c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty" 1701535244 6078 f1cb470c9199e7110a27851508ed7a5c "" 27 | "c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1701536386 29988 6d57d2b884f7464419678df319af86d2 "" 28 | "c:/texlive/2023/texmf-dist/tex/latex/preview/preview.sty" 1701537161 13873 f20e5546ad38d2ac6052703bc753a668 "" 29 | "c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg" 1701537589 1015 ca4a9fdee3211711bd723951f61ec2fe "" 30 | "c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls" 1701537589 29032 f9277545472a63d812460310b499511e "" 31 | "c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty" 1701537907 4121 f96144e646260251ded7f19bcb6f4e71 "" 32 | "c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty" 1701538067 10894 d359a13923460b2a73d4312d613554c8 "" 33 | "c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty" 1701538188 4937 4ce600ce9bd4ec84d0250eb6892fcf4f "" 34 | "c:/texlive/2023/texmf-dist/web2c/texmf.cnf" 1701534871 41009 84b61f42d16d06bedb915f57aa2374cf "" 35 | "c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1701538279 4754669 d96ac81ce84784930de265f48f40d2ce "" 36 | "c:/texlive/2023/texmf-var/web2c/pdftex/pdflatex.fmt" 1701538467 8213458 d09614b24db911a26a44c8c5d1755e81 "" 37 | "c:/texlive/2023/texmf.cnf" 1701538257 713 614551405fc44b98efbf516466e3c909 "" 38 | "reg1a.aux" 1715603351 32 3985256e7290058c681f74d7a3565a19 "pdflatex" 39 | "reg1a.tex" 1715603339 1950 1a62d18f7e64479fcd0226603d80aaf3 "" 40 | (generated) 41 | "reg1a.aux" 42 | "reg1a.log" 43 | "reg1a.pdf" 44 | (rewritten before read) 45 | -------------------------------------------------------------------------------- /Python/README.md: -------------------------------------------------------------------------------- 1 | # Running did_multiplegt_dyn in Python 2 | 3 | The **DIDmultiplegtDYN** library has been developed for the R language. As a result, one could use the **rpy2** library to load the package in a Python environment. All the most updated options of the did_multiplegt_dyn command can be accessed, even though some tweaks are needed to adjust Python classes such that input objects can be read correcly. Lastly, the output can be assigned to a Python object and parsed with nested list syntax. 4 | 5 | ## The rpy2 library 6 | The **rpy2** enables R commands and packages in Python scripts. It can be installed by running 7 | ```r 8 | pip install rpy2 9 | ``` 10 | For Windows users, it is best to install rpy2 using **wheel**. First, get wheel: 11 | ```r 12 | pip install wheel 13 | ``` 14 | Then, get the latest rpy2 .whl extension [here](http://www.lfd.uci.edu/~gohlke/pythonlibs/#rpy2). Download the .whl file and open **cmd** in the directory where the .whl file is located. Run: 15 | ```r 16 | pip install `whl_file'.whl 17 | ``` 18 | replacing `whl_file' with the name of the file. Now you can check that rpy2 is installed by loading python and checking whether **import rpy2** returns errors. 19 | 20 | Before using rpy2 in Windows, you should also update your R PATH information. Browse *edit environmental variables* in the search menu and do the following: 21 | 1. Add R to PATH (with R 4.3.3, you should add a bin path similar to "C:\Program Files\R\R-4.3.3\bin\x64") 22 | 2. Create a new variable called R_HOME and assign it to the R version directory (e.g., "C:\Program Files\R\R-4.3.3") 23 | 3. Create a new variable called R_USER and assign it to the entry in the *user* field from running Sys.info() in R. 24 | 25 | ## R-Python integration 26 | Start by loading the required libraries: 27 | ```r 28 | import rpy2 29 | import rpy2.robjects as robjects 30 | from rpy2.robjects.packages import importr 31 | from rpy2.robjects import pandas2ri 32 | ``` 33 | We will need *robjects* to load string vectors, *importr* to load DIDmultiplegtDYN and *pandas2ri* to convert a pandas dataframe into an R dataframe. The last one should be explicitely activated in the script. To showcase the routine, we load the dataset from Vella and Verkeek ([1998](https://onlinelibrary.wiley.com/doi/abs/10.1002/(SICI)1099-1255(199803/04)13:2%3C163::AID-JAE460%3E3.0.CO;2-Y)): 34 | ```r 35 | import wooldridge 36 | 37 | pandas2ri.activate() 38 | wagepan = pandas2ri.py2ri(wooldridge.data('wagepan')) 39 | ``` 40 | 41 | If you installed rpy2 using just pip install rpy2, then replace the last line with 42 | ```r 43 | wagepan = pandas2ri.py2rpy(wooldridge.data('wagepan')) 44 | ``` 45 | Once the dataframe is loaded, we can set up the estimation options. The arguments of the options have different classes (numeric, list, character, ...). These R classes are normally different than Python ones ("in R, *almost* everything is a vector"), but a bit of tweaking is enough to ensure that all the options of **did_multiplegt_dyn** can be correctly assigned. Consider the following equivalences: 46 | + string arguments ("outcome", "group", "time", "treatment", "by", "cluster", "weight", "switchers", "save_results") can be specified as Python strings (e.g. outcome = 'lwage'); 47 | + integer arguments ("effects","placebo", "ci_level", "continuous", "bootstrap", "by_path") can be specified as floats with zero decimal part (e.g. effects = 3.0) - This is due to the fact that the R integer class is quite restrictive, so the syntax check looks for numeric objects such that the remainder from 1 is 0; 48 | + list arguments ("predict_het", "design", "date_first_switch") can be specified as Python lists (e.g. design = [1, "console"]); 49 | + string vector arguments (e.g., arguments that can be strings or vectors of strings, that is, "controls", "trends_nonparams") can be specified as a single string in the single variable case or using *StrVector* in case of multiple variables (e.g. controls = robjects.StrVector(["married", "black"])); 50 | + logical arguments ("normalized", "normalized_weights", "effects_equal", "trends_lin", "same_switchers", "same_switchers_pl", "graph_off", "save_sample", "less_conservative_se", "dont_drop_larger_lower", "drop_if_d_miss_before_first_switch") can be specified using True/False. 51 | 52 | The results can be assigned to an object that has it own print method. The base syntax for this object is the same as R lists, which implies that the object can be accessed through subsetting. 53 | 54 | ## Example 55 | ```r 56 | DIDmultiplegtDYN = importr('DIDmultiplegtDYN') 57 | did = DIDmultiplegtDYN.did_multiplegt_dyn(df = wagepan, outcome = 'lwage', group = 'nr', time = 'year', treatment = 'union', effects = 5.0, normalized = True, design = [1, "console"], controls = robjects.StrVector(["married", "hours"])) 58 | 59 | # Display the results 60 | print(did) 61 | 62 | # Retrieve the ATE 63 | print(did[1][3][0]) 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_dyn_graph.R: -------------------------------------------------------------------------------- 1 | #' Function for event study plot. the did_multiplegt_dyn command always generates a ggplot object that can be printed right after the end of the routine (graph_off = FALSE) or called afterwards from the global environment after assigning the did_multiplegt_dyn output to a variable. 2 | #' @param data data 3 | #' @import ggplot2 4 | #' @import dplyr 5 | #' @importFrom magrittr %>% 6 | #' @returns A ggplot object. 7 | #' @noRd 8 | did_multiplegt_dyn_graph <- function(data) { 9 | grmat <- rbind(cbind(data$Effects, 1:nrow(data$Effects)),cbind(data$ATE, 0)) 10 | if (!is.null(data$Placebos)) { 11 | grmat <- rbind(grmat, cbind(data$Placebos, (-1) * 1:nrow(data$Placebos))) 12 | } 13 | colnames(grmat)[ncol(grmat)] <- "Time" 14 | grmat[nrow(data$Effects) + 1, c(1,3,4)] <- 0 15 | grmat <- data.frame(grmat[, c(1,3,4,9)]) 16 | did_multiplegt_dyn_plot <- ggplot(grmat,aes(x = .data$Time, y = .data$Estimate, group = 1)) + 17 | geom_line(colour = "blue") + 18 | geom_errorbar(data = ~dplyr::filter(.x, grmat$Estimate != 0),aes(ymin = .data$LB.CI, ymax = .data$UB.CI), position=position_dodge(0.05), width = 0.2, colour = "red") + 19 | geom_point(colour = "blue") + 20 | ggtitle("DID, from last period before treatment changes (t=0) to t") + 21 | xlab("Relative time to last period before treatment changes (t=0)") + 22 | theme(plot.title = element_text(hjust = 0.5)) 23 | return(did_multiplegt_dyn_plot) 24 | } 25 | 26 | #' Internal function of did_multiplegt_dyn to overlay plots 27 | #' @param obj A did_multiplegt_dyn object 28 | #' @import ggplot2 29 | #' @import cowplot 30 | #' @returns A ggplot object. 31 | #' @noRd 32 | combine_plot <- function(obj) { 33 | if (!is.null(obj$args[["by"]])) { 34 | color_set <- get_colors(length(obj$by_levels)) 35 | base_plot <- obj$by_level_1$plot 36 | plot <- ggplot(data = base_plot$data, aes(x = base_plot$data$Time, y = base_plot$data$Estimate)) + 37 | geom_point(colour = color_set[1]) + geom_line(aes(colour = paste0(obj$args$by," = ", obj$by_levels[1]))) + 38 | geom_errorbar(data = base_plot$data, aes(ymin = base_plot$data$LB.CI, ymax = base_plot$data$UB.CI), position=position_dodge(0.05), width = 0.2, colour = color_set[1]) 39 | if (length(obj$by_levels) > 1) { 40 | for (j in 2:length(obj$by_levels)) { 41 | add_plot <- obj[[paste0("by_level_",j)]][["plot"]] 42 | plot <- plot + 43 | geom_point(data = add_plot$data, aes_(x = add_plot$data$Time, y = add_plot$data$Estimate), colour = color_set[j]) + 44 | geom_errorbar(data = add_plot$data, aes_(ymin = add_plot$data$LB.CI, ymax = add_plot$data$UB.CI), position=position_dodge(0.05), width = 0.2, colour = color_set[j]) + 45 | geom_line(data = add_plot$data, aes_(x = add_plot$data$Time, y = add_plot$data$Estimate, 46 | colour = paste0(obj$args$by, " = ", obj$by_levels[j]))) 47 | } 48 | } 49 | plot <- plot + ylab("Estimate") + ggtitle("DID, from last period before treatment changes (t=0) to t") + 50 | xlab("Relative time to last period before treatment changes (t=0)") + 51 | theme(plot.title = element_text(hjust = 0.5), legend.position = "bottom") + 52 | scale_colour_manual("", breaks = paste0(obj$args$by, " = ", obj$by_levels), values = color_set) 53 | } else if (!is.null(obj$args[["by_path"]])) { 54 | if (length(obj$by_levels) > 100) { 55 | message("The command allows a maximum of 100 graphs to be combined in a 10 x 10 window. The resulting graph will be restricted to the first 100 treatment paths.") 56 | } 57 | sides <- ceiling(sqrt(length(obj$by_levels))); len <- 1/sides; 58 | plot <- ggdraw() 59 | for (j in 1:length(obj$by_levels)) { 60 | plot <- plot + draw_plot(obj[[paste0("by_level_",j)]]$plot + ggtitle(sprintf("Treatment path (%s); %.0f switchers", obj$by_levels[j], obj[[paste0("by_level_",j)]]$results$Effects[1,6])) + xlab(" "), width = len, height = len, y = (sides - ceiling(j/sides)) * len, x = ((j-1) %% sides) * len) 61 | } 62 | plot <- plot + ggtitle("DID from last period before treatment changes (t = 0) to t") + theme(plot.title = element_text(hjust = 0.5)) 63 | } 64 | return(plot) 65 | } 66 | 67 | #' Internal function to retrieve plot colors 68 | #' @param N Number of colors to retrieve 69 | #' @import ggplot2 70 | #' @importFrom grDevices colors 71 | #' @returns A list of colors. 72 | #' @noRd 73 | get_colors <- function(N) { 74 | must_color <- c(552, 26, 81, 68, 450, 640, 24, 498) 75 | # Indices of the following colors in ggplot's colors() 76 | # Red, blue, green, cyan, magenta, violet, black, orange 77 | other_color <- 1:657 78 | other_color <- other_color[!(other_color %in% must_color)] 79 | if (N > length(must_color)) { 80 | index_colors <- c(must_color, sample(other_color, N - length(must_color))) 81 | } else { 82 | index_colors <- must_color[1:N] 83 | } 84 | return(colors()[index_colors]) 85 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_by_path.R: -------------------------------------------------------------------------------- 1 | #' Internal function of did_multiplegt_dyn by treatment path 2 | #' @param df df 3 | #' @param by_path by_path 4 | #' @param outcome outcome 5 | #' @param group group 6 | #' @param time time 7 | #' @param treatment treatment 8 | #' @param effects effects 9 | #' @param placebo placebo 10 | #' @param ci_level ci_level 11 | #' @param switchers switchers 12 | #' @param trends_nonparam trends_nonparam 13 | #' @param weight weight 14 | #' @param controls controls 15 | #' @param dont_drop_larger_lower dont_drop_larger_lower 16 | #' @param drop_if_d_miss_before_first_switch drop_if_d_miss_before_first_switch 17 | #' @param cluster cluster 18 | #' @param same_switchers same_switchers 19 | #' @param same_switchers_pl same_switchers_pl 20 | #' @param effects_equal effects_equal 21 | #' @param save_results save_results 22 | #' @param normalized normalized 23 | #' @param predict_het predict_het 24 | #' @param trends_lin trends_lin 25 | #' @param less_conservative_se less_conservative_se 26 | #' @param continuous continuous 27 | #' @returns A dataframe with the by_path classifier 28 | #' @noRd 29 | 30 | did_multiplegt_by_path <- function( 31 | df, 32 | outcome, 33 | group, 34 | time, 35 | treatment, 36 | effects, 37 | placebo, 38 | ci_level, 39 | switchers, 40 | trends_nonparam, 41 | weight, 42 | controls, 43 | dont_drop_larger_lower, 44 | drop_if_d_miss_before_first_switch, 45 | cluster, 46 | same_switchers, 47 | same_switchers_pl, 48 | effects_equal, 49 | save_results, 50 | normalized, 51 | predict_het, 52 | trends_lin, 53 | less_conservative_se, 54 | continuous, 55 | by_path 56 | ) { 57 | 58 | data <- did_multiplegt_main(df = df, outcome = outcome, group = group, time = time, treatment = treatment, effects = effects, placebo = placebo, ci_level = ci_level,switchers = switchers, trends_nonparam = trends_nonparam, weight = weight, controls = controls, dont_drop_larger_lower = dont_drop_larger_lower, drop_if_d_miss_before_first_switch = drop_if_d_miss_before_first_switch, cluster = cluster, same_switchers = same_switchers, same_switchers_pl = same_switchers_pl, effects_equal = effects_equal, save_results = save_results, normalized = normalized, predict_het = predict_het, trends_lin = trends_lin, less_conservative_se = less_conservative_se, continuous = continuous, data_only = TRUE) 59 | class(data$df) <- "data.frame" 60 | 61 | design_base <- did_multiplegt_dyn_design(data = data, design_opt = list(1, "console"), weight = weight, by = NULL, by_index = "_no_by", append = FALSE) 62 | 63 | l_XX <- data$l_XX 64 | T_max_XX <- data$T_max_XX 65 | path_index <- data$df[c("group", "time", "time_XX", "treatment_XX", "F_g_XX")] 66 | data <- NULL 67 | if (by_path == -1) { 68 | by_path <- nrow(design_base$design_mat) 69 | } 70 | design_set <- design_base$design_mat[1:min(by_path, nrow(design_base$design_mat)), ] 71 | if (by_path > nrow(design_base$design_mat)) { 72 | message(sprintf("You requested %.0f treatment paths, but there are only %.0f paths in your data. The program will continue with the latter number of treatment paths.")) 73 | } 74 | path <- design_set[,3] 75 | for (j in 1:l_XX) { 76 | path <- paste0(path,",",design_set[,3+j]) 77 | } 78 | 79 | for (i in 0:l_XX) { 80 | path_index[[paste0("D_Fg",i)]] <- ifelse(path_index$time_XX == path_index$F_g_XX - 1 + i, path_index$treatment_XX, NA) 81 | } 82 | path_index$treatment_XX <- NULL 83 | for (j in 0:l_XX) { 84 | path_index <- path_index %>% group_by(.data$group) %>% 85 | mutate(!!paste0("D_fg",j) := mean(.data[[paste0("D_Fg",j)]],na.rm = TRUE)) %>% ungroup() 86 | path_index[[paste0("D_Fg",j)]] <- NULL 87 | } 88 | 89 | path_index$path <- path_index$D_fg0 90 | path_index$D_fg0 <- NULL 91 | for (j in 1:l_XX) { 92 | path_index$path <- ifelse(!is.na(path_index[[paste0("D_fg",j)]]), paste0(path_index$path,",",path_index[[paste0("D_fg",j)]]), path_index$path) 93 | path_index[[paste0("D_fg",j)]] <- NULL 94 | } 95 | path_index$yet_to_switch_XX <- as.numeric(path_index$time_XX < path_index$F_g_XX) 96 | path_index$time_XX <- path_index$F_g_XX <- NULL 97 | path_index$baseline_XX <- substr(path_index$path,1,1) 98 | names(path_index) <- c(group, time, "path_XX", "yet_to_switch_XX", "baseline_XX") 99 | 100 | df <- merge(df, path_index, by = c(group, time)) 101 | df <- df[order(df[[group]], df[[time]]), ] 102 | data <- list(df = df, path = path) 103 | return(data) 104 | } 105 | 106 | #' Auxiliary function to check if "," is in var 107 | #' @param str str 108 | #' @returns bool 109 | #' @noRd 110 | count_comma <- function(str) { 111 | tot = 0 112 | for (k in 1:nchar(str)) { 113 | if (substr(str,k,k) == ",") { 114 | tot = tot + 1 115 | } 116 | } 117 | return(tot) 118 | } -------------------------------------------------------------------------------- /R/R/did_multiplegt_dyn_graph.R: -------------------------------------------------------------------------------- 1 | #' Function for event study plot. the did_multiplegt_dyn command always generates a ggplot object that can be printed right after the end of the routine (graph_off = FALSE) or called afterwards from the global environment after assigning the did_multiplegt_dyn output to a variable. 2 | #' @param data data 3 | #' @param args args 4 | #' @import ggplot2 5 | #' @importFrom dplyr filter 6 | #' @returns A ggplot object. 7 | #' @noRd 8 | did_multiplegt_dyn_graph <- function(data, args = list()) { 9 | grmat <- rbind(cbind(data$Effects, 1:nrow(data$Effects)),cbind(data$ATE, 0)) 10 | if (!is.null(data$Placebos)) { 11 | grmat <- rbind(grmat, cbind(data$Placebos, (-1) * 1:nrow(data$Placebos))) 12 | } 13 | colnames(grmat)[ncol(grmat)] <- "Time" 14 | grmat[nrow(data$Effects) + 1, c(1,3,4)] <- 0 15 | grmat <- data.frame(grmat[, c(1,3,4,9)]) 16 | did_multiplegt_dyn_plot <- ggplot(grmat,aes(x = .data$Time, y = .data$Estimate, group = 1)) + 17 | geom_line(colour = "blue") + 18 | geom_errorbar(data = ~dplyr::filter(.x, grmat$Estimate != 0),aes(ymin = .data$LB.CI, ymax = .data$UB.CI), position=position_dodge(0.05), width = 0.2, colour = "red") + 19 | geom_point(colour = "blue") + 20 | ggtitle("DID, from last period before treatment changes (t=0) to t") + 21 | xlab("Relative time to last period before treatment changes (t=0)") + 22 | theme(plot.title = element_text(hjust = 0.5)) + theme_minimal_grid() 23 | 24 | for (layer in args) { 25 | did_multiplegt_dyn_plot <- did_multiplegt_dyn_plot + layer 26 | } 27 | 28 | return(did_multiplegt_dyn_plot) 29 | } 30 | 31 | 32 | #' Internal function of did_multiplegt_dyn to overlay plots 33 | #' @param obj A did_multiplegt_dyn object 34 | #' @import ggplot2 35 | #' @import cowplot 36 | #' @returns A ggplot object. 37 | #' @noRd 38 | combine_plot <- function(obj) { 39 | if (!is.null(obj$args[["by"]])) { 40 | color_set <- get_colors(length(obj$by_levels)) 41 | base_plot <- obj$by_level_1$plot 42 | plot <- ggplot(data = base_plot$data, aes(x = base_plot$data$Time, y = base_plot$data$Estimate)) + 43 | geom_point(colour = color_set[1]) + geom_line(aes(colour = paste0(obj$args$by," = ", obj$by_levels[1]))) + 44 | geom_errorbar(data = base_plot$data, aes(ymin = base_plot$data$LB.CI, ymax = base_plot$data$UB.CI), position=position_dodge(0.05), width = 0.2, colour = color_set[1]) 45 | if (length(obj$by_levels) > 1) { 46 | for (j in 2:length(obj$by_levels)) { 47 | add_plot <- obj[[paste0("by_level_",j)]][["plot"]] 48 | plot <- plot + 49 | geom_point(data = add_plot$data, aes_(x = add_plot$data$Time, y = add_plot$data$Estimate), colour = color_set[j]) + 50 | geom_errorbar(data = add_plot$data, aes_(ymin = add_plot$data$LB.CI, ymax = add_plot$data$UB.CI), position=position_dodge(0.05), width = 0.2, colour = color_set[j]) + 51 | geom_line(data = add_plot$data, aes_(x = add_plot$data$Time, y = add_plot$data$Estimate, 52 | colour = paste0(obj$args$by, " = ", obj$by_levels[j]))) 53 | } 54 | } 55 | plot <- plot + ylab("Estimate") + ggtitle("DID, from last period before treatment changes (t=0) to t") + 56 | xlab("Relative time to last period before treatment changes (t=0)") + 57 | theme(plot.title = element_text(hjust = 0.5), legend.position = "bottom") + 58 | scale_colour_manual("", breaks = paste0(obj$args$by, " = ", obj$by_levels), values = color_set) 59 | } else if (!is.null(obj$args[["by_path"]])) { 60 | if (length(obj$by_levels) > 100) { 61 | message("The command allows a maximum of 100 graphs to be combined in a 10 x 10 window. The resulting graph will be restricted to the first 100 treatment paths.") 62 | } 63 | sides <- ceiling(sqrt(length(obj$by_levels))); len <- 1/sides; 64 | plot <- ggdraw() 65 | for (j in 1:length(obj$by_levels)) { 66 | plot <- plot + draw_plot(obj[[paste0("by_level_",j)]]$plot + ggtitle(sprintf("Treatment path (%s); %.0f switchers", obj$by_levels[j], obj[[paste0("by_level_",j)]]$results$Effects[1,6])) + xlab(" "), width = len, height = len, y = (sides - ceiling(j/sides)) * len, x = ((j-1) %% sides) * len) 67 | } 68 | plot <- plot + ggtitle("DID from last period before treatment changes (t = 0) to t") + theme(plot.title = element_text(hjust = 0.5)) 69 | } 70 | return(plot) 71 | } 72 | 73 | #' Internal function to retrieve plot colors 74 | #' @param N Number of colors to retrieve 75 | #' @import ggplot2 76 | #' @importFrom grDevices colors 77 | #' @returns A list of colors. 78 | #' @noRd 79 | get_colors <- function(N) { 80 | must_color <- c(552, 26, 81, 68, 450, 640, 24, 498) 81 | # Indices of the following colors in ggplot's colors() 82 | # Red, blue, green, cyan, magenta, violet, black, orange 83 | other_color <- 1:657 84 | other_color <- other_color[!(other_color %in% must_color)] 85 | if (N > length(must_color)) { 86 | index_colors <- c(must_color, sample(other_color, N - length(must_color))) 87 | } else { 88 | index_colors <- must_color[1:N] 89 | } 90 | return(colors()[index_colors]) 91 | } -------------------------------------------------------------------------------- /R/R/did_multiplegt_dyn_dfs.R: -------------------------------------------------------------------------------- 1 | #' Option that produces a table showing the number of groups switching for the first time by period 2 | #' @param data data 3 | #' @param dfs dfs 4 | #' @param by by 5 | #' @param by_index by_index 6 | #' @param file file 7 | #' @import data.table 8 | #' @returns A list with the date_first_switch output. 9 | #' @noRd 10 | did_multiplegt_dyn_dfs <- function( 11 | data, 12 | dfs, 13 | by, 14 | by_index, 15 | file 16 | ) { 17 | 18 | # Inherited Globals # 19 | df <- data$df 20 | T_max_XX <- data$T_max_XX 21 | 22 | tot_s <- NULL 23 | time <- NULL 24 | 25 | suppressWarnings({ 26 | 27 | ## Fetch the arguments 28 | dfs_opt <- dfs[1] 29 | dfs_path <- dfs[2] 30 | 31 | ## Error message if the arguments in the option were specified wrong 32 | if (dfs_opt != "" & dfs_opt != "by_baseline_treat") { 33 | stop("Only option by_baseline_treat allowed.") 34 | } 35 | 36 | ## Drop non switchers and keep one observation per group 37 | df <- subset(df, !(df$F_g_XX == T_max_XX + 1 | is.na(df$F_g_XX))) 38 | df <- subset(df, df$time_XX == df$F_g_XX) 39 | df <- subset(df, select = c("group", "time", "F_g_XX", "d_sq_XX")) 40 | 41 | ## When by_baseline_treat is not specified 42 | if (dfs_opt == "") { 43 | ## collapse the number of groups by time 44 | df$tot_s <- 1 45 | df[, tot_s := sum(tot_s, na.rm = TRUE), by = time] 46 | df$group <- df$F_g_XX <- df$d_sq_XX <- NULL 47 | df <- unique(df) 48 | df <- df[order(df$time), ] 49 | ## generate the share of each group 50 | df$share_XX <- (df$tot_s / sum(df$tot_s, na.rm = TRUE)) * 100 51 | df <- subset(df, select = c("tot_s", "share_XX", "time")) 52 | ## make matrix with the number and share of groups by time 53 | dfsmat <- matrix(NA, ncol = 2, nrow = dim(df)[1]) 54 | rown <- c() 55 | coln <- c("N", "Share") 56 | df <- data.frame(df) 57 | for (j in 1:2) { 58 | for (i in 1:dim(df)[1]) { 59 | if (j == 1) { 60 | rown <- c(rown, df$time[i]) 61 | } 62 | dfsmat[i,j] <- as.numeric(df[i,j]) 63 | } 64 | } 65 | df <- data.table(df) 66 | colnames(dfsmat) <- coln 67 | rownames(dfsmat) <- rown 68 | dfsmat[, 2] <- sprintf("%s", format(round(dfsmat[,2], 2), big.mark=",", scientific=FALSE, trim=TRUE)) 69 | 70 | ## output as excel 71 | if (dfs_path != "console") { 72 | by_add <- "" 73 | if (by_index != "_no_by") { 74 | by_add <- paste0(", ",abbreviate(by,5), "=", by_index) 75 | } 76 | file[[paste0("Switch. Dates",by_add)]] <- as.data.frame(dfsmat) 77 | } 78 | 79 | res_dfs <- list( 80 | dfs_opt = dfs_opt, 81 | dfs_path = dfs_path, 82 | dfs_mat = noquote(dfsmat) 83 | ) 84 | } 85 | 86 | ## When by_baseline_treat is specified 87 | if (dfs_opt == "by_baseline_treat") { 88 | ## collapse, but this time by time and status quo treatment 89 | df$tot_s <- 1 90 | df[, tot_s := sum(tot_s, na.rm = TRUE), by = c("time", "d_sq_XX")] 91 | df$group <- df$F_g_XX <- NULL 92 | df <- unique(df) 93 | df <- df[order(df$d_sq_XX, df$time), ] 94 | levels_d_sq_XX <- levels(factor(df$d_sq_XX)) 95 | 96 | res_dfs <- list( 97 | dfs_opt = dfs_opt, 98 | dfs_path = dfs_path, 99 | levels_baseline_treat = length(levels_d_sq_XX) 100 | ) 101 | index <- 1 102 | by_add <- "" 103 | if (by_index != "_no_by") { 104 | by_add <- paste0(", ",abbreviate(by,5), "=", by_index) 105 | } 106 | for (l in levels_d_sq_XX) { 107 | df_by <- subset(df, df$d_sq_XX == l) 108 | df_by$share_XX <- (df_by$tot_s / sum(df_by$tot_s, na.rm = TRUE)) * 100 109 | df_by <- subset(df_by, select = c("tot_s", "share_XX", "time")) 110 | ## make matrix with the number and share of groups by time, one for each level of status quo treatment 111 | dfsmat <- matrix(NA, ncol = 2, nrow = dim(df_by)[1]) 112 | rown <- c() 113 | coln <- c("N", "Share") 114 | df_by <- data.frame(df_by) 115 | for (j in 1:2) { 116 | for (i in 1:dim(df_by)[1]) { 117 | if (j == 1) { 118 | rown <- c(rown, df_by$time[i]) 119 | } 120 | dfsmat[i,j] <- as.numeric(df_by[i,j]) 121 | } 122 | } 123 | df_by <- data.table(df_by) 124 | colnames(dfsmat) <- coln 125 | rownames(dfsmat) <- rown 126 | 127 | dfsmat[, 2] <- sprintf("%s", format(round(dfsmat[,2], 2), big.mark=",", scientific=FALSE, trim=TRUE)) 128 | 129 | res_dfs <- append(res_dfs, l) 130 | res_dfs <- append(res_dfs, list(noquote(dfsmat))) 131 | names(res_dfs)[length(res_dfs) - 1] <- paste0("level",index) 132 | names(res_dfs)[length(res_dfs)] <- paste0("dfs_mat",index) 133 | 134 | ## output as excel 135 | if (dfs_path != "console") { 136 | sheetn <- paste0("Base treat. ", l) 137 | file[[paste0(sheetn, by_add)]] <- as.data.frame(dfsmat) 138 | } 139 | index <- index + 1 140 | } 141 | } 142 | }) 143 | 144 | if (length(names(file)) > 0) { 145 | res_dfs$dfs_file <- file 146 | } 147 | return(res_dfs) 148 | } -------------------------------------------------------------------------------- /R/R/did_multiplegt_by_path.R: -------------------------------------------------------------------------------- 1 | #' Internal function of did_multiplegt_dyn by treatment path 2 | #' @param df df 3 | #' @param by_path by_path 4 | #' @param outcome outcome 5 | #' @param group group 6 | #' @param time time 7 | #' @param treatment treatment 8 | #' @param effects effects 9 | #' @param placebo placebo 10 | #' @param ci_level ci_level 11 | #' @param switchers switchers 12 | #' @param trends_nonparam trends_nonparam 13 | #' @param weight weight 14 | #' @param controls controls 15 | #' @param dont_drop_larger_lower dont_drop_larger_lower 16 | #' @param drop_if_d_miss_before_first_switch drop_if_d_miss_before_first_switch 17 | #' @param cluster cluster 18 | #' @param same_switchers same_switchers 19 | #' @param same_switchers_pl same_switchers_pl 20 | #' @param only_never_switchers only_never_switchers 21 | #' @param effects_equal effects_equal 22 | #' @param save_results save_results 23 | #' @param normalized normalized 24 | #' @param predict_het predict_het 25 | #' @param trends_lin trends_lin 26 | #' @param less_conservative_se less_conservative_se 27 | #' @param continuous continuous 28 | #' @returns A dataframe with the by_path classifier 29 | #' @import data.table 30 | #' @noRd 31 | 32 | did_multiplegt_by_path <- function( 33 | df, 34 | outcome, 35 | group, 36 | time, 37 | treatment, 38 | effects, 39 | placebo, 40 | ci_level, 41 | switchers, 42 | trends_nonparam, 43 | weight, 44 | controls, 45 | dont_drop_larger_lower, 46 | drop_if_d_miss_before_first_switch, 47 | cluster, 48 | same_switchers, 49 | same_switchers_pl, 50 | effects_equal, 51 | only_never_switchers, 52 | save_results, 53 | normalized, 54 | predict_het, 55 | trends_lin, 56 | less_conservative_se, 57 | continuous, 58 | by_path 59 | ) { 60 | 61 | data <- did_multiplegt_main(df = df, outcome = outcome, group = group, time = time, treatment = treatment, effects = effects, placebo = placebo, ci_level = ci_level,switchers = switchers, trends_nonparam = trends_nonparam, weight = weight, controls = controls, dont_drop_larger_lower = dont_drop_larger_lower, drop_if_d_miss_before_first_switch = drop_if_d_miss_before_first_switch, cluster = cluster, same_switchers = same_switchers, same_switchers_pl = same_switchers_pl, only_never_switchers = only_never_switchers, effects_equal = effects_equal, save_results = save_results, normalized = normalized, predict_het = predict_het, trends_lin = trends_lin, less_conservative_se = less_conservative_se, continuous = continuous, data_only = TRUE) 62 | 63 | design_base <- did_multiplegt_dyn_design(data = data, design_opt = list(1, "console"), weight = weight, by = NULL, by_index = "_no_by", file = NULL) 64 | 65 | path_index <- copy(data$df) 66 | l_XX <- data$l_XX 67 | T_max_XX <- data$T_max_XX 68 | path_index <- path_index[, c("group", "time", "time_XX", "treatment_XX", "F_g_XX"), with = TRUE] 69 | 70 | data <- NULL 71 | if (by_path == -1) { 72 | by_path <- nrow(design_base$design_mat) 73 | } 74 | design_set <- matrix(design_base$design_mat[1:min(by_path, nrow(design_base$design_mat)), ], ncol = ncol(design_base$design_mat), nrow = min(by_path, nrow(design_base$design_mat))) 75 | if (by_path > nrow(design_base$design_mat)) { 76 | message(sprintf("You requested %.0f treatment paths, but there are only %.0f paths in your data. The program will continue with the latter number of treatment paths.")) 77 | } 78 | path <- design_set[,3] 79 | for (j in 1:l_XX) { 80 | path <- paste0(path,",",design_set[,3+j]) 81 | } 82 | 83 | for (i in 0:l_XX) { 84 | path_index[[paste0("D_Fg",i)]] <- ifelse(path_index$time_XX == path_index$F_g_XX - 1 + i, path_index$treatment_XX, NA) 85 | } 86 | path_index$treatment_XX <- NULL 87 | for (j in 0:l_XX) { 88 | path_index[, paste0("D_fg",j) := mean(get(paste0("D_Fg",j)), na.rm = TRUE), by = c("group")] 89 | } 90 | 91 | for (j in 0:l_XX) { 92 | path_index[[paste0("D_Fg",j)]] <- NULL 93 | } 94 | 95 | path_index$path <- path_index$D_fg0 96 | path_index$D_fg0 <- NULL 97 | for (j in 1:l_XX) { 98 | path_index$path <- ifelse(!is.na(path_index[[paste0("D_fg",j)]]), paste0(path_index$path,",",path_index[[paste0("D_fg",j)]]), path_index$path) 99 | path_index[[paste0("D_fg",j)]] <- NULL 100 | } 101 | path_index$yet_to_switch_XX <- as.numeric(path_index$time_XX < path_index$F_g_XX) 102 | path_index$time_XX <- path_index$F_g_XX <- NULL 103 | path_index$baseline_XX <- substr(path_index$path,1,1) 104 | 105 | path_index <- data.table::setnames(path_index, old = c("group", "time", "path", "yet_to_switch_XX", "baseline_XX"), new = c(group, time, "path_XX", "yet_to_switch_XX", "baseline_XX")) 106 | df <- merge(df, path_index, by = c(group, time)) 107 | df <- df[order(df[[group]], df[[time]]), ] 108 | data <- list(df = df, path = path) 109 | return(data) 110 | } 111 | 112 | #' Auxiliary function to check if "," is in var 113 | #' @param str str 114 | #' @returns bool 115 | #' @noRd 116 | count_comma <- function(str) { 117 | tot = 0 118 | for (k in 1:nchar(str)) { 119 | if (substr(str,k,k) == ",") { 120 | tot = tot + 1 121 | } 122 | } 123 | return(tot) 124 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_dyn_dfs.R: -------------------------------------------------------------------------------- 1 | #' Option that produces a table showing the number of groups switching for the first time by period 2 | #' @param data data 3 | #' @param dfs dfs 4 | #' @param by by 5 | #' @param by_index by_index 6 | #' @param append append 7 | #' @import dplyr 8 | #' @importFrom magrittr %>% 9 | #' @importFrom rlang := 10 | #' @importFrom rlang .data 11 | #' @returns A list with the date_first_switch output. 12 | #' @noRd 13 | did_multiplegt_dyn_dfs <- function( 14 | data, 15 | dfs, 16 | by, 17 | by_index, 18 | append 19 | ) { 20 | 21 | # Inherited Globals # 22 | df <- data$df 23 | T_max_XX <- data$T_max_XX 24 | 25 | suppressWarnings({ 26 | 27 | ## Fetch the arguments 28 | dfs_opt <- dfs[1] 29 | dfs_path <- dfs[2] 30 | 31 | ## Error message if the arguments in the option were specified wrong 32 | if (dfs_opt != "" & dfs_opt != "by_baseline_treat") { 33 | stop("Only option by_baseline_treat allowed.") 34 | } 35 | 36 | ## Drop non switchers and keep one observation per group 37 | df <- subset(df, !(df$F_g_XX == T_max_XX + 1 | is.na(df$F_g_XX))) 38 | df <- subset(df, df$time_XX == df$F_g_XX) 39 | df <- df %>% dplyr::select(.data$group, .data$time, .data$F_g_XX, .data$d_sq_XX) 40 | 41 | ## When by_baseline_treat is not specified 42 | if (dfs_opt == "") { 43 | ## collapse the number of groups by time 44 | df$tot_s <- 1 45 | df <- df %>% group_by(.data$time) %>% 46 | mutate(tot_s = sum(.data$tot_s, na.rm = TRUE)) %>% 47 | dplyr::select(-.data$group, -.data$F_g_XX, -.data$d_sq_XX) 48 | df <- unique(df) 49 | df <- df[order(df$time), ] 50 | ## generate the share of each group 51 | df$share_XX <- (df$tot_s / sum(df$tot_s, na.rm = TRUE)) * 100 52 | df <- df[c("tot_s", "share_XX", "time")] 53 | ## make matrix with the number and share of groups by time 54 | dfsmat <- matrix(NA, ncol = 2, nrow = dim(df)[1]) 55 | rown <- c() 56 | coln <- c("N", "Share") 57 | for (j in 1:2) { 58 | for (i in 1:dim(df)[1]) { 59 | if (j == 1) { 60 | rown <- c(rown, df$time[i]) 61 | } 62 | dfsmat[i,j] <- as.numeric(df[i,j]) 63 | } 64 | } 65 | colnames(dfsmat) <- coln 66 | rownames(dfsmat) <- rown 67 | dfsmat[, 2] <- sprintf("%s", format(round(dfsmat[,2], 2), big.mark=",", scientific=FALSE, trim=TRUE)) 68 | 69 | ## output as excel 70 | if (dfs_path != "console") { 71 | by_add <- "" 72 | if (by_index != "_no_by") { 73 | by_add <- paste0(", ",abbreviate(by,5), "=", by_index) 74 | } 75 | message("Save to excel option shut down due to dependencies issues (library 'xlsx'). See the Disclaimer section of the manual for further information.") 76 | #write.xlsx(dfsmat, dfs_path, row.names = TRUE, col.names = TRUE, 77 | #sheetName = paste0("Switch. Dates",by_add), append = append) 78 | } 79 | 80 | res_dfs <- list( 81 | dfs_opt = dfs_opt, 82 | dfs_path = dfs_path, 83 | dfs_mat = noquote(dfsmat) 84 | ) 85 | } 86 | 87 | ## When by_baseline_treat is specified 88 | if (dfs_opt == "by_baseline_treat") { 89 | ## collapse, but this time by time and status quo treatment 90 | df$tot_s <- 1 91 | df <- df %>% group_by(.data$time, .data$d_sq_XX) %>% 92 | mutate(tot_s = sum(.data$tot_s, na.rm = TRUE)) %>% 93 | dplyr::select(-.data$group, -.data$F_g_XX) 94 | df <- unique(df) 95 | df <- df[order(df$d_sq_XX, df$time), ] 96 | levels_d_sq_XX <- levels(factor(df$d_sq_XX)) 97 | 98 | res_dfs <- list( 99 | dfs_opt = dfs_opt, 100 | dfs_path = dfs_path, 101 | levels_baseline_treat = length(levels_d_sq_XX) 102 | ) 103 | index <- 1 104 | by_add <- "" 105 | if (by_index != "_no_by") { 106 | by_add <- paste0(", ",abbreviate(by,5), "=", by_index) 107 | } 108 | for (l in levels_d_sq_XX) { 109 | df_by <- subset(df, df$d_sq_XX == l) 110 | df_by$share_XX <- (df_by$tot_s / sum(df_by$tot_s, na.rm = TRUE)) * 100 111 | df_by <- df_by[c("tot_s", "share_XX", "time")] 112 | ## make matrix with the number and share of groups by time, one for each level of status quo treatment 113 | dfsmat <- matrix(NA, ncol = 2, nrow = dim(df_by)[1]) 114 | rown <- c() 115 | coln <- c("N", "Share") 116 | for (j in 1:2) { 117 | for (i in 1:dim(df_by)[1]) { 118 | if (j == 1) { 119 | rown <- c(rown, df_by$time[i]) 120 | } 121 | dfsmat[i,j] <- as.numeric(df_by[i,j]) 122 | } 123 | } 124 | colnames(dfsmat) <- coln 125 | rownames(dfsmat) <- rown 126 | 127 | dfsmat[, 2] <- sprintf("%s", format(round(dfsmat[,2], 2), big.mark=",", scientific=FALSE, trim=TRUE)) 128 | 129 | res_dfs <- append(res_dfs, l) 130 | res_dfs <- append(res_dfs, list(noquote(dfsmat))) 131 | names(res_dfs)[length(res_dfs) - 1] <- paste0("level",index) 132 | names(res_dfs)[length(res_dfs)] <- paste0("dfs_mat",index) 133 | 134 | ## output as excel 135 | if (dfs_path != "console") { 136 | sheetn <- paste0("Base treat. ", l) 137 | message("Save to excel option shut down due to dependencies issues (library 'xlsx'). See the Disclaimer section of the manual for further information.") 138 | #write.xlsx(dfsmat, dfs_path, row.names = TRUE, col.names = TRUE, sheetName = paste0(sheetn, by_add), append = append) 139 | append <- TRUE 140 | } 141 | index <- index + 1 142 | } 143 | } 144 | }) 145 | return(res_dfs) 146 | } -------------------------------------------------------------------------------- /R/R/did_multiplegt_dyn_design.R: -------------------------------------------------------------------------------- 1 | #' Option that shows the different treatment paths that switcher groups follow 2 | #' @param data data 3 | #' @param design_opt design_opt 4 | #' @param weight weight 5 | #' @param by by 6 | #' @param by_index by_index 7 | #' @param file file 8 | #' @import data.table 9 | #' @returns A list with the design option output. 10 | #' @noRd 11 | did_multiplegt_dyn_design <- function( 12 | data, 13 | design_opt, 14 | weight, 15 | by, 16 | by_index, 17 | file 18 | ) { 19 | 20 | # Inherited Globals # 21 | df <- data$df 22 | l_XX <- data$l_XX 23 | T_max_XX <- data$T_max_XX 24 | 25 | time_l_XX <- NULL 26 | group_XX <- NULL 27 | weight_XX <- NULL 28 | treatment_XX <- NULL 29 | N_XX <- NULL 30 | N_w_XX <- NULL 31 | treat_GRP <- NULL 32 | id_XX <- NULL 33 | in_table_XX <- NULL 34 | g_weight_XX <- NULL 35 | 36 | ## Error message if the arguments in the option were specified wrong 37 | suppressWarnings({ 38 | 39 | ## Fetch the arguments 40 | des_p <- as.numeric(design_opt[1]) 41 | des_path <- design_opt[2] 42 | des_n <- l_XX 43 | des_per <- des_p * 100 44 | 45 | ## keep periods up to ℓ periods after the first switch 46 | df$F_g_plus_n_XX <- df$F_g_XX + des_n - 1 47 | df$sel_XX <- df$time_XX >= df$F_g_XX - 1 & df$time_XX <= df$F_g_plus_n_XX 48 | df <- subset(df, df$time_XX >= df$F_g_XX - 1 & df$time_XX <= df$F_g_plus_n_XX) 49 | df <- df[order(df$group_XX, df$time_XX), ] 50 | df[, time_l_XX := seq_len(.N), by = group_XX] 51 | df <- subset(df, select = c("group_XX", "time_l_XX", "weight_XX", "treatment_XX", "F_g_XX")) 52 | 53 | ## Aggregate weights by group 54 | if (!is.null(weight)) { 55 | df[, g_weight_XX := sum(weight_XX, na.rm = TRUE), by = group_XX] 56 | } else { 57 | df$g_weight_XX <- 1 58 | } 59 | df$weight_XX <- NULL 60 | 61 | max_time <- max(df$time_l_XX, na.rm = TRUE) 62 | treat_list <- c() 63 | treat_str <- "" 64 | for (i in 1:max_time) { 65 | df[, paste0("treatment_XX",i) := mean(treatment_XX[time_l_XX == i]), by = group_XX] 66 | treat_list <- c(treat_list, paste0("treatment_XX",i)) 67 | treat_str <- paste0(treat_str,"treatment_XX",i,",") 68 | } 69 | treat_str <- substr(treat_str, 1, nchar(treat_str) - 1) 70 | df$time_l_XX <- df$treatment_XX <- NULL 71 | df <- unique(df) 72 | 73 | ## Drop missing treatments 74 | for (var in treat_list) { 75 | df <- subset(df, !is.na(df[[var]])) 76 | } 77 | 78 | ## Creating varibale to store number of groups per treatment path and collapsing 79 | df$N_XX <- 1 80 | df$N_w_XX <- (df$g_weight_XX * df$N_XX) / sum(df$g_weight_XX, na.rm = TRUE) 81 | df$group_XX <- df$g_weight_XX <- NULL 82 | df[, N_XX := sum(N_XX, na.rm = TRUE), by = treat_list] 83 | df[, N_w_XX := sum(N_w_XX, na.rm = TRUE), by = treat_list] 84 | df$F_g_XX <- NULL 85 | df <- unique(df) 86 | tot_switch <- sum(df$N_XX, na.rm = TRUE) 87 | 88 | ## Keep the observations amounting to p% of the detected treatment paths 89 | df$neg_N_XX <- - df$N_XX 90 | df[, treat_GRP := .GRP, by = c(treat_list)] 91 | df <- df[order(df$neg_N_XX, df$treat_GRP), ] 92 | df$neg_N_XX <- df$treat_GRP <- NULL 93 | df$cum_sum_XX <- cumsum(df$N_w_XX) 94 | df$in_table_XX <- as.numeric(df$cum_sum_XX <= des_p) 95 | df <- df[order(df$in_table_XX, df$cum_sum_XX), ] 96 | df[, id_XX := seq_len(.N), by = in_table_XX] 97 | 98 | ## Keep all observations up to the first exceeding the p% 99 | df <- subset(df, df$in_table_XX == 1 | (df$in_table_XX == 0 & df$id_XX == 1)) 100 | 101 | ## Store the final % of groups included by the design option 102 | if (des_p < 1) { 103 | last_p <- 100 * min(df$cum_sum_XX[df$in_table_XX == 0]) 104 | } else { 105 | last_p <- 100 106 | } 107 | df$neg_N_XX <- - df$N_XX 108 | df[, treat_GRP := .GRP, by = c(treat_list)] 109 | df <- df[order(df$neg_N_XX, df$treat_GRP), ] 110 | df <- subset(df, select = c("N_XX", "N_w_XX", treat_list)) 111 | df$N_w_XX <- df$N_w_XX * 100 112 | 113 | ## Prepare matrix for the output table 114 | coln <- c("N", "Share") 115 | rown <- c() 116 | desmat <- matrix(NA, nrow = dim(df)[1], ncol = 2 + 1 + l_XX) 117 | 118 | ## Generate the column/row names and fill treatment path 119 | df <- data.frame(df) 120 | for (j in 1:(2 + 1 + l_XX)) { 121 | for (i in 1:dim(df)[1]) { 122 | if (j == 1) { 123 | rown <- c(rown, paste0("TreatPath",i)) 124 | } 125 | desmat[i,j] <- as.numeric(df[i,j]) 126 | } 127 | if (j > 2) { 128 | coln <- c(coln, paste0("\U2113","=",j - 2 - 1)) 129 | } 130 | } 131 | df <- data.table(df) 132 | colnames(desmat) <- coln 133 | rownames(desmat) <- rown 134 | 135 | desmat[, 2] <- noquote(sprintf("%s", format(round(desmat[,2], 2), big.mark=",", scientific=FALSE, trim=TRUE))) 136 | des_const <- c(l_XX, des_per, tot_switch, last_p) 137 | names(des_const) <- c("effects", "coverage_opt", "switchers", "detected_coverage") 138 | 139 | ## Save output as xlsx 140 | if (des_path != "console") { 141 | by_add <- "" 142 | if (by_index != "_no_by") { 143 | by_add <- paste0(", ",abbreviate(by,5), "=", by_index) 144 | } 145 | file[[paste0("Design",by_add)]] <- as.data.frame(desmat) 146 | } 147 | 148 | design <- list( 149 | design_path = des_path, 150 | design_mat = noquote(desmat), 151 | design_const = des_const, 152 | design_file = file 153 | ) 154 | return(design) 155 | }) 156 | } -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_dyn_design.R: -------------------------------------------------------------------------------- 1 | #' Option that shows the different treatment paths that switcher groups follow 2 | #' @param data data 3 | #' @param design_opt design_opt 4 | #' @param weight weight 5 | #' @param by by 6 | #' @param by_index by_index 7 | #' @param append append 8 | #' @import dplyr 9 | #' @importFrom magrittr %>% 10 | #' @importFrom rlang := 11 | #' @importFrom rlang .data 12 | #' @returns A list with the design option output. 13 | #' @noRd 14 | did_multiplegt_dyn_design <- function( 15 | data, 16 | design_opt, 17 | weight, 18 | by, 19 | by_index, 20 | append 21 | ) { 22 | 23 | # Inherited Globals # 24 | df <- data$df 25 | l_XX <- data$l_XX 26 | T_max_XX <- data$T_max_XX 27 | 28 | ## Error message if the arguments in the option were specified wrong 29 | suppressWarnings({ 30 | 31 | ## Fetch the arguments 32 | des_p <- as.numeric(design_opt[1]) 33 | des_path <- design_opt[2] 34 | des_n <- l_XX 35 | des_per <- des_p * 100 36 | 37 | ## keep periods up to ℓ periods after the first switch 38 | df$F_g_plus_n_XX <- df$F_g_XX + des_n - 1 39 | df$sel_XX <- df$time_XX >= df$F_g_XX - 1 & df$time_XX <= df$F_g_plus_n_XX 40 | df <- subset(df, df$time_XX >= df$F_g_XX - 1 & df$time_XX <= df$F_g_plus_n_XX) 41 | df <- df[order(df$group_XX, df$time_XX), ] 42 | df <- df %>% group_by(.data$group_XX) %>% 43 | mutate(time_l_XX = row_number()) %>% ungroup() 44 | df <- df %>% dplyr::select(.data$group_XX, .data$time_l_XX, .data$weight_XX, .data$treatment_XX, .data$F_g_XX) 45 | 46 | ## Aggregate weights by group 47 | if (!is.null(weight)) { 48 | df <- df %>% group_by(.data$group_XX) %>% 49 | mutate(g_weight = sum(.data$weight_XX), na.rm = TRUE) 50 | } else { 51 | df$g_weight_XX <- 1 52 | } 53 | df <- df %>% dplyr::select(-.data$weight_XX) 54 | 55 | max_time <- max(df$time_l_XX, na.rm = TRUE) 56 | treat_list <- c() 57 | treat_str <- "" 58 | for (i in 1:max_time) { 59 | df <- df %>% group_by(.data$group_XX) %>% 60 | mutate(!!paste0("treatment_XX",i) := mean(.data$treatment_XX[.data$time_l_XX == i])) %>% 61 | ungroup() 62 | treat_list <- c(treat_list, paste0("treatment_XX",i)) 63 | treat_str <- paste0(treat_str,"treatment_XX",i,",") 64 | } 65 | treat_str <- substr(treat_str, 1, nchar(treat_str) - 1) 66 | df <- df %>% dplyr::select(-.data$time_l_XX, -.data$treatment_XX) 67 | df <- unique(df) 68 | 69 | ## Drop missing treatments 70 | for (var in treat_list) { 71 | df <- subset(df, !is.na(df[[var]])) 72 | } 73 | 74 | ## Creating varibale to store number of groups per treatment path and collapsing 75 | df$N_XX <- 1 76 | df$N_w_XX <- (df$g_weight_XX * df$N_XX) / sum(df$g_weight_XX, na.rm = TRUE) 77 | df <- df %>% dplyr::select(-.data$group_XX, -.data$g_weight_XX) 78 | df$treatments <- df[treat_list] 79 | df <- df %>% group_by(.data$treatments) %>% 80 | mutate(N_XX = sum(.data$N_XX, na.rm = TRUE)) %>% 81 | mutate(N_w_XX = sum(.data$N_w_XX, na.rm = TRUE)) 82 | df <- df %>% dplyr::select(-.data$F_g_XX) 83 | df <- unique(df) 84 | tot_switch <- sum(df$N_XX, na.rm = TRUE) 85 | 86 | ## Keep the observations amounting to p% of the detected treatment paths 87 | df <- df %>% dplyr::arrange(-.data$N_XX, .data$treatments) 88 | df$cum_sum_XX <- cumsum(df$N_w_XX) 89 | df$in_table_XX <- as.numeric(df$cum_sum_XX <= des_p) 90 | df <- df[order(df$in_table_XX, df$cum_sum_XX), ] 91 | df <- df %>% group_by(.data$in_table_XX) %>% mutate(id_XX = row_number()) 92 | 93 | ## Keep all observations up to the first exceeding the p% 94 | df <- subset(df, df$in_table_XX == 1 | (df$in_table_XX == 0 & df$id_XX == 1)) 95 | 96 | ## Store the final % of groups included by the design option 97 | if (des_p < 1) { 98 | last_p <- 100 * min(df$cum_sum_XX[df$in_table_XX == 0]) 99 | } else { 100 | last_p <- 100 101 | } 102 | df <- df %>% dplyr::arrange(-.data$N_XX, .data$treatments) 103 | df <- df[c("N_XX", "N_w_XX", treat_list)] 104 | df$N_w_XX <- df$N_w_XX * 100 105 | 106 | ## Prepare matrix for the output table 107 | coln <- c("N", "Share") 108 | rown <- c() 109 | desmat <- matrix(NA, nrow = dim(df)[1], ncol = 2 + 1 + l_XX) 110 | 111 | ## Generate the column/row names and fill treatment path 112 | for (j in 1:(2 + 1 + l_XX)) { 113 | for (i in 1:dim(df)[1]) { 114 | if (j == 1) { 115 | rown <- c(rown, paste0("TreatPath",i)) 116 | } 117 | desmat[i,j] <- as.numeric(df[i,j]) 118 | } 119 | if (j > 2) { 120 | coln <- c(coln, paste0("\U2113","=",j - 2 - 1)) 121 | } 122 | } 123 | colnames(desmat) <- coln 124 | rownames(desmat) <- rown 125 | 126 | desmat[, 2] <- noquote(sprintf("%s", format(round(desmat[,2], 2), big.mark=",", scientific=FALSE, trim=TRUE))) 127 | des_const <- c(l_XX, des_per, tot_switch, last_p) 128 | names(des_const) <- c("effects", "coverage_opt", "switchers", "detected_coverage") 129 | 130 | ## Save output as xlsx 131 | if (des_path != "console") { 132 | by_add <- "" 133 | if (by_index != "_no_by") { 134 | by_add <- paste0(", ",abbreviate(by,5), "=", by_index) 135 | } 136 | message("Save to excel option shut down due to dependencies issues (library 'xlsx'). See the Disclaimer section of the manual for further information.") 137 | #write.xlsx(desmat, des_path, row.names = TRUE, col.names = TRUE, 138 | #sheetName = paste0("Design",by_add), append = append) 139 | } 140 | 141 | design <- list( 142 | design_path = des_path, 143 | design_mat = noquote(desmat), 144 | design_const = des_const 145 | ) 146 | return(design) 147 | }) 148 | } -------------------------------------------------------------------------------- /vignettes/vignette_2.md: -------------------------------------------------------------------------------- 1 | # did_multiplegt_dyn and esttab 2 | 3 | **esttab** is one of the most used Stata packages for retrieving estimation results as formatted tables. In the light of many users' requests, we have made **`did_multiplegt_dyn compatible with esttab`**. This vignette showcases how to use **esttab** in combination with **did_multiplegt_dyn** to save your results in an external file. **esttab** allows for many formats (TeX, html, ...) which can also be accessed with a **did_multiplegt_dyn** output. In this tutorial, we will only focus on LaTeX tabulars. 4 | 5 | + [Setup](#setup) 6 | + [Integration with esttab](#integration-with-esttab) 7 | - [General use](#general-use) 8 | - [Formatting](#formatting) 9 | 10 | ## Setup 11 | 12 | We test **esttab** via a DGP with 1000 groups and 20 periods. The treatment $D_{g,t}$ is randomly drawn from $\lbrace 0,1\rbrace$ at each $(g,t)$ cell. The outcome $Y_{g,t}$ is a function of $D_{g,t}$ and a time-dependent covariate $X_{g,t}$. We also generate two group-specific variables, $H^1_g$ and $H^2_g$, to test the output of the integration when **did_multiplegt_dyn** is run with the predict_het option. 13 | 14 | ```applescript 15 | clear 16 | set seed 123 17 | scalar TT = 20 18 | scalar GG = 1000 19 | set obs `= TT * GG' 20 | gen G = mod(_n-1,GG) + 1 21 | gen T = floor((_n-1)/GG) 22 | sort G T 23 | gen D = uniform() > 0.5 24 | gen X = uniform() * T 25 | gen Y = uniform() * (1 + D + X) 26 | gen H1 = G/GG 27 | gen H2 = mod(G, TT) 28 | ``` 29 | 30 | ## Integration with esttab 31 | 32 | ### General use 33 | The normal use of **esttab** requires saving the estimation model (plus eventual scalars/locals) with **estimates store** and then runnig **esttab** with the models' names as arguments. Let's test these basic features with a **did_multiplegt_dyn** model. We will run two specifications: 34 | 1. a model with 5 dynamic effects, where we also test for the equality of the event study estimates; 35 | 2. the same model, but we also estimate 3 placebos, control for $X_{g,t}$ and test for the joint significance of the placebos. 36 | 37 | This setting allows us to showcase how to retrieve the estimation results and add scalars/locals from **did_multiplegt_dyn** to **esttab**. 38 | 39 | First, we run **did_multiplegt_dyn** with the specifications above and save the results with **est sto** and the scalars/locals with **estadd**. 40 | 41 | ```applescript 42 | est clear 43 | did_multiplegt_dyn Y G T D, effects(5) graph_off effects_equal 44 | estadd scalar p_joint = e(p_equality_effects) 45 | estadd local controls = "No" 46 | est sto model_1 47 | 48 | did_multiplegt_dyn Y G T D, effects(5) placebo(3) effects_equal controls(X) graph_off 49 | estadd scalar p_joint = e(p_equality_effects) 50 | estadd scalar p_placebo = e(p_jointplacebo) 51 | estadd local controls = "Yes" 52 | est sto model_2 53 | ``` 54 | 55 | Then, we use **esttab** to save the results in a TeX tabular: 56 | ```applescript 57 | esttab model_* using "filename.tex", replace booktabs se s(p_joint p_placebo controls) 58 | ``` 59 | 60 | The resulting table should look like this: 61 |

62 | 63 |

64 | 65 | The code above is sufficient to save **did_multiplegt_dyn** output virtually with any option set, except with the **by()** or **by_path()** options, since, by design, the program will return an e(V) and e(b) only for the last level of the *by* variable. 66 | 67 | A special case occurs with the **predict_het()** option, since the program outputs also the results from regressing the group-level estimates of the event study effects on the variables specified as the option argument. For instance, we can run the following model specification and save the results with **esttab**: 68 | 69 | ```applescript 70 | est clear 71 | did_multiplegt_dyn Y G T D, predict_het(H1 H2, all) graph_off effects(3) 72 | est sto model_1 73 | esttab model_* using "filename.tex", replace booktabs se noobs 74 | ``` 75 | 76 | In this case, the resulting **esttab** table will be partitioned as follows: 77 |

78 | 79 |

80 | 81 | The first equation box (labeled after the outcome variable) contains the main results from **did_multiplegt_dyn**, while the equation boxes below show the output from **predict_het()**. 82 | 83 | ### Formatting 84 | 85 | **esttab** allows for a vast range of customisation. In this last subsection, we show how to improve the formatting of the first table in this tutorial. Specifically, we will change 86 | + the scalar/local labels, via `s(..., label(...))`; 87 | + the format of the floats, via `b(fmt)`; 88 | + the coefficient labels, as to mirror the notation from de Chaisemartin and D'Haultfoeuille (2024), via `coeflabels(coef "label")`; 89 | + Stata to LaTeX syntax, via `substitute(\_ _)`; 90 | + the space between the column number and the first equation line, via `mlabels(,none) collabels(,none)`. 91 | 92 | Run again the first code block from the [previous subsection](#integration-with-esttab). Then, run the following: 93 | 94 | ``` 95 | esttab model_* using "filename.tex", replace booktabs se s(control p_joint p_placebo, label("Controls" "Joint Eq. Effects" "Joint Sig. Placebo")) b(%9.5fc) coeflabels(Effect_1 "$\hat{\delta}_1$" Effect_2 "$\hat{\delta}_2$" Effect_3 "$\hat{\delta}_3$" Effect_4 "$\hat{\delta}_4$" Effect_5 "$\hat{\delta}_5$" Avg_Tot_Effect "$\hat{\delta}$" Placebo_1 "$\hat{\delta}_1^{pl}$" Placebo_2 "$\hat{\delta}_2^{pl}$" Placebo_3 "$\hat{\delta}_3^{pl}$") substitute(\_ _) mlabels(,none) collabels(,none) 96 | ``` 97 | 98 | The code above yields this table: 99 |

100 | 101 |

102 | 103 | --- 104 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/did_multiplegt_bootstrap.R: -------------------------------------------------------------------------------- 1 | #' Internal function of did_multiplegt_dyn - bootstrap se 2 | #' @param df df 3 | #' @param outcome outcome 4 | #' @param group group 5 | #' @param time time 6 | #' @param treatment treatment 7 | #' @param effects effects 8 | #' @param placebo placebo 9 | #' @param ci_level ci_level 10 | #' @param switchers switchers 11 | #' @param trends_nonparam trends_nonparam 12 | #' @param weight weight 13 | #' @param controls controls 14 | #' @param dont_drop_larger_lower dont_drop_larger_lower 15 | #' @param drop_if_d_miss_before_first_switch drop_if_d_miss_before_first_switch 16 | #' @param cluster cluster 17 | #' @param same_switchers same_switchers 18 | #' @param same_switchers_pl same_switchers_pl 19 | #' @param effects_equal effects_equal 20 | #' @param save_results save_results 21 | #' @param normalized normalized 22 | #' @param predict_het predict_het 23 | #' @param trends_lin trends_lin 24 | #' @param less_conservative_se less_conservative_se 25 | #' @param continuous continuous 26 | #' @param bootstrap bootstrap 27 | #' @param base base 28 | #' @returns A list of the final results updated with the bootstrap standard errors 29 | #' @noRd 30 | did_multiplegt_bootstrap <- function( 31 | df, 32 | outcome, 33 | group, 34 | time, 35 | treatment, 36 | effects, 37 | placebo, 38 | ci_level, 39 | switchers, 40 | trends_nonparam, 41 | weight, 42 | controls, 43 | dont_drop_larger_lower, 44 | drop_if_d_miss_before_first_switch, 45 | cluster, 46 | same_switchers, 47 | same_switchers_pl, 48 | effects_equal, 49 | save_results, 50 | normalized, 51 | predict_het, 52 | trends_lin, 53 | less_conservative_se, 54 | continuous, 55 | bootstrap, 56 | base 57 | ){ 58 | 59 | bresults_effects <- NULL 60 | bresults_ATE <- NULL 61 | bresults_placebo <- NULL 62 | 63 | n_effects <- nrow(base$Effects) 64 | bresults_effects <- matrix(NA, nrow = bootstrap, ncol = n_effects) 65 | if (isFALSE(trends_lin)) { 66 | bresults_ATE <- matrix(NA, nrow = bootstrap, ncol = 1) 67 | } 68 | if (placebo > 0) { 69 | n_placebo <- nrow(base$Placebos) 70 | bresults_placebo <- matrix(NA, nrow = bootstrap, ncol = n_placebo) 71 | } 72 | 73 | bs_group <- ifelse(!is.null(cluster), cluster, group) 74 | 75 | bdf <- data.frame(cbind(df[[bs_group]], 1:nrow(df))) 76 | colnames(bdf) <- c(bs_group, "id") 77 | xtset <- list() 78 | for (l in levels(factor(bdf[[bs_group]]))) { 79 | xtset[[l]] <- subset(bdf, bdf[[bs_group]] == l)[["id"]] 80 | } 81 | 82 | for (j in 1:bootstrap) { 83 | df_boot <- df[list_to_vec(xtset[sample(1:length(xtset), size = length(xtset), replace = TRUE)]), ] 84 | df_boot <- df_boot[order(df_boot[[group]], df_boot[[time]]), ] 85 | rownames(df_boot) <- 1:nrow(df_boot) 86 | suppressMessages({ 87 | df_est <- did_multiplegt_main(df = df_boot, outcome = outcome, group = group, time = time, treatment = treatment, effects = effects, placebo = placebo, ci_level = ci_level,switchers = switchers, trends_nonparam = trends_nonparam, weight = weight, controls = controls, dont_drop_larger_lower = dont_drop_larger_lower, drop_if_d_miss_before_first_switch = drop_if_d_miss_before_first_switch, cluster = cluster, same_switchers = same_switchers, same_switchers_pl = same_switchers_pl, effects_equal = effects_equal, save_results = save_results, normalized = normalized, predict_het = predict_het, trends_lin = trends_lin, less_conservative_se = less_conservative_se, continuous = continuous)}) 88 | 89 | res <- df_est$did_multiplegt_dyn 90 | for (i in 1:ncol(bresults_effects)) { 91 | if (i <= nrow(res$Effects)) { 92 | bresults_effects[j,i] <- res$Effects[i,1] 93 | } 94 | } 95 | 96 | if (!is.null(bresults_ATE)) { 97 | if (!is.null(res$ATE[1])) { 98 | bresults_ATE[j,1] <- res$ATE[1] 99 | } 100 | } 101 | 102 | if (!is.null(bresults_placebo)) { 103 | for (i in 1:ncol(bresults_placebo)) { 104 | if (i <= nrow(res$Placebos)) { 105 | bresults_placebo[j,i] <- res$Placebos[i,1] 106 | } 107 | } 108 | } 109 | res <- df_est <- NULL 110 | progressBar(j, bootstrap) 111 | } 112 | 113 | ci_level <- ci_level / 100 114 | z_level <- qnorm(ci_level + (1 - ci_level)/2) 115 | 116 | for (i in 1:ncol(bresults_effects)) { 117 | if (!is.null(base$Effects[i,1])) { 118 | base$Effects[i,2] <- sd(bresults_effects[,i], na.rm = TRUE) 119 | base$Effects[i,3] <- base$Effects[i,1] - z_level * base$Effects[i,2] 120 | base$Effects[i,4] <- base$Effects[i,1] + z_level * base$Effects[i,2] 121 | } 122 | } 123 | if (nrow(base$Effects) == 1) { 124 | class(base$Effects) <- "numeric" 125 | } 126 | 127 | if (!is.null(bresults_ATE)) { 128 | if (!is.null(base$ATE[1])) { 129 | base$ATE[2] <- sd(bresults_ATE, na.rm = TRUE) 130 | base$ATE[3] <- base$ATE[1] - z_level * base$ATE[2] 131 | base$ATE[4] <- base$ATE[1] + z_level * base$ATE[2] 132 | } 133 | } 134 | 135 | if (!is.null(bresults_placebo)) { 136 | for (i in 1:ncol(bresults_placebo)) { 137 | if (!is.null(base$Placebos[i,1])) { 138 | base$Placebos[i,2] <- sd(bresults_placebo[,i], na.rm = TRUE) 139 | base$Placebos[i,3] <- base$Placebos[i,1] - z_level * base$Placebos[i,2] 140 | base$Placebos[i,4] <- base$Placebos[i,1] + z_level * base$Placebos[i,2] 141 | } 142 | } 143 | if (nrow(base$Placebos) == 1) { 144 | class(base$Placebos) <- "numeric" 145 | } 146 | } 147 | return(base) 148 | } 149 | 150 | #' Internal function to convert lists to vectors 151 | #' @param lis A list 152 | #' @returns A vector 153 | #' @noRd 154 | list_to_vec <- function(lis) { 155 | vec <- c() 156 | for (j in 1:length(lis)) { 157 | for (i in 1:length(lis[[j]])) { 158 | vec <- c(vec, lis[[j]][[i]]) 159 | } 160 | } 161 | return(vec) 162 | } 163 | -------------------------------------------------------------------------------- /vignettes/assets/reg1.log: -------------------------------------------------------------------------------- 1 | This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023) (preloaded format=pdflatex 2023.12.2) 13 MAY 2024 15:43 2 | entering extended mode 3 | restricted \write18 enabled. 4 | file:line:error style messages enabled. 5 | %&-line parsing enabled. 6 | **"c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1.tex" 7 | (c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1.tex 8 | LaTeX2e <2023-11-01> 9 | L3 programming layer <2023-11-09> 10 | (c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 11 | Document Class: standalone 2022/10/10 v1.3b Class to compile TeX sub-files standalone 12 | (c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 13 | Package: shellesc 2023/07/08 v1.0d unified shell escape interface for LaTeX 14 | Package shellesc Info: Restricted shell escape enabled on input line 77. 15 | ) (c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 16 | Package: ifluatex 2019/10/25 v1.5 ifluatex legacy package. Use iftex instead. 17 | (c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 18 | Package: iftex 2022/02/03 v1.0f TeX engine tests 19 | )) (c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 20 | Package: xkeyval 2022/06/16 v2.9 package option processing (HA) 21 | (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex 22 | \XKV@toks=\toks17 23 | \XKV@tempa@toks=\toks18 24 | (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex)) 25 | \XKV@depth=\count186 26 | File: xkeyval.tex 2014/12/03 v2.7a key=value parser (HA) 27 | )) 28 | \sa@internal=\count187 29 | \c@sapage=\count188 30 | (c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 31 | File: standalone.cfg 2022/10/10 v1.3b Default configuration file for 'standalone' class 32 | ) (c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 33 | Document Class: article 2023/05/17 v1.4n Standard LaTeX document class 34 | (c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 35 | File: size10.clo 2023/05/17 v1.4n Standard LaTeX file (size option) 36 | ) 37 | \c@part=\count189 38 | \c@section=\count190 39 | \c@subsection=\count191 40 | \c@subsubsection=\count192 41 | \c@paragraph=\count193 42 | \c@subparagraph=\count194 43 | \c@figure=\count195 44 | \c@table=\count196 45 | \abovecaptionskip=\skip48 46 | \belowcaptionskip=\skip49 47 | \bibindent=\dimen140 48 | ) (c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 49 | Package: varwidth 2009/03/30 ver 0.92; Variable-width minipages 50 | \@vwid@box=\box51 51 | \sift@deathcycles=\count197 52 | \@vwid@loff=\dimen141 53 | \@vwid@roff=\dimen142 54 | ) 55 | \sa@box=\box52 56 | ) (c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 57 | Package: booktabs 2020/01/12 v1.61803398 Publication quality tables 58 | \heavyrulewidth=\dimen143 59 | \lightrulewidth=\dimen144 60 | \cmidrulewidth=\dimen145 61 | \belowrulesep=\dimen146 62 | \belowbottomsep=\dimen147 63 | \aboverulesep=\dimen148 64 | \abovetopsep=\dimen149 65 | \cmidrulesep=\dimen150 66 | \cmidrulekern=\dimen151 67 | \defaultaddspace=\dimen152 68 | \@cmidla=\count198 69 | \@cmidlb=\count199 70 | \@aboverulesep=\dimen153 71 | \@belowrulesep=\dimen154 72 | \@thisruleclass=\count266 73 | \@lastruleclass=\count267 74 | \@thisrulewidth=\dimen155 75 | ) (c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 76 | File: l3backend-pdftex.def 2023-11-09 L3 backend support: PDF output (pdfTeX) 77 | \l__color_backend_stack_int=\count268 78 | \l__pdf_internal_box=\box53 79 | ) (./reg1.aux) 80 | \openout1 = `reg1.aux'. 81 | 82 | LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 4. 83 | LaTeX Font Info: ... okay on input line 4. 84 | LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 4. 85 | LaTeX Font Info: ... okay on input line 4. 86 | LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 4. 87 | LaTeX Font Info: ... okay on input line 4. 88 | LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 4. 89 | LaTeX Font Info: ... okay on input line 4. 90 | LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 4. 91 | LaTeX Font Info: ... okay on input line 4. 92 | LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 4. 93 | LaTeX Font Info: ... okay on input line 4. 94 | LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 4. 95 | LaTeX Font Info: ... okay on input line 4. 96 | LaTeX Font Info: External font `cmex10' loaded for size 97 | (Font) <7> on input line 8. 98 | LaTeX Font Info: External font `cmex10' loaded for size 99 | (Font) <5> on input line 8. 100 | LaTeX Font Info: External font `cmex10' loaded for size 101 | (Font) <8> on input line 45. 102 | LaTeX Font Info: External font `cmex10' loaded for size 103 | (Font) <6> on input line 45. 104 | [1 105 | 106 | {c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (./reg1.aux) 107 | *********** 108 | LaTeX2e <2023-11-01> 109 | L3 programming layer <2023-11-09> 110 | *********** 111 | ) 112 | Here is how much of TeX's memory you used: 113 | 1164 strings out of 474267 114 | 21722 string characters out of 5751465 115 | 1935558 words of memory out of 5000000 116 | 23411 multiletter control sequences out of 15000+600000 117 | 559892 words of font info for 42 fonts, out of 8000000 for 9000 118 | 1141 hyphenation exceptions out of 8191 119 | 57i,11n,71p,352b,292s stack positions out of 10000i,1000n,20000p,200000b,200000s 120 | 121 | Output written on reg1.pdf (1 page, 54335 bytes). 122 | PDF statistics: 123 | 33 PDF objects out of 1000 (max. 8388607) 124 | 19 compressed objects within 1 object stream 125 | 0 named destinations out of 1000 (max. 500000) 126 | 1 words of extra memory for PDF output out of 10000 (max. 10000000) 127 | 128 | -------------------------------------------------------------------------------- /vignettes/assets/reg2.log: -------------------------------------------------------------------------------- 1 | This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023) (preloaded format=pdflatex 2023.12.2) 13 MAY 2024 15:43 2 | entering extended mode 3 | restricted \write18 enabled. 4 | file:line:error style messages enabled. 5 | %&-line parsing enabled. 6 | **"c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg2.tex" 7 | (c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg2.tex 8 | LaTeX2e <2023-11-01> 9 | L3 programming layer <2023-11-09> 10 | (c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 11 | Document Class: standalone 2022/10/10 v1.3b Class to compile TeX sub-files standalone 12 | (c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 13 | Package: shellesc 2023/07/08 v1.0d unified shell escape interface for LaTeX 14 | Package shellesc Info: Restricted shell escape enabled on input line 77. 15 | ) (c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 16 | Package: ifluatex 2019/10/25 v1.5 ifluatex legacy package. Use iftex instead. 17 | (c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 18 | Package: iftex 2022/02/03 v1.0f TeX engine tests 19 | )) (c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 20 | Package: xkeyval 2022/06/16 v2.9 package option processing (HA) 21 | (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex 22 | \XKV@toks=\toks17 23 | \XKV@tempa@toks=\toks18 24 | (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex)) 25 | \XKV@depth=\count186 26 | File: xkeyval.tex 2014/12/03 v2.7a key=value parser (HA) 27 | )) 28 | \sa@internal=\count187 29 | \c@sapage=\count188 30 | (c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 31 | File: standalone.cfg 2022/10/10 v1.3b Default configuration file for 'standalone' class 32 | ) (c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 33 | Document Class: article 2023/05/17 v1.4n Standard LaTeX document class 34 | (c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 35 | File: size10.clo 2023/05/17 v1.4n Standard LaTeX file (size option) 36 | ) 37 | \c@part=\count189 38 | \c@section=\count190 39 | \c@subsection=\count191 40 | \c@subsubsection=\count192 41 | \c@paragraph=\count193 42 | \c@subparagraph=\count194 43 | \c@figure=\count195 44 | \c@table=\count196 45 | \abovecaptionskip=\skip48 46 | \belowcaptionskip=\skip49 47 | \bibindent=\dimen140 48 | ) (c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 49 | Package: varwidth 2009/03/30 ver 0.92; Variable-width minipages 50 | \@vwid@box=\box51 51 | \sift@deathcycles=\count197 52 | \@vwid@loff=\dimen141 53 | \@vwid@roff=\dimen142 54 | ) 55 | \sa@box=\box52 56 | ) (c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 57 | Package: booktabs 2020/01/12 v1.61803398 Publication quality tables 58 | \heavyrulewidth=\dimen143 59 | \lightrulewidth=\dimen144 60 | \cmidrulewidth=\dimen145 61 | \belowrulesep=\dimen146 62 | \belowbottomsep=\dimen147 63 | \aboverulesep=\dimen148 64 | \abovetopsep=\dimen149 65 | \cmidrulesep=\dimen150 66 | \cmidrulekern=\dimen151 67 | \defaultaddspace=\dimen152 68 | \@cmidla=\count198 69 | \@cmidlb=\count199 70 | \@aboverulesep=\dimen153 71 | \@belowrulesep=\dimen154 72 | \@thisruleclass=\count266 73 | \@lastruleclass=\count267 74 | \@thisrulewidth=\dimen155 75 | ) (c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 76 | File: l3backend-pdftex.def 2023-11-09 L3 backend support: PDF output (pdfTeX) 77 | \l__color_backend_stack_int=\count268 78 | \l__pdf_internal_box=\box53 79 | ) (./reg2.aux) 80 | \openout1 = `reg2.aux'. 81 | 82 | LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 4. 83 | LaTeX Font Info: ... okay on input line 4. 84 | LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 4. 85 | LaTeX Font Info: ... okay on input line 4. 86 | LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 4. 87 | LaTeX Font Info: ... okay on input line 4. 88 | LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 4. 89 | LaTeX Font Info: ... okay on input line 4. 90 | LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 4. 91 | LaTeX Font Info: ... okay on input line 4. 92 | LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 4. 93 | LaTeX Font Info: ... okay on input line 4. 94 | LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 4. 95 | LaTeX Font Info: ... okay on input line 4. 96 | LaTeX Font Info: External font `cmex10' loaded for size 97 | (Font) <7> on input line 8. 98 | LaTeX Font Info: External font `cmex10' loaded for size 99 | (Font) <5> on input line 8. 100 | LaTeX Font Info: External font `cmex10' loaded for size 101 | (Font) <8> on input line 48. 102 | LaTeX Font Info: External font `cmex10' loaded for size 103 | (Font) <6> on input line 48. 104 | [1 105 | 106 | {c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (./reg2.aux) 107 | *********** 108 | LaTeX2e <2023-11-01> 109 | L3 programming layer <2023-11-09> 110 | *********** 111 | ) 112 | Here is how much of TeX's memory you used: 113 | 1164 strings out of 474267 114 | 21722 string characters out of 5751465 115 | 1933558 words of memory out of 5000000 116 | 23411 multiletter control sequences out of 15000+600000 117 | 559892 words of font info for 42 fonts, out of 8000000 for 9000 118 | 1141 hyphenation exceptions out of 8191 119 | 57i,11n,71p,352b,292s stack positions out of 10000i,1000n,20000p,200000b,200000s 120 | 121 | Output written on reg2.pdf (1 page, 52197 bytes). 122 | PDF statistics: 123 | 33 PDF objects out of 1000 (max. 8388607) 124 | 19 compressed objects within 1 object stream 125 | 0 named destinations out of 1000 (max. 500000) 126 | 1 words of extra memory for PDF output out of 10000 (max. 10000000) 127 | 128 | -------------------------------------------------------------------------------- /R/R/did_multiplegt_bootstrap.R: -------------------------------------------------------------------------------- 1 | #' Internal function of did_multiplegt_dyn - bootstrap se 2 | #' @param df df 3 | #' @param outcome outcome 4 | #' @param group group 5 | #' @param time time 6 | #' @param treatment treatment 7 | #' @param effects effects 8 | #' @param placebo placebo 9 | #' @param ci_level ci_level 10 | #' @param switchers switchers 11 | #' @param trends_nonparam trends_nonparam 12 | #' @param weight weight 13 | #' @param controls controls 14 | #' @param dont_drop_larger_lower dont_drop_larger_lower 15 | #' @param drop_if_d_miss_before_first_switch drop_if_d_miss_before_first_switch 16 | #' @param cluster cluster 17 | #' @param same_switchers same_switchers 18 | #' @param same_switchers_pl same_switchers_pl 19 | #' @param only_never_switchers only_never_switchers 20 | #' @param effects_equal effects_equal 21 | #' @param save_results save_results 22 | #' @param normalized normalized 23 | #' @param predict_het predict_het 24 | #' @param trends_lin trends_lin 25 | #' @param less_conservative_se less_conservative_se 26 | #' @param continuous continuous 27 | #' @param bootstrap bootstrap 28 | #' @param base base 29 | #' @returns A list of the final results updated with the bootstrap standard errors 30 | #' @noRd 31 | did_multiplegt_bootstrap <- function( 32 | df, 33 | outcome, 34 | group, 35 | time, 36 | treatment, 37 | effects, 38 | placebo, 39 | ci_level, 40 | switchers, 41 | trends_nonparam, 42 | weight, 43 | controls, 44 | dont_drop_larger_lower, 45 | drop_if_d_miss_before_first_switch, 46 | cluster, 47 | same_switchers, 48 | same_switchers_pl, 49 | only_never_switchers, 50 | effects_equal, 51 | save_results, 52 | normalized, 53 | predict_het, 54 | trends_lin, 55 | less_conservative_se, 56 | continuous, 57 | bootstrap, 58 | base 59 | ){ 60 | 61 | bresults_effects <- NULL 62 | bresults_ATE <- NULL 63 | bresults_placebo <- NULL 64 | 65 | n_effects <- nrow(base$Effects) 66 | bresults_effects <- matrix(NA, nrow = bootstrap, ncol = n_effects) 67 | if (isFALSE(trends_lin)) { 68 | bresults_ATE <- matrix(NA, nrow = bootstrap, ncol = 1) 69 | } 70 | if (placebo > 0) { 71 | n_placebo <- nrow(base$Placebos) 72 | bresults_placebo <- matrix(NA, nrow = bootstrap, ncol = n_placebo) 73 | } 74 | 75 | bs_group <- ifelse(!is.null(cluster), cluster, group) 76 | 77 | bdf <- data.frame(cbind(df[[bs_group]], 1:nrow(df))) 78 | colnames(bdf) <- c(bs_group, "id") 79 | xtset <- list() 80 | for (l in levels(factor(bdf[[bs_group]]))) { 81 | xtset[[l]] <- subset(bdf, bdf[[bs_group]] == l)[["id"]] 82 | } 83 | 84 | for (j in 1:bootstrap) { 85 | df_boot <- df[list_to_vec(xtset[sample(1:length(xtset), size = length(xtset), replace = TRUE)]), ] 86 | df_boot <- df_boot[order(df_boot[[group]], df_boot[[time]]), ] 87 | # rownames(df_boot) <- 1:nrow(df_boot) 88 | suppressMessages({ 89 | df_est <- did_multiplegt_main(df = df_boot, outcome = outcome, group = group, time = time, treatment = treatment, effects = effects, placebo = placebo, ci_level = ci_level,switchers = switchers, trends_nonparam = trends_nonparam, weight = weight, controls = controls, dont_drop_larger_lower = dont_drop_larger_lower, drop_if_d_miss_before_first_switch = drop_if_d_miss_before_first_switch, cluster = cluster, same_switchers = same_switchers, same_switchers_pl = same_switchers_pl, only_never_switchers = only_never_switchers, effects_equal = effects_equal, save_results = save_results, normalized = normalized, predict_het = predict_het, trends_lin = trends_lin, less_conservative_se = less_conservative_se, continuous = continuous)}) 90 | 91 | res <- df_est$did_multiplegt_dyn 92 | for (i in 1:ncol(bresults_effects)) { 93 | if (i <= nrow(res$Effects)) { 94 | bresults_effects[j,i] <- res$Effects[i,1] 95 | } 96 | } 97 | 98 | if (!is.null(bresults_ATE)) { 99 | if (!is.null(res$ATE[1])) { 100 | bresults_ATE[j,1] <- res$ATE[1] 101 | } 102 | } 103 | 104 | if (!is.null(bresults_placebo)) { 105 | for (i in 1:ncol(bresults_placebo)) { 106 | if (i <= nrow(res$Placebos)) { 107 | bresults_placebo[j,i] <- res$Placebos[i,1] 108 | } 109 | } 110 | } 111 | res <- df_est <- NULL 112 | progressBar(j, bootstrap) 113 | } 114 | 115 | ci_level <- ci_level / 100 116 | z_level <- qnorm(ci_level + (1 - ci_level)/2) 117 | 118 | for (i in 1:ncol(bresults_effects)) { 119 | if (!is.null(base$Effects[i,1])) { 120 | base$Effects[i,2] <- sd(bresults_effects[,i], na.rm = TRUE) 121 | base$Effects[i,3] <- base$Effects[i,1] - z_level * base$Effects[i,2] 122 | base$Effects[i,4] <- base$Effects[i,1] + z_level * base$Effects[i,2] 123 | } 124 | } 125 | if (nrow(base$Effects) == 1) { 126 | class(base$Effects) <- "numeric" 127 | } 128 | 129 | if (!is.null(bresults_ATE)) { 130 | if (!is.null(base$ATE[1])) { 131 | base$ATE[2] <- sd(bresults_ATE, na.rm = TRUE) 132 | base$ATE[3] <- base$ATE[1] - z_level * base$ATE[2] 133 | base$ATE[4] <- base$ATE[1] + z_level * base$ATE[2] 134 | } 135 | } 136 | 137 | if (!is.null(bresults_placebo)) { 138 | for (i in 1:ncol(bresults_placebo)) { 139 | if (!is.null(base$Placebos[i,1])) { 140 | base$Placebos[i,2] <- sd(bresults_placebo[,i], na.rm = TRUE) 141 | base$Placebos[i,3] <- base$Placebos[i,1] - z_level * base$Placebos[i,2] 142 | base$Placebos[i,4] <- base$Placebos[i,1] + z_level * base$Placebos[i,2] 143 | } 144 | } 145 | if (nrow(base$Placebos) == 1) { 146 | class(base$Placebos) <- "numeric" 147 | } 148 | } 149 | return(base) 150 | } 151 | 152 | #' Internal function to convert lists to vectors 153 | #' @param lis A list 154 | #' @returns A vector 155 | #' @noRd 156 | list_to_vec <- function(lis) { 157 | vec <- c() 158 | for (j in 1:length(lis)) { 159 | for (i in 1:length(lis[[j]])) { 160 | vec <- c(vec, lis[[j]][[i]]) 161 | } 162 | } 163 | return(vec) 164 | } 165 | -------------------------------------------------------------------------------- /vignettes/assets/reg1a.log: -------------------------------------------------------------------------------- 1 | This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023) (preloaded format=pdflatex 2023.12.2) 13 MAY 2024 14:29 2 | entering extended mode 3 | restricted \write18 enabled. 4 | file:line:error style messages enabled. 5 | %&-line parsing enabled. 6 | **"c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1a.tex" 7 | (c:/Users/39380/C DE CHAISEMARTIN Dropbox/RAs De Chaisemartin/RAs Really Credible DID-TWFE/GitHub repo - local/did_multiplegt_dyn/vignettes/assets/reg1a.tex 8 | LaTeX2e <2023-11-01> 9 | L3 programming layer <2023-11-09> 10 | (c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cls 11 | Document Class: standalone 2022/10/10 v1.3b Class to compile TeX sub-files standalone 12 | (c:/texlive/2023/texmf-dist/tex/latex/tools/shellesc.sty 13 | Package: shellesc 2023/07/08 v1.0d unified shell escape interface for LaTeX 14 | Package shellesc Info: Restricted shell escape enabled on input line 77. 15 | ) (c:/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty 16 | Package: ifluatex 2019/10/25 v1.5 ifluatex legacy package. Use iftex instead. 17 | (c:/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty 18 | Package: iftex 2022/02/03 v1.0f TeX engine tests 19 | )) (c:/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty 20 | Package: xkeyval 2022/06/16 v2.9 package option processing (HA) 21 | (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex 22 | \XKV@toks=\toks17 23 | \XKV@tempa@toks=\toks18 24 | (c:/texlive/2023/texmf-dist/tex/generic/xkeyval/keyval.tex)) 25 | \XKV@depth=\count186 26 | File: xkeyval.tex 2014/12/03 v2.7a key=value parser (HA) 27 | )) 28 | \sa@internal=\count187 29 | \c@sapage=\count188 30 | (c:/texlive/2023/texmf-dist/tex/latex/standalone/standalone.cfg 31 | File: standalone.cfg 2022/10/10 v1.3b Default configuration file for 'standalone' class 32 | ) (c:/texlive/2023/texmf-dist/tex/latex/base/article.cls 33 | Document Class: article 2023/05/17 v1.4n Standard LaTeX document class 34 | (c:/texlive/2023/texmf-dist/tex/latex/base/size10.clo 35 | File: size10.clo 2023/05/17 v1.4n Standard LaTeX file (size option) 36 | ) 37 | \c@part=\count189 38 | \c@section=\count190 39 | \c@subsection=\count191 40 | \c@subsubsection=\count192 41 | \c@paragraph=\count193 42 | \c@subparagraph=\count194 43 | \c@figure=\count195 44 | \c@table=\count196 45 | \abovecaptionskip=\skip48 46 | \belowcaptionskip=\skip49 47 | \bibindent=\dimen140 48 | ) (c:/texlive/2023/texmf-dist/tex/latex/varwidth/varwidth.sty 49 | Package: varwidth 2009/03/30 ver 0.92; Variable-width minipages 50 | \@vwid@box=\box51 51 | \sift@deathcycles=\count197 52 | \@vwid@loff=\dimen141 53 | \@vwid@roff=\dimen142 54 | ) 55 | \sa@box=\box52 56 | ) (c:/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty 57 | Package: booktabs 2020/01/12 v1.61803398 Publication quality tables 58 | \heavyrulewidth=\dimen143 59 | \lightrulewidth=\dimen144 60 | \cmidrulewidth=\dimen145 61 | \belowrulesep=\dimen146 62 | \belowbottomsep=\dimen147 63 | \aboverulesep=\dimen148 64 | \abovetopsep=\dimen149 65 | \cmidrulesep=\dimen150 66 | \cmidrulekern=\dimen151 67 | \defaultaddspace=\dimen152 68 | \@cmidla=\count198 69 | \@cmidlb=\count199 70 | \@aboverulesep=\dimen153 71 | \@belowrulesep=\dimen154 72 | \@thisruleclass=\count266 73 | \@lastruleclass=\count267 74 | \@thisrulewidth=\dimen155 75 | ) (c:/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def 76 | File: l3backend-pdftex.def 2023-11-09 L3 backend support: PDF output (pdfTeX) 77 | \l__color_backend_stack_int=\count268 78 | \l__pdf_internal_box=\box53 79 | ) (./reg1a.aux) 80 | \openout1 = `reg1a.aux'. 81 | 82 | LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 4. 83 | LaTeX Font Info: ... okay on input line 4. 84 | LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 4. 85 | LaTeX Font Info: ... okay on input line 4. 86 | LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 4. 87 | LaTeX Font Info: ... okay on input line 4. 88 | LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 4. 89 | LaTeX Font Info: ... okay on input line 4. 90 | LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 4. 91 | LaTeX Font Info: ... okay on input line 4. 92 | LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 4. 93 | LaTeX Font Info: ... okay on input line 4. 94 | LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 4. 95 | LaTeX Font Info: ... okay on input line 4. 96 | LaTeX Font Info: External font `cmex10' loaded for size 97 | (Font) <7> on input line 8. 98 | LaTeX Font Info: External font `cmex10' loaded for size 99 | (Font) <5> on input line 8. 100 | LaTeX Font Info: External font `cmex10' loaded for size 101 | (Font) <8> on input line 44. 102 | LaTeX Font Info: External font `cmex10' loaded for size 103 | (Font) <6> on input line 44. 104 | [1 105 | 106 | {c:/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (./reg1a.aux) 107 | *********** 108 | LaTeX2e <2023-11-01> 109 | L3 programming layer <2023-11-09> 110 | *********** 111 | ) 112 | Here is how much of TeX's memory you used: 113 | 1163 strings out of 474267 114 | 21716 string characters out of 5751465 115 | 1935558 words of memory out of 5000000 116 | 23410 multiletter control sequences out of 15000+600000 117 | 559892 words of font info for 42 fonts, out of 8000000 for 9000 118 | 1141 hyphenation exceptions out of 8191 119 | 57i,11n,71p,353b,292s stack positions out of 10000i,1000n,20000p,200000b,200000s 120 | 121 | Output written on reg1a.pdf (1 page, 78394 bytes). 122 | PDF statistics: 123 | 48 PDF objects out of 1000 (max. 8388607) 124 | 28 compressed objects within 1 object stream 125 | 0 named destinations out of 1000 (max. 500000) 126 | 1 words of extra memory for PDF output out of 10000 (max. 10000000) 127 | 128 | -------------------------------------------------------------------------------- /vignettes/src/vignette_1_Stata.do: -------------------------------------------------------------------------------- 1 | // A toy example // 2 | 3 | clear 4 | set obs 9 5 | gen Party = char(64 + ceil(_n/3)) 6 | egen Partyid = group(Party) 7 | bys Party: gen Year = 2003 + (_n-1)*2 8 | mat define Share = (0.4\0.35\0.25\0.4\0.45\0.50\0.15\0.1\0.05) 9 | mat define LeadChangeYr = (2004\2004\2004\.\.\.\2005\2005\2005) 10 | foreach v in Share LeadChangeYr { 11 | mat coln `v' = "`v'" 12 | svmat `v', n(col) 13 | } 14 | gen Treatment = Year >= LeadChangeYr 15 | 16 | mat define res = J(4, 1, .) 17 | did_multiplegt_dyn Share Partyid Year Treatment if inlist(Party, "A", "B"), effects(2) graph_off 18 | mat res[2,1] = e(Effect_1) 19 | mat res[4,1] = e(Effect_2) 20 | did_multiplegt_dyn Share Partyid Year Treatment if inlist(Party, "C", "B"), effects(2) graph_off 21 | mat res[1,1] = e(Effect_1) 22 | mat res[3,1] = e(Effect_2) 23 | svmat res 24 | 25 | gen ell = _n 26 | line res1 ell in 1/4, lc(black) || /// 27 | scatter res1 ell in 1/4 if mod(_n, 2) == 0, mcolor(red) msize(1.5) || /// 28 | scatter res1 ell in 1/4 if mod(_n, 2) == 1, mcolor(blue) msize(1.5) || /// 29 | , legend(order(2 "Party A's dynamic effects" 3 "Party B's dynamic effects") pos(6) col(2)) /// 30 | ytitle("dynamic effects") xtitle("# periods after first switch") ylabel(-0.3(0.1)0.1, ) yline(0) 31 | gr export "assets/vignette_1_Stata_fig1.jpg", replace 32 | 33 | // General case // 34 | clear 35 | set seed 123 36 | scalar TT = 20 37 | scalar GG = 5 38 | set obs `= TT * GG' 39 | gen G = mod(_n-1,GG) + 1 40 | gen T = floor((_n-1)/GG) 41 | sort G T 42 | 43 | gen D = 0 44 | forv j=2/5 { 45 | replace D = 1 if G == `j' & T == `j' + 2 46 | } 47 | bys G: gen D_stag = sum(D) 48 | gen Y = uniform() * (1 + 100*D_stag) if mod(T,4) == 0 49 | drop D_stag* 50 | 51 | browse 52 | 53 | did_multiplegt_dyn Y G T D, effects(5) graph_off 54 | 55 | bys G: gen D0 = D[1] 56 | gen D_change = abs(D - D0) != 0 57 | bys G: gen at_least_one_D_change = sum(D_change) 58 | 59 | bys G: egen never_treated = max(at_least_one_D_change) 60 | replace never_treated = 1 - never_treated 61 | bys G: egen F_g_temp = min(T * D_change) if D_change != 0 62 | bys G: egen F_g = mean(F_g_temp) 63 | sum T 64 | replace F_g = r(max) + 1 if missing(F_g) 65 | 66 | gen subsample = mod(F_g, 4) + 1 67 | replace subsample = 0 if at_least_one_D_change == 0 68 | 69 | sort G T 70 | browse 71 | 72 | keep if !missing(Y) 73 | 74 | // Naive run 75 | did_multiplegt_dyn Y G T at_least_one_D_change, effects(2) 76 | 77 | scalar effects = 2 78 | mat define res = J(4 * effects, 6, .) 79 | forv j=1/4 { 80 | did_multiplegt_dyn Y G T at_least_one_D_change /// 81 | if inlist(subsample, 0, `j'), effects(`=effects') graph_off 82 | forv i = 1/`=effects' { 83 | mat adj = mat_res_XX[`i',1..6] 84 | forv c =1/6 { 85 | mat res[`j'+(`i'-1)*4,`c'] = adj[1, `c'] 86 | } 87 | } 88 | } 89 | mat li res 90 | 91 | // Bigger case for graph // 92 | 93 | clear 94 | set seed 123 95 | scalar TT = 20 96 | scalar GG = 1000 97 | set obs `= TT * GG' 98 | gen G = mod(_n-1,GG) + 1 99 | gen T = floor((_n-1)/GG) 100 | sort G T 101 | 102 | gen D = 0 103 | replace D = 1 if T == mod(G - 1, 5) + 3 & mod(G-1, 5) != 0 104 | bys G: gen D_stag_temp = sum(D) 105 | bys G: gen D_stag = sum(D_stag_temp) 106 | gen Y = uniform() * (1 + D_stag) 107 | replace Y = . if mod(T, 4) != 0 108 | drop D_stag* 109 | 110 | bys G: gen D0 = D[1] 111 | gen D_change = abs(D - D0) != 0 112 | bys G: gen at_least_one_D_change = sum(D_change) 113 | 114 | bys G: egen never_treated = max(at_least_one_D_change) 115 | replace never_treated = 1 - never_treated 116 | bys G: egen F_g_temp = min(T * D_change) if D_change != 0 117 | bys G: egen F_g = mean(F_g_temp) 118 | sum T 119 | replace F_g = r(max) + 1 if missing(F_g) 120 | 121 | gen subsample = (4 - mod(F_g, 4)) * (mod(F_g, 4) != 0) + 1 122 | replace subsample = 0 if at_least_one_D_change == 0 123 | 124 | sort G T 125 | 126 | keep if !missing(Y) 127 | 128 | scalar effects = 2 129 | mat define res = J(4 * effects, 6, .) 130 | forv j=1/4 { 131 | did_multiplegt_dyn Y G T at_least_one_D_change /// 132 | if inlist(subsample, 0, `j'), effects(`=effects') graph_off 133 | forv i = 1/`=effects' { 134 | mat adj = mat_res_XX[`i',1..6] 135 | forv c =1/6 { 136 | mat res[`j'+(`i'-1)*4,`c'] = adj[1, `c'] 137 | } 138 | } 139 | } 140 | mat li res 141 | 142 | did_multiplegt_dyn Y G T at_least_one_D_change, graph_off 143 | 144 | // Add the 0-period to the saved matrix 145 | mat res = (0,0,0,0,0,0) \ res 146 | svmat res 147 | gen rel_time = _n-1 if !missing(res1) 148 | tw rcap res3 res4 rel_time, lc(blue) || /// 149 | connected res1 rel_time, mc(blue) lc(blue) || /// 150 | , xtitle("Relative time to last period before treatment changes (t=0)") /// 151 | title("DID, from last period before treatment changes (t=0) to t") /// 152 | ytitle(" ") leg(off) 153 | gr export "assets/vignette_1_Stata_fig2.jpg", replace 154 | 155 | // Bigger case for graph - PLACEBO // 156 | // Changes to the DGP to have at least 1 pre-period 157 | 158 | clear 159 | set seed 123 160 | scalar TT = 20 161 | scalar GG = 1000 162 | set obs `= TT * GG' 163 | gen G = mod(_n-1,GG) + 1 164 | gen T = floor((_n-1)/GG) 165 | sort G T 166 | 167 | gen D = 0 168 | replace D = 1 if T == mod(G - 1, 5) + 7 & mod(G-1, 5) != 0 169 | bys G: gen D_stag_temp = sum(D) 170 | bys G: gen D_stag = sum(D_stag_temp) 171 | gen Y = uniform() * (1 + D_stag) 172 | replace Y = . if mod(T, 4) != 0 173 | drop D_stag* 174 | 175 | bys G: gen D0 = D[1] 176 | gen D_change = abs(D - D0) != 0 177 | bys G: gen at_least_one_D_change = sum(D_change) 178 | 179 | bys G: egen never_treated = max(at_least_one_D_change) 180 | replace never_treated = 1 - never_treated 181 | bys G: egen F_g_temp = min(T * D_change) if D_change != 0 182 | bys G: egen F_g = mean(F_g_temp) 183 | sum T 184 | replace F_g = r(max) + 1 if missing(F_g) 185 | 186 | gen subsample = (4 - mod(F_g, 4)) * (mod(F_g, 4) != 0) + 1 187 | replace subsample = 0 if at_least_one_D_change == 0 188 | 189 | sort G T 190 | 191 | keep if !missing(Y) 192 | 193 | scalar effects = 2 194 | scalar placebo = 1 195 | mat define res = J(4*placebo + 4 * effects, 7, .) 196 | forv j=1/4 { 197 | did_multiplegt_dyn Y G T at_least_one_D_change /// 198 | if inlist(subsample, 0, `j'), effects(`=effects') placebo(`=placebo') graph_off 199 | forv i = 1/`=effects' { 200 | mat adj = mat_res_XX[`i',1..6] 201 | forv c =1/6 { 202 | mat res[`j'+(`i'-1)*4,`c'] = adj[1, `c'] 203 | } 204 | mat res[`j'+(`i'-1)*4,7] =`j'+(`i'-1)*4 205 | } 206 | forv i = 1/`=placebo' { 207 | mat adj = mat_res_XX[effects + 1 + `i',1..6] 208 | forv c =1/6 { 209 | mat res[(`i'+1)*4 - `j' + (4*(effects - 1) +1),`c'] = adj[1, `c'] 210 | } 211 | mat res[(`i'+1)*4 - `j' + (4*(effects - 1) +1),7] = - ((`i'+1)*4 - `j') 212 | } 213 | } 214 | mat li res 215 | 216 | did_multiplegt_dyn Y G T at_least_one_D_change, graph_off 217 | 218 | // Add the 0-period to the saved matrix 219 | mat res = (0,0,0,0,0,0,0) \ res 220 | svmat res 221 | sort res7 222 | tw rcap res3 res4 res7, lc(blue) || /// 223 | connected res1 res7, mc(blue) lc(blue) || /// 224 | , xtitle("Relative time to last period before treatment changes (t=0)") /// 225 | title("DID, from last period before treatment changes (t=0) to t") /// 226 | ytitle(" ") leg(off) xlabel(-7(1)8) 227 | gr export "assets/vignette_1_Stata_fig3.jpg", replace 228 | -------------------------------------------------------------------------------- /no_xlsx/DIDmultiplegtDYN/R/print.R: -------------------------------------------------------------------------------- 1 | #' @title A print method for did_multiplegt_dyn 2 | #' @name print.did_multiplegt_dyn 3 | #' @description A customized printed display for did_multiplegt_dyn output 4 | #' @param x A did_multiplegt_dyn object 5 | #' @param ... Undocumented 6 | #' @returns No return, custom print method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 7 | #' @export 8 | print.did_multiplegt_dyn <- function(x, ...) { 9 | cat("\n") 10 | 11 | by_levels <- c("_no_by") 12 | if (!is.null(x$by_levels)) { 13 | by_levels <- x$by_levels 14 | } 15 | 16 | for (b in 1:length(by_levels)) { 17 | 18 | if (by_levels[b] == "_no_by") { 19 | ref <- x 20 | } else { 21 | ref <- x[[paste0("by_level_",b)]] 22 | if (!is.null(x$args[["by"]])) { 23 | section <- paste(" By",x$args$by, "=", by_levels[b], "###") 24 | } else if (!is.null(x$args[["by_path"]])) { 25 | section <- paste0(" By treatment path: (", by_levels[b],") ", "###") 26 | } 27 | cat(noquote(strrep("#", 70 - nchar(section) - 1)));cat(section); 28 | cat("\n");cat("\n"); 29 | } 30 | 31 | cat(noquote(strrep("-", 70)));cat("\n"); 32 | cat(strrep(" ", 7));cat("Estimation of treatment effects: Event-study effects");cat("\n"); 33 | cat(noquote(strrep("-", 70)));cat("\n"); 34 | mat_print(ref$results$Effects[, 1:(6 + ((!is.null(x$args$weight))*2))]) 35 | cat("\n") 36 | 37 | if (!is.null(ref$results$p_equality_effects)) { 38 | cat(sprintf("Test of equality of the effects : p-value = %.4f", ref$results$p_equality_effects)) 39 | cat("\n");cat("\n") 40 | } 41 | 42 | if (isTRUE(x$args$trends_lin)) { 43 | cat(noquote(strrep("-", 70)));cat("\n"); 44 | cat(strrep(" ", 4));cat("When the trends_lin is specified no average effects are reported");cat("\n"); 45 | cat(noquote(strrep("-", 70)));cat("\n"); 46 | 47 | } else { 48 | cat(noquote(strrep("-", 70)));cat("\n"); 49 | cat(strrep(" ", 4));cat("Average cumulative (total) effect per treatment unit");cat("\n"); 50 | cat(noquote(strrep("-", 70)));cat("\n"); 51 | mat_print(ref$results$ATE[, 1:(6 + ((!is.null(x$args$weight))*2))]) 52 | cat(sprintf("Average number of time periods over which a treatment effect is accumulated: %.4f", ref$results$delta_D_avg_total)) 53 | cat("\n") 54 | } 55 | cat("\n") 56 | 57 | if (ref$results$N_Placebos != 0) { 58 | cat(noquote(strrep("-", 70)));cat("\n"); 59 | cat(strrep(" ", 4));cat(" Testing the parallel trends and no anticipation assumptions");cat("\n"); 60 | cat(noquote(strrep("-", 70)));cat("\n"); 61 | mat_print(ref$results$Placebos[, 1:(6 + ((!is.null(x$args$weight))*2))]) 62 | if (is.null(x$args$bootstrap)) { 63 | cat("\n") 64 | cat(sprintf("Test of joint nullity of the placebos : p-value = %.4f", ref$results$p_jointplacebo)) 65 | cat("\n") 66 | } 67 | cat("\n") 68 | } 69 | 70 | if (!is.null(ref$design)) { 71 | if (ref$design$design_path == "console") { 72 | cat("\n") 73 | cat(noquote(strrep("-", 70)));cat("\n"); 74 | cat(strrep(" ", 4));cat(sprintf("Detection of treatment paths - %.0f periods after first switch", ref$design$design_const[1]));cat("\n"); 75 | cat(noquote(strrep("-", 70)));cat("\n"); 76 | print(ref$design$design_mat);cat("\n"); 77 | cat(sprintf("Treatment paths detected in at least %.2f%% of the %.0f switching groups for which %.0f effects could be estimated", ref$design$design_const[2], ref$design$design_const[3], ref$design$design_const[1])) 78 | cat(sprintf(" (Total %% = %.2f%%)", ref$design$design_const[4]));cat("\n");cat("\n"); 79 | cat("Design interpretation (first row):");cat("\n") 80 | n_groups <- ref$design$design_mat[1,1] 81 | d_start <- ref$design$design_mat[1,3] 82 | d_vec <- "(" 83 | for (i in 1:ref$design$design_const[1]) { 84 | d_vec <- paste0(d_vec, ref$design$design_mat[1, 3 + i],",") 85 | } 86 | d_vec <- paste0(substr(d_vec,1,nchar(d_vec)-1),")") 87 | cat(sprintf("%s groups started with treatment %s and experienced treatment path %s", n_groups, d_start, d_vec)) 88 | cat("\n") 89 | } 90 | else { 91 | cat(sprintf("Design exported to %s", ref$design$design_path)); cat("\n") 92 | } 93 | } 94 | 95 | if (!is.null(ref$date_first_switch)) { 96 | if (ref$date_first_switch$dfs_opt != "by_baseline_treat") { 97 | if (ref$date_first_switch$dfs_path == "console") { 98 | cat("\n") 99 | cat(noquote(strrep("-", 40)));cat("\n"); 100 | cat(strrep(" ", 7));cat("Switching dates");cat("\n"); 101 | cat(noquote(strrep("-", 40)));cat("\n"); 102 | cat("By any status quo treatment");cat("\n"); 103 | print(ref$date_first_switch$dfs_mat) 104 | cat("\n") 105 | } 106 | else { 107 | cat(sprintf("Switching dates exported to %s", ref$date_first_switch$dfs_path)); cat("\n") 108 | } 109 | } 110 | else { 111 | if (ref$date_first_switch$dfs_path == "console") { 112 | cat("\n") 113 | cat(noquote(strrep("-", 40)));cat("\n"); 114 | cat(strrep(" ", 7));cat("Switching dates");cat("\n"); 115 | cat(noquote(strrep("-", 40)));cat("\n"); 116 | for (l in 1:ref$date_first_switch$levels_baseline_treat) { 117 | cat(sprintf("Status quo treatment = %s", ref$date_first_switch[[paste0("level",l)]]));cat("\n"); 118 | print(ref$date_first_switch[[paste0("dfs_mat",l)]]) 119 | cat("\n") 120 | } 121 | } 122 | if (ref$date_first_switch$dfs_path != "console") { 123 | cat(sprintf("Switching dates exported to %s", ref$date_first_switch$dfs_path)); cat("\n") 124 | } 125 | } 126 | } 127 | 128 | if (!is.null(ref$normalized_weights)) { 129 | cat("\n") 130 | cat(noquote(strrep("-", 60)));cat("\n"); 131 | cat(strrep(" ", 13));cat("Weights on treatment lags");cat("\n"); 132 | cat(noquote(strrep("-", 60)));cat("\n"); 133 | print(ref$normalized_weights$norm_weight_mat) 134 | cat("\n") 135 | } 136 | 137 | if (!is.null(ref$results$predict_het)) { 138 | cat("\n") 139 | cat(noquote(strrep("-", 60)));cat("\n"); 140 | cat(strrep(" ", 13));cat("Predicting effect heterogeneity");cat("\n"); 141 | cat(noquote(strrep("-", 60)));cat("\n"); 142 | for (l in levels(factor(ref$results$predict_het$effect))) { 143 | het_tab <- subset(ref$results$predict_het, ref$results$predict_het$effect == l) 144 | het_mat <- as.matrix(het_tab[,c(3,4,6,7,8)]) 145 | rownames(het_mat) <- het_tab$covariate 146 | colnames(het_mat) <- c("Estimate", "SE", "LB CI", "UB CI", "N") 147 | pval <- mean(het_tab$pF) 148 | cat(sprintf("Effect %s:\n", l)) 149 | mat_print(het_mat) 150 | cat(sprintf("Test of joint nullity of the estimates : p-value = %.4f\n", pval));cat("\n") 151 | } 152 | } 153 | } 154 | 155 | cat("\n") 156 | cat("The development of this package was funded by the European Union.");cat("\n") 157 | cat("ERC REALLYCREDIBLE - GA N. 101043899");cat("\n") 158 | 159 | } 160 | 161 | #' @title A summary method for did_multiplegt_dyn 162 | #' @name summary.did_multiplegt_dyn 163 | #' @description A customized summary display for did_multiplegt_dyn output 164 | #' @param object A did_multiplegt_dyn object 165 | #' @param ... Undocumented 166 | #' @returns No return, custom summary method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 167 | #' @export 168 | summary.did_multiplegt_dyn <- function(object, ...) { 169 | print(object) 170 | } 171 | 172 | #' Auxiliary function for print method 173 | #' @param mat mat 174 | #' @returns No return, just prints the refined input. 175 | #' @noRd 176 | mat_print <- function(mat) { 177 | if (inherits(mat,"matrix")) { 178 | dis <- matrix(data = 0, nrow = nrow(mat) , ncol = ncol(mat)) 179 | dis[,1:4] <- sprintf("%s", format(round(mat[,1:4], 5), big.mark=",", scientific=FALSE, trim=TRUE)) 180 | dis[,5:ncol(dis)] <- 181 | sprintf("%s", format(round(mat[,5:ncol(dis)], 0), big.mark=",", scientific=FALSE, trim=TRUE)) 182 | rownames(dis) <- rownames(mat) 183 | colnames(dis) <- colnames(mat) 184 | print(noquote(dis[, , drop = FALSE])) 185 | } else { 186 | dis <- vector(length = length(mat)) 187 | dis[1:4] <- sprintf("%s", format(round(mat[1:4], 5), big.mark=",", scientific=FALSE, trim=TRUE)) 188 | dis[5:length(mat)] <- sprintf("%s", format(round(mat[5:length(mat)], 0), big.mark=",", scientific=FALSE, trim=TRUE)) 189 | names(dis) <- names(mat) 190 | print(noquote(dis[ , drop = FALSE])) 191 | } 192 | } -------------------------------------------------------------------------------- /vignettes/src/vignette_1_R.R: -------------------------------------------------------------------------------- 1 | # Part I: Data Generation 2 | library(dplyr) 3 | set.seed(123) 4 | TT <- 20; GG <- 5 5 | df <- data.frame(id = 1:(GG*TT)) 6 | df$G <- ((df$id-1) %% GG)+1 7 | df$T <- floor((df$id-1)/GG) 8 | df$id <- NULL 9 | df <- df[order(df$G, df$T), ] 10 | 11 | ## One never treated group and 4 groups treated starting from period 4 12 | ## Treatment variable D: 13 | #### Group 1 -> always 0 14 | #### Group n -> 1 at period n+2 15 | 16 | df$D <- 0 17 | for (v in c(2,3,4,5)) { 18 | df$D <- ifelse(df$G == v & df$T == v+2, 1, df$D) 19 | } 20 | df <- df %>% group_by(.data$G) %>% mutate(D_stag = cumsum(.data$D)) %>% ungroup() 21 | 22 | ## The outcome is not-missing only every 4 years 23 | df$Y <- ifelse(df$T %% 4 == 0, runif(n = nrow(df)) * (1 + 100*df$D_stag), NA) 24 | df$D_stag <- NULL 25 | 26 | ## Let's browse the raw dataset 27 | View(df) 28 | 29 | ## Now we have dataset with the issue of outcome being observed less frequently than the treatent 30 | 31 | # Part II: Data Adjustment 32 | ## We need now to create the groups for the estimation. 33 | ## Keep in mind that, since the outcome is observable every 4 periods, we need to generate four indicators which point to (g,t) cells such that: 34 | #### 1. treatment has never changed since the start of the panel or treatment has changed (at least once) at t and the change occurred in a non-missing outcome year 35 | #### 2. treatment has never changed since the start of the panel or treatment has changed (at least once) at t and the change occurred the year before a non-missing outcome year 36 | #### 3. treatment has never changed since the start of the panel or treatment has changed (at least once) at t and the change occurred two years before a non-missing outcome year 37 | #### 4. treatment has never changed since the start of the panel or treatment has changed (at least once) at t and the change occurred three years before a non-missing outcome year 38 | 39 | # a) Identify whether treatment has changed within each group (my suggestion: load the dplyr library) 40 | 41 | df$D0 <- df$D[(df$G-1)*length(levels(factor(df$T)))+1] # We take the treatment value corresponding to T == 1 (If your groups are strings, just generate their id to make this method work) 42 | df$D_change <- as.numeric(abs(df$D - df$D0) != 0) 43 | df <- df %>% group_by(.data$G) %>% mutate(at_least_one_D_change = cumsum(.data$D_change)) %>% ungroup() 44 | ## As expected, the group 2 has at least one treatment change starting from period 4 (the period where its first treatment change occurred), group 3 from period 5 and so on. 45 | 46 | # b) Identify when treatment changed for the first time 47 | 48 | ## We generate a variable (F_g) equal to the earliest period when there has been a treatment change 49 | ## For never switchers we use the same convention as de Chaisemartin & D'Haultfoeuille (2024) and set F_g = number of periods + 1 50 | df <- df %>% group_by(.data$G) %>% 51 | mutate(never_treated = as.numeric(sum(.data$D_change, na.rm = TRUE) == 0)) %>% 52 | mutate(F_g = ifelse(.data$never_treated == 1, max(df$T, na.rm = TRUE) +1, min(ifelse(.data$D_change == 0, NA, .data$T * .data$D_change), na.rm = TRUE))) %>% ungroup() 53 | 54 | ## Let's use the modulus operator to check whether the F_g falls on a 4th period or 1/2/3 years after (There are several ways to do this!) 55 | ## My approach (below): each year where the outcome is not missing takes value 1. Since the outcome is non-missing every 4 years, we just check whether the period is divisible by 4. If yes, the modulus operator by 4 yields 0. If not, it yield the remainder from dividing by 4. This remainder is always between 1 and 3. So, it is enough to increase the remainder by 1 to obtain 4 distinct values such that obs with non missing outcome (every fourth period) have value 1, obs after them value 2 and so on. 56 | df$subsample <- (df$F_g %% 4) + 1 57 | 58 | ## From here, it is enough to multiply the subset variable by the indicator for at least one cange in D 59 | df$subsample <- df$subsample * df$at_least_one_D_change 60 | 61 | 62 | # Take a moment to look at the data and see yourself that the variable model_subset takes on values 0,1,2, 3 and 4. 63 | View(df) 64 | 65 | # Keep only the observations with non-missing outcome 66 | df <- subset(df, !is.na(df$Y)) 67 | 68 | ## Part III: Estimation 69 | ## Notice that the estimation will be performed with the staggerized version of the treatment (that we called at_least_one_D_change) 70 | 71 | library(DIDmultiplegtDYN) 72 | effects <- 2 73 | table <- NULL 74 | for (j in 1:4) { 75 | temp <- did_multiplegt_dyn( 76 | subset(df, df$subsample %in% c(0, j)), "Y", "G", "T", "at_least_one_D_change", 77 | graph_off = TRUE, effects = effects) 78 | rownames(temp$results$Effects) <- 79 | sapply(1:temp$results$N_Effects, function(x) paste0("Effect_", j + (x-1) * 4)) 80 | table <- rbind(table, temp$results$Effects) 81 | } 82 | rown <- unlist(strsplit(rownames(table), "_")) 83 | table <- cbind(table, as.numeric(rown[rown != "Effect"])) 84 | print(table[order(table[,ncol(table)]),1:(ncol(table)-1)]) 85 | 86 | # Bigger Data for graphs 87 | set.seed(123) 88 | library(dplyr) 89 | library(DIDmultiplegtDYN) 90 | 91 | TT <- 20; GG <- 1000 92 | df <- data.frame(id = 1:(GG*TT)) 93 | df$G <- ((df$id-1) %% GG)+1 94 | df$T <- floor((df$id-1)/GG) 95 | df$id <- NULL 96 | df <- df[order(df$G, df$T), ] 97 | df$D <- 0 98 | df$D <- ifelse(df$T == ((df$G - 1) %% 5) + 3 & (df$G - 1) %% 5 != 0, 1, df$D) 99 | df <- df %>% group_by(.data$G) %>% mutate(D_stag_temp = cumsum(.data$D)) %>% ungroup() 100 | df <- df %>% group_by(.data$G) %>% mutate(D_stag = cumsum(.data$D_stag_temp)) %>% ungroup() 101 | df$Y <- ifelse(df$T %% 4 == 0, runif(n = nrow(df)) * (1 + df$D_stag), NA) 102 | df$D_stag <- NULL 103 | df$D0 <- df$D[(df$G-1)*length(levels(factor(df$T)))+1] 104 | df$D_change <- as.numeric(abs(df$D - df$D0) != 0) 105 | df <- df %>% group_by(.data$G) %>% mutate(at_least_one_D_change = cumsum(.data$D_change)) %>% ungroup() 106 | df <- df %>% group_by(.data$G) %>% 107 | mutate(never_treated = as.numeric(sum(.data$D_change, na.rm = TRUE) == 0)) %>% 108 | mutate(F_g = ifelse(.data$never_treated == 1, max(df$T, na.rm = TRUE) +1, min(ifelse(.data$D_change == 0, NA, .data$T * .data$D_change), na.rm = TRUE))) %>% ungroup() 109 | df$subsample <- (4 - (df$F_g %% 4)) * (df$F_g %% 4 != 0) + 1 110 | df$subsample <- df$subsample * df$at_least_one_D_change 111 | df <- subset(df, !is.na(df$Y)) 112 | 113 | effects <- 2 114 | table <- NULL 115 | for (j in 1:4) { 116 | temp <- did_multiplegt_dyn( 117 | subset(df, df$subsample %in% c(0, j)), "Y", "G", "T", "at_least_one_D_change", 118 | graph_off = TRUE, effects = effects) 119 | rownames(temp$results$Effects) <- 120 | sapply(1:temp$results$N_Effects, function(x) paste0("Effect_", j + (x-1) * 4)) 121 | table <- rbind(table, temp$results$Effects) 122 | } 123 | rown <- unlist(strsplit(rownames(table), "_")) 124 | table <- cbind(table, as.numeric(rown[rown != "Effect"])) 125 | print(table[order(table[,ncol(table)]),1:(ncol(table)-1)]) 126 | 127 | library(ggplot2) 128 | table <- table[order(table[,ncol(table)]), ] 129 | table <- rbind(rep(0, ncol(table)), table) 130 | colnames(table)[ncol(table)] <- "Time" 131 | table <- as.data.frame(table) 132 | out_plot <- ggplot(table, aes(x = .data$Time, y = .data$Estimate, group = 1)) + 133 | geom_line(colour = "blue") + 134 | geom_errorbar(data = ~dplyr::filter(.x, table$Estimate != 0), aes(ymin = .data[["LB CI"]], ymax = .data[["UB CI"]]), 135 | position=position_dodge(0.05), width = 0.2, colour = "red") + 136 | geom_point(colour = "blue") + 137 | ggtitle("DID, from last period before treatment changes (t=0) to t") + 138 | xlab("Relative time to last period before treatment changes (t=0)") + 139 | theme(plot.title = element_text(hjust = 0.5)) 140 | print(out_plot) 141 | 142 | # Bigger Data for graphs - PLACEBO 143 | set.seed(123) 144 | library(dplyr) 145 | library(DIDmultiplegtDYN) 146 | 147 | TT <- 20; GG <- 1000 148 | df <- data.frame(id = 1:(GG*TT)) 149 | df$G <- ((df$id-1) %% GG)+1 150 | df$T <- floor((df$id-1)/GG) 151 | df$id <- NULL 152 | df <- df[order(df$G, df$T), ] 153 | df$D <- 0 154 | df$D <- ifelse(df$T == ((df$G - 1) %% 5) + 3 & (df$G - 1) %% 5 != 0, 1, df$D) 155 | df <- df %>% group_by(.data$G) %>% mutate(D_stag_temp = cumsum(.data$D)) %>% ungroup() 156 | df <- df %>% group_by(.data$G) %>% mutate(D_stag = cumsum(.data$D_stag_temp)) %>% ungroup() 157 | df$Y <- ifelse(df$T %% 4 == 0, runif(n = nrow(df)) * (1 + df$D_stag), NA) 158 | df$D_stag <- NULL 159 | df$D0 <- df$D[(df$G-1)*length(levels(factor(df$T)))+1] 160 | df$D_change <- as.numeric(abs(df$D - df$D0) != 0) 161 | df <- df %>% group_by(.data$G) %>% mutate(at_least_one_D_change = cumsum(.data$D_change)) %>% ungroup() 162 | df <- df %>% group_by(.data$G) %>% 163 | mutate(never_treated = as.numeric(sum(.data$D_change, na.rm = TRUE) == 0)) %>% 164 | mutate(F_g = ifelse(.data$never_treated == 1, max(df$T, na.rm = TRUE) +1, min(ifelse(.data$D_change == 0, NA, .data$T * .data$D_change), na.rm = TRUE))) %>% ungroup() 165 | df$subsample <- (4 - (df$F_g %% 4)) * (df$F_g %% 4 != 0) + 1 166 | df$subsample <- df$subsample * df$at_least_one_D_change 167 | df <- subset(df, !is.na(df$Y)) 168 | 169 | effects <- 2 170 | table <- NULL 171 | for (j in 1:4) { 172 | temp <- did_multiplegt_dyn( 173 | subset(df, df$subsample %in% c(0, j)), "Y", "G", "T", "at_least_one_D_change", 174 | graph_off = TRUE, effects = effects) 175 | rownames(temp$results$Effects) <- 176 | sapply(1:temp$results$N_Effects, function(x) paste0("Effect_", j + (x-1) * 4)) 177 | table <- rbind(table, temp$results$Effects) 178 | } 179 | rown <- unlist(strsplit(rownames(table), "_")) 180 | table <- cbind(table, as.numeric(rown[rown != "Effect"])) 181 | print(table[order(table[,ncol(table)]),1:(ncol(table)-1)]) 182 | 183 | library(ggplot2) 184 | table <- table[order(table[,ncol(table)]), ] 185 | table <- rbind(rep(0, ncol(table)), table) 186 | colnames(table)[ncol(table)] <- "Time" 187 | table <- as.data.frame(table) 188 | out_plot <- ggplot(table, aes(x = .data$Time, y = .data$Estimate, group = 1)) + 189 | geom_line(colour = "blue") + 190 | geom_errorbar(data = ~dplyr::filter(.x, table$Estimate != 0), aes(ymin = .data[["LB CI"]], ymax = .data[["UB CI"]]), 191 | position=position_dodge(0.05), width = 0.2, colour = "red") + 192 | geom_point(colour = "blue") + 193 | ggtitle("DID, from last period before treatment changes (t=0) to t") + 194 | xlab("Relative time to last period before treatment changes (t=0)") + 195 | theme(plot.title = element_text(hjust = 0.5)) 196 | print(out_plot) -------------------------------------------------------------------------------- /R/R/print.R: -------------------------------------------------------------------------------- 1 | #' @title A print method for did_multiplegt_dyn 2 | #' @name print.did_multiplegt_dyn 3 | #' @description A customized printed display for did_multiplegt_dyn output 4 | #' @param x A did_multiplegt_dyn object 5 | #' @param ... Undocumented 6 | #' @returns No return, custom print method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 7 | #' @export 8 | print.did_multiplegt_dyn <- function(x, ...) { 9 | cat("\n") 10 | 11 | by_levels <- c("_no_by") 12 | if (!is.null(x$by_levels)) { 13 | by_levels <- x$by_levels 14 | } 15 | 16 | for (b in 1:length(by_levels)) { 17 | 18 | if (by_levels[b] == "_no_by") { 19 | ref <- x 20 | } else { 21 | ref <- x[[paste0("by_level_",b)]] 22 | if (!is.null(x$args[["by"]])) { 23 | section <- paste(" By",x$args$by, "=", by_levels[b], "###") 24 | } else if (!is.null(x$args[["by_path"]])) { 25 | section <- paste0(" By treatment path: (", by_levels[b],") ", "###") 26 | } 27 | cat(noquote(strrep("#", 70 - nchar(section) - 1)));cat(section); 28 | cat("\n");cat("\n"); 29 | } 30 | 31 | cat(noquote(strrep("-", 70)));cat("\n"); 32 | cat(strrep(" ", 7));cat("Estimation of treatment effects: Event-study effects");cat("\n"); 33 | cat(noquote(strrep("-", 70)));cat("\n"); 34 | mat_print(ref$results$Effects[, 1:(6 + ((!is.null(x$args$weight))*2))]) 35 | cat("\n") 36 | if (!is.null(ref$results$p_jointeffects)) { 37 | cat(sprintf("Test of joint nullity of the effects : p-value = %.4f", ref$results$p_jointeffects)) 38 | cat("\n") 39 | } 40 | 41 | if (!is.null(ref$results$p_equality_effects)) { 42 | cat(sprintf("Test of equality of the effects : p-value = %.4f", ref$results$p_equality_effects)) 43 | cat("\n");cat("\n") 44 | } 45 | 46 | if (isTRUE(x$args$trends_lin)) { 47 | cat(noquote(strrep("-", 70)));cat("\n"); 48 | cat(strrep(" ", 4));cat("When the trends_lin is specified no average effects are reported");cat("\n"); 49 | cat(noquote(strrep("-", 70)));cat("\n"); 50 | 51 | } else { 52 | cat(noquote(strrep("-", 70)));cat("\n"); 53 | cat(strrep(" ", 4));cat("Average cumulative (total) effect per treatment unit");cat("\n"); 54 | cat(noquote(strrep("-", 70)));cat("\n"); 55 | mat_print(ref$results$ATE[, 1:(6 + ((!is.null(x$args$weight))*2))]) 56 | cat(sprintf("Average number of time periods over which a treatment effect is accumulated: %.4f", ref$results$delta_D_avg_total)) 57 | cat("\n") 58 | } 59 | cat("\n") 60 | 61 | if (ref$results$N_Placebos != 0) { 62 | cat(noquote(strrep("-", 70)));cat("\n"); 63 | cat(strrep(" ", 4));cat(" Testing the parallel trends and no anticipation assumptions");cat("\n"); 64 | cat(noquote(strrep("-", 70)));cat("\n"); 65 | mat_print(ref$results$Placebos[, 1:(6 + ((!is.null(x$args$weight))*2))]) 66 | if (is.null(x$args$bootstrap)) { 67 | cat("\n") 68 | cat(sprintf("Test of joint nullity of the placebos : p-value = %.4f", ref$results$p_jointplacebo)) 69 | cat("\n") 70 | } 71 | cat("\n") 72 | } 73 | 74 | if (!is.null(ref$design)) { 75 | if (ref$design$design_path == "console") { 76 | cat("\n") 77 | cat(noquote(strrep("-", 70)));cat("\n"); 78 | cat(strrep(" ", 4));cat(sprintf("Detection of treatment paths - %.0f periods after first switch", ref$design$design_const[1]));cat("\n"); 79 | cat(noquote(strrep("-", 70)));cat("\n"); 80 | print(ref$design$design_mat);cat("\n"); 81 | cat(sprintf("Treatment paths detected in at least %.2f%% of the %.0f switching groups for which %.0f effects could be estimated", ref$design$design_const[2], ref$design$design_const[3], ref$design$design_const[1])) 82 | cat(sprintf(" (Total %% = %.2f%%)", ref$design$design_const[4]));cat("\n");cat("\n"); 83 | cat("Design interpretation (first row):");cat("\n") 84 | n_groups <- ref$design$design_mat[1,1] 85 | d_start <- ref$design$design_mat[1,3] 86 | d_vec <- "(" 87 | for (i in 1:ref$design$design_const[1]) { 88 | d_vec <- paste0(d_vec, ref$design$design_mat[1, 3 + i],",") 89 | } 90 | d_vec <- paste0(substr(d_vec,1,nchar(d_vec)-1),")") 91 | cat(sprintf("%s groups started with treatment %s and experienced treatment path %s", n_groups, d_start, d_vec)) 92 | cat("\n") 93 | } 94 | else { 95 | cat(sprintf("Design exported to %s", ref$design$design_path)); cat("\n") 96 | } 97 | } 98 | 99 | if (!is.null(ref$date_first_switch)) { 100 | if (ref$date_first_switch$dfs_opt != "by_baseline_treat") { 101 | if (ref$date_first_switch$dfs_path == "console") { 102 | cat("\n") 103 | cat(noquote(strrep("-", 40)));cat("\n"); 104 | cat(strrep(" ", 7));cat("Switching dates");cat("\n"); 105 | cat(noquote(strrep("-", 40)));cat("\n"); 106 | cat("By any status quo treatment");cat("\n"); 107 | print(ref$date_first_switch$dfs_mat) 108 | cat("\n") 109 | } 110 | else { 111 | cat(sprintf("Switching dates exported to %s", ref$date_first_switch$dfs_path)); cat("\n") 112 | } 113 | } 114 | else { 115 | if (ref$date_first_switch$dfs_path == "console") { 116 | cat("\n") 117 | cat(noquote(strrep("-", 40)));cat("\n"); 118 | cat(strrep(" ", 7));cat("Switching dates");cat("\n"); 119 | cat(noquote(strrep("-", 40)));cat("\n"); 120 | for (l in 1:ref$date_first_switch$levels_baseline_treat) { 121 | cat(sprintf("Status quo treatment = %s", ref$date_first_switch[[paste0("level",l)]]));cat("\n"); 122 | print(ref$date_first_switch[[paste0("dfs_mat",l)]]) 123 | cat("\n") 124 | } 125 | } 126 | if (ref$date_first_switch$dfs_path != "console") { 127 | cat(sprintf("Switching dates exported to %s", ref$date_first_switch$dfs_path)); cat("\n") 128 | } 129 | } 130 | } 131 | 132 | if (!is.null(ref$normalized_weights)) { 133 | cat("\n") 134 | cat(noquote(strrep("-", 60)));cat("\n"); 135 | cat(strrep(" ", 13));cat("Weights on treatment lags");cat("\n"); 136 | cat(noquote(strrep("-", 60)));cat("\n"); 137 | print(ref$normalized_weights$norm_weight_mat) 138 | cat("\n") 139 | } 140 | 141 | if (!is.null(ref$results$predict_het)) { 142 | cat("\n") 143 | cat(noquote(strrep("-", 60)));cat("\n"); 144 | cat(strrep(" ", 13));cat("Predicting effect heterogeneity");cat("\n"); 145 | cat(noquote(strrep("-", 60)));cat("\n"); 146 | het_ref <- ref$results$predict_het$effect 147 | for (l in levels(factor(subset(het_ref, het_ref > 0)))) { 148 | het_tab <- subset(ref$results$predict_het, ref$results$predict_het$effect == l) 149 | het_mat <- as.matrix(het_tab[,c(3,4,6,7,8)]) 150 | rownames(het_mat) <- het_tab$covariate 151 | colnames(het_mat) <- c("Estimate", "SE", "LB CI", "UB CI", "N") 152 | pval <- mean(het_tab$pF) 153 | cat(sprintf("Effect %s:\n", l)) 154 | mat_print(het_mat) 155 | cat(sprintf("Test of joint nullity of the estimates : p-value = %.4f\n", pval));cat("\n") 156 | } 157 | if (length(levels(factor(subset(het_ref, het_ref < 0)))) > 0) { 158 | het_pl_ref <- subset(ref$results$predict_het, ref$results$predict_het$effect < 0) 159 | het_pl_ref$effect <- - het_pl_ref$effect 160 | for (l in levels(factor(het_pl_ref$effect))) { 161 | het_tab <- subset(het_pl_ref, het_pl_ref$effect == l) 162 | het_mat <- as.matrix(het_tab[,c(3,4,6,7,8)]) 163 | rownames(het_mat) <- het_tab$covariate 164 | colnames(het_mat) <- c("Estimate", "SE", "LB CI", "UB CI", "N") 165 | pval <- mean(het_tab$pF) 166 | cat(sprintf("Placebo %s:\n", l)) 167 | mat_print(het_mat) 168 | cat(sprintf("Test of joint nullity of the estimates : p-value = %.4f\n", pval));cat("\n") 169 | } 170 | } 171 | } 172 | } 173 | 174 | cat("\n") 175 | cat("The development of this package was funded by the European Union.");cat("\n") 176 | cat("ERC REALLYCREDIBLE - GA N. 101043899");cat("\n") 177 | 178 | } 179 | 180 | #' @title A summary method for did_multiplegt_dyn 181 | #' @name summary.did_multiplegt_dyn 182 | #' @description A customized summary display for did_multiplegt_dyn output 183 | #' @param object A did_multiplegt_dyn object 184 | #' @param ... Undocumented 185 | #' @returns No return, custom summary method for did_multiplegt_dyn objects. Estimation tables are fetched from the object and displayed in the same style as the Stata did_multiplegt_dyn command. 186 | #' @export 187 | summary.did_multiplegt_dyn <- function(object, ...) { 188 | print(object) 189 | } 190 | 191 | #' Auxiliary function for print method 192 | #' @param mat mat 193 | #' @returns No return, just prints the refined input. 194 | #' @noRd 195 | mat_print <- function(mat) { 196 | if (inherits(mat,"matrix")) { 197 | dis <- matrix(data = 0, nrow = nrow(mat) , ncol = ncol(mat)) 198 | dis[,1:4] <- sprintf("%s", format(round(mat[,1:4], 5), big.mark=",", scientific=FALSE, trim=TRUE)) 199 | dis[,5:ncol(dis)] <- 200 | sprintf("%s", format(round(mat[,5:ncol(dis)], 0), big.mark=",", scientific=FALSE, trim=TRUE)) 201 | rownames(dis) <- rownames(mat) 202 | colnames(dis) <- colnames(mat) 203 | print(noquote(dis[, , drop = FALSE])) 204 | } else { 205 | dis <- vector(length = length(mat)) 206 | dis[1:4] <- sprintf("%s", format(round(mat[1:4], 5), big.mark=",", scientific=FALSE, trim=TRUE)) 207 | dis[5:length(mat)] <- sprintf("%s", format(round(mat[5:length(mat)], 0), big.mark=",", scientific=FALSE, trim=TRUE)) 208 | names(dis) <- names(mat) 209 | print(noquote(dis[ , drop = FALSE])) 210 | } 211 | } --------------------------------------------------------------------------------