6.11 Two way fixed effect (TWFE) Revisited

We have already seen the TWFE and its importance in accounting for unobserved heterogeneity. The TWFE is heavily linked to the difference-in-differences setting (perhaps mistakenly). However, note that the TWFE estimator is not equal to the DiD estimator unless the treatment effects are homogeneous across both units and time.

\[\begin{equation} \label{eq:TWFE} Y_{it} = \theta_{t} + \eta_{i} + \alpha D_{it} + v_{it} \;.....TWFE \end{equation}\]

Here, \(Y_{it}\) is the outcome of individual \(i\) in period \(t\) (\(t \in \{1,\;2,\;...,\;T\}\))

\(\theta_{t}\) is the time fixed effects; \(\eta_{i}\) is the unit fixed effect

\(D_{it}\) captures whether individual \(i\) is treated in time \(t\)

Equation above is the TWFE.

In two groups and two-period setting, the above equation can be estimated in a number of different ways. Let’s simulate data to look.

Assign treatment effect = 20

Data Arrange 1: Demeaning to get rid of \(\eta_i\) from TWFE equation (Within Estimator)

Let’s look at the concept behind the within estimator. In the two-period two-group case, TWFE can be written as: \[\begin{equation} Y_{i1} = \theta_{1} + \eta_{i} + \alpha D_{i1} + v_{i1} \nonumber \\ Y_{i2} = \theta_{2} + \eta_{i} + \alpha D_{i2} + v_{i2} \end{equation}\]

where, \(i\) is represented by 1 (treatment group) and 0 (untreated group).

Adding the sub-equations and dividing by the number of time period \((T=2)\) yields: \[\begin{equation} \frac{Y_{i1}+Y_{i2}}{2} = \frac{\theta_{1}+\theta_{2}}{2} + \frac{2\eta_{i}}{2} + \frac{\alpha (D_{i1}+D_{i2})}{2} + \frac{v_{i1}+v_{i2}}{2} \\ Y_{i} = \frac{\theta_{1}+\theta_{2}}{2} + \eta_{i} + \alpha D_{i} + v_{i} \nonumber \end{equation}\]

Substracting the above equation from the TWFE yields the following: \[\begin{equation} Y_{it}-Y_{i} = \theta_{t} - \frac{\theta_{1}+\theta_{2}}{2} + \alpha (D_{it}-D_i) + (v_{it}-v_i) \end{equation}\]

The code shows data arranging for the within estimator.

###########################
# Treatment group
###########################
treat_t <- rep(1, 1000)
period_t <- rep(c(0, 1), each = 500)
id <- rep(seq(1, 500, 1), 2) #for the panel nature of data
y_treat <- 20 * period_t + 7  + rnorm(1000, 0, 5) 
treatdata <- data.frame(treat = treat_t, period = period_t, Y = y_treat, id = id)
treatdata <- treatdata %>% mutate(Ytrans = Y - mean(Y),
                                  D = treat * period - mean(treat * period))

##########################
# control group
##########################
control_t <- rep(0, 1000)  
period_c <- rep(c(0, 1), each = 500)
id <- rep(seq(501, 1000, 1), 2)
y_control <- 3 +  rnorm(1000, 0, 5) 
controldata = data.frame(treat = control_t, period = period_c, Y = y_control, id = id)
controldata <- controldata %>% mutate(Ytrans = Y - mean(Y),
                                  D = treat * period - mean(treat * period))

data = rbind(treatdata, controldata)

Data Arrange 2: First differencing

Let’s briefly look at the concept behind first differencing. Write TWFE as: \[\begin{equation} Y_{i1} = \theta_{1} + \eta_{i} + \alpha D_{i1} + v_{i1} \nonumber \\ Y_{i2} = \theta_{2} + \eta_{i} + \alpha D_{i2} + v_{i2} \end{equation}\]

for \(i \in \{0,\;1\}\).

Then, \[\begin{equation} Y_{i2} - Y_{i1} = \theta_{2} - \theta_{1} + \alpha (D_{i2}-D_{i1}) + (v_{i2} - v_{i1}) \end{equation}\]

The code shows data arranging for the first difference estimator.

# First the treated group
fd_treat1 <- treatdata %>% filter(period == 0) %>% dplyr::select(-c("Ytrans"))
colnames(fd_treat1) <- c("treat1", "period1", "Y1", "id")
fd_treat2 <- treatdata %>% filter(period == 1)%>% dplyr::select(-c("Ytrans"))
colnames(fd_treat2) <- c("treat2", "period2", "Y2", "id")
fd_treat <- merge(fd_treat1, fd_treat2, by = "id", all.x = T)
fd_treat <- fd_treat %>% mutate(Y_FD = Y2 - Y1,
                                D = (period2 * treat2) - (period1 * treat1)) 

# Then the control group
fd_control1 <- controldata %>% filter(period == 0) %>% dplyr::select(-c("Ytrans"))
colnames(fd_control1) <- c("treat1", "period1", "Y1", "id")
fd_control2 <- controldata %>% filter(period == 1)%>% dplyr::select(-c("Ytrans"))
colnames(fd_control2) <- c("treat2", "period2", "Y2", "id")
fd_control <- merge(fd_control1, fd_control2, by = "id", all.x = T)
fd_control <- fd_control %>% mutate(Y_FD = Y2 - Y1,
                                D = (period2 * treat2) - (period1 * treat1)) 

FDdata = rbind(fd_treat, fd_control)