Difference-in-Differences
Before the Lab
- Read the summary of the research paper by David Card and Alan Krueger available on Moodle.
Lab Overview
- Introduction to panel data analysis
- Difference-in-differences estimation
Minimum Wages and Employment: A case study of the fast-food industry in New Jersey and Pennsylvania
By David Card and Alan Krueger, American Economic Review, 1994
David Edward Card is a Canadian labor economist and Professor of Economics at the University of California, Berkeley.
Alan Bennett Krueger was an American economist, Bendheim Professor of Economics and Public Affairs at Princeton University and Research Associate at the National Bureau of Economic Research.
ABSTRACT: On April 1, 1992, New Jersey’s minimum wage rose from $4.25 to $5.05 per hour. To evaluate the impact of the law we surveyed 410 fast-food restaurants in New Jersey and eastern Pennsylvania before and after the rise. Comparisons of employment growth at stores in New Jersey and Pennsylvania (where the minimum wage was constant) provide simple estimates of the effect of the higher minimum wage. We also compare employment changes at stores in New Jersey that were initially paying high wages (above $5) to the changes at lower-wage stores. We find no indication that the rise in the minimum wage reduced employment. (JEL 530, 523)
See: Nobel Prize in Economics 2021
Overview
April \(1^{st}\) 1992: New Jersey (USA) raises minimum wage from $4.25/hour to $5.05/hour.
This increase gave New Jersey the highest state-level minimum wage in the US and was strongly opposed by the state’s businesses.
Conventional economic theory unambiguously predicts that an increase in the minimum wage will lead perfectly competitive employers to cut down employment.
Research Question: What was the impact of the increase in minimum wage on employment?
1. Getting Started
Download Lab 7’s materials from Moodle:
- Save provided data set in your
datafolder in BRM-Labs project folder. - Save provided R script in your
codefolder in BRM-Labs project folder.
- Save provided data set in your
Open the provided lab 7’s R script.
Setup your R environment.
# Clean work environment
rm(list = ls()) # USE with CAUTION: this will delete everything in your environment
# Install packages
# install.packages("lmtest")
# install.packages("sandwich")
# Load packages
library(tidyverse)
library(stargazer)
library(ggthemes)
library(GGally)
library(skimr)
library(corrr)
library(infer)
library(janitor)
library(lmtest)
library(sandwich)
- Load the data.
# Load data
load("data/fastfood.RData")
# Check if is tibble
is_tibble(tb.fastfood)
[1] TRUE
Here is the variable description:
id: store idtime: indicator variable = 1 if after minimum wage increaseemptot: number of full-time equivalent (FTE) employeesstate: indicator variable = 1 if New Jerseychain: anonymous restaurant chain ID (e.g. McDonald’s, Burger King, etc.)co_owned: indicator of company-owned versus franchisee-ownedatmin: indicator variable = 1 if wage is at minimum wage levelwage: hourly wagehrsopen: number of hours openpmeal: price of a full mealfracft: fraction of full time employees
2. Analytical considerations
At what level of analysis would you collect data?
- Worker level
- Company level
- Industry level
What data (variables for analysis) would you collect?
- What are the key variables?
- What other factors could be impacted by this change besides employment?
How would you collect the data?
- Survey the companies in person, by telephone, or by mail. (Card and Krueger)
- Get payroll records from each company. (Neumark and Wascher)
- Use Bureau of Labor Statistics’ (BLS) data. (Card and Krueger)
At what point(s) in time would you collect the data?
What analysis would you perform?
Card and Krueger use data from fast-food restaurants collected through a telephone survey. Note the following advantages of this sample:
- They are a leading employer of low-wage workers
- They comply with minimum wage regulations and thus are expected to comply with the change in legislation
- Their job requirements and products are relatively homogeneous and thus it is easy to obtain reliable measures of employment, wages, and product prices
- They are known to have a high response rate to telephone surveys
The authors collected data in two waves:
- First wave: a little over one month before the increase in New Jersey’s minimum wage
- Second wave: eight months after the minimum wage increase
To estimate the impact of the increase in minimum wage on unemployment, consider the following approaches:
2.1 Approach 1
Calculate the difference in New Jersey’s employment level before and after the change in minimum wage
\[employment_t = \beta_0 + \beta_1Time_{t} + \mu_t\]
What are the problems with this approach?
There may be variables in the error term that impact employment and also changed from \(Time_0\) to \(Time_1\). This would result in a biased \(\beta_1\). For instance, if a recession took place from \(Time_0\) to \(Time_1\), this would be related to the variable \(Time\) and also to \(employment\), preventing us from learning the true impact of the minimum wage increase on employment. The coefficient for \(Time\) just shows the difference in employment between the periods after and before, but does not necessarily reveal the effect of the new policy.
2.2 Approach 2
Calculate the difference in New Jersey’s employment level and another state’s employment level (not affected by the minimum wage policy) after the change in minimum wage
\[employment_i = \beta_0 + \beta_1TreatedState_i + \mu_i\]
What are the problems with this approach?
There may be variables in the error term that impact employment and that are state specific. This would result in a biased \(\beta_1\). For instance, if the two states have different political parties in power, the party’s policies may impact both the employment level and the decision to raise minimum wage. This would result in a violation of MLR.4 (zero conditional mean assumption), and our estimate of the impact of the minimum wage increase on employment would suffer from omitted variable bias. Similarly, even if we observe a difference between states, this difference could have already existed in the period before the new law was passed. The coefficient for \(TreatedState\) just shows the difference in employment between the treated State and the control State, but does not necessarily reveal the effect of the new policy.
2.3 Approach 3
Difference-in-differences
\[employment_{it} = \beta_0 + \beta_1TreatedState_i + \beta_2Time_{t} + \beta_3TreatedState_i\times Time_{t} + \mu_{it}\]
To proceed with a Difference-in-differences analysis, the authors collected data for New Jersey (NJ) and Pennsylvania (PA). Pennsylvania shares a border with the state of New Jersey but the minimum wage was unchanged there. It serves as a control group.
3. Exploratory Data Analysis
3.1 Data Structure
How is this data set different from the data sets we have seen so far?
What is our unit of analysis? The fast-food store.
# Print the first rows of the dataset
head(tb.fastfood)
This is a panel data set. Each fast-food store’s id
shows up twice in the data, once for time period 0 and once for time
period 1.
Is this a balanced panel? Meaning, do we observe each store in each of the time periods?
# Number of observations per time period
tb.fastfood %>%
group_by(time) %>%
summarize(n = n()) %>%
ungroup()
# Number of time periods per ID
tb.fastfood %>%
group_by(id) %>%
summarize(n = n()) %>%
filter(n!=2) %>%
ungroup()
3.2 Data Pre-processing
We will now look at the summary statistics of our numerical variables. Note that some of the variables are affected by missing data.
# Summary statistics
stargazer( data.frame(tb.fastfood %>% select(-id))
, type = "text"
, header = FALSE
, font.size = "small"
, title = "Summary Statistics")
Summary Statistics
==========================================
Statistic N Mean St. Dev. Min Max
------------------------------------------
time 714 0.500 0.500 0 1
emptot 714 20.979 9.508 0.000 85.000
state 714 0.812 0.391 0 1
chain 714 2.090 1.079 1 4
co_owned 714 0.356 0.479 0 1
atmin 714 0.322 0.468 0 1
wage 708 4.805 0.359 4.250 6.250
hrsopen 707 14.452 2.821 7.000 24.000
pmeal 672 3.341 0.658 2.140 5.860
fracft 708 0.344 0.239 0.000 0.904
------------------------------------------
To make this example simpler for today’s lab we will clean the data set by eliminating stores with missing information:
# Create indicator for missing data by ID
tb.fastfood <- tb.fastfood %>%
mutate(missing = as.double(complete.cases(tb.fastfood)==FALSE)) %>%
group_by(id) %>%
mutate(missing = max(missing)) %>%
ungroup()
# Check data
# View(tb.fastfood %>% filter(missing>0))
# Check how many stores have missing data
tb.fastfood %>%
filter(missing>0) %>%
summarize(n = n_distinct(id))
# Keep only IDs with complete information
tb.fastfood <- tb.fastfood %>% filter(missing==0)
tb.fastfood <- tb.fastfood %>% select(-missing)
# Alternatively you could drop the stores with NAs using:
tb.fastfood <- tb.fastfood %>%
drop_na %>%
group_by(id) %>%
mutate(n = n()) %>%
filter(n==2) %>%
select(-n) %>%
ungroup()
We will also format the state and time
variables as factors:
# Create state and time factors
tb.fastfood <- tb.fastfood %>%
mutate( state = factor(state, levels = c(0,1), labels = c("PA", "NJ"))
, time = factor(time, levels = c(0,1), labels = c("Before", "After")))
3.3 Descriptive Statistics
We can now produce a summary statistics table for the clean data set.
# Summary statistics
stargazer(data.frame(tb.fastfood %>% select(-id))
, type = "text"
, header = FALSE
, font.size = "small"
, title = "Summary Statistics")
Summary Statistics
==========================================
Statistic N Mean St. Dev. Min Max
------------------------------------------
emptot 632 20.691 8.731 5.000 70.500
chain 632 2.092 1.075 1 4
co_owned 632 0.348 0.477 0 1
atmin 632 0.326 0.469 0 1
wage 632 4.804 0.358 4.250 6.250
hrsopen 632 14.295 2.679 7.000 24.000
pmeal 632 3.355 0.662 2.390 5.860
fracft 632 0.343 0.240 0.000 0.904
------------------------------------------
Change in wages
The following plot shows the change in the distribution of wages for the treatment and control group, before and after the change. We can see that both groups had a very similar wage distribution before the change in minimum wage was implemented in New Jersey. After the change, all restaurants in New Jersey that were paying less than $5.05/hour started paying at that rate, complying with the new legislation.
# Plot the distribution of wages per state before and after the policy
ggplot(data = tb.fastfood, aes(x = wage)) +
geom_histogram(binwidth=0.2) +
facet_wrap(~ state + time) + theme_bw()
Please note that the plot above is not testing the assumption of a common trend prior to treatment. In order to test the common trend assumption, one needs to have data for the control and treatment groups at more than one time point before the treatment takes place. This data set does not allow us to test that assumption as we only have two data points — before and after treatment.
Change in employment
Consider the following questions:
What kind of plot does economic theory predict?
How does this plot differ from our expectations?
# Boxplot of full time employment per state before and after
ggplot(data = tb.fastfood, aes(x = time, y = emptot, fill = state)) +
geom_boxplot() +
theme_bw() +
labs(x = "Time", y = "FTE Employment", fill="State") +
scale_fill_fivethirtyeight()
Economic theory predicts a reduction in employment resulting from the increase in minimum wage. The plot suggests that this did not happen. On the contrary, employment seems to have increased slightly for the treated group (New Jersey).
Means of key variables for PA and NJ before and after policy
We can use the full data set to build a table with the before and after means for treatment and control groups.
# Create a table with the variable means for treatment and control groups before and after the policy
tb.bf.aft <- tb.fastfood %>%
group_by(state, time) %>%
summarise(
mean_emptot = mean(emptot),
mean_wage = mean(wage),
mean_pmeal = mean(pmeal),
mean_hrsopen = mean(hrsopen),
mean_fracft = mean(fracft)) %>%
ungroup()
tb.bf.aft
At \(Time_0\), average employment was 23.6 full-time equivalent (FTE) workers per store in Pennsylvania, compared with an average of 20.0 in New Jersey. Despite the increase in wages, FTE employment increased in New Jersey. Whereas New Jersey stores were initially smaller, employment gains in New Jersey coupled with losses in Pennsylvania led to a small and statistically insignificant interstate difference at \(Time_1\).
We can use the data on the table to build a new plot.
# Diff-in-diff plot
ggplot( data = tb.bf.aft
, aes(x = time, y = mean_emptot, group = state, color = state)) +
geom_point() + geom_line() + theme_bw() +
scale_color_fivethirtyeight() + ylim(15, 25)
4. Difference-in-Differences
4.1 Manual Diff-in-Diff
The differences-in-differences strategy amounts to comparing the change in mean FTE in New Jersey to the change in mean FTE in Pennsylvania.
\[\text{Treatment Effect} = (\bar{Y}_{NJ,T1}-\bar{Y}_{NJ,T0})-(\bar{Y}_{PA,T1}-\bar{Y}_{PA,T0})\]
# Difference in differences (manual)
(20.47745 - 20.00588) - (21.54098-23.59836)
[1] 2.5289
Surprisingly, employment rose in New Jersey relative to Pennsylvania after the minimum wage change. New Jersey stores were initially smaller than their Pennsylvania counterparts but grew relative to Pennsylvania stores after the rise in the minimum wage. The relative gain (the “difference in differences” of the changes in employment) is 2.52 FTE employees.
We can also write the treatment effect as:
\[\text{Treatment Effect} = (\bar{Y}_{NJ,T1}-\bar{Y}_{PA,T1})-(\bar{Y}_{NJ,T0}-\bar{Y}_{PA,T0})\]
# Difference in differences (manual)
(20.47745 - 21.54098) - (20.00588-23.59836)
[1] 2.5289
4.2 Regression
We can estimate the difference-in-differences (DiD)
estimator in a regression framework using the lm()
function. This approach has several advantages:
It is easy to calculate standard errors.
We can control for other covariates, which may reduce residual variance and lead to more precise estimates.
It naturally accommodates extensions to more than two periods.
It can be adapted to varying treatment intensities (e.g., different levels of a policy across groups).
Although we are working with panel data, for a two-period
DiD setting, the use of lm() is sufficient and
preferred for its simplicity and transparency.
However, since the same unit (e.g., store, individual, firm) appears
more than once, the assumption of independent errors is
violated. To address this, we use robust
(heteroskedasticity-consistent) standard errors, which can be
computed with the sandwich and lmtest
packages.
Note: While lm() is appropriate and
convenient for estimating difference-in-differences with
two-period panel data, for panels with more
than two time periods, it is recommended to use the
plm() function from the plm
package.
# Estimate model
lm1 <- lm(emptot ~ state + time + state:time, data = tb.fastfood)
# Robust standard errors
robust_se1 <- sqrt(diag(vcovHC(lm1, type = "HC3")))
# Stargazer with robust SEs manually added
stargazer(lm1, lm1,
se = list(NULL, robust_se1),
column.labels = c("DiD", "DiD Robust SE"),
type = "text",
title = "Difference-in-Differences Results",
omit.stat = c("ser", "f"),
no.space = TRUE,
header = FALSE,
font.size = "small",
dep.var.caption = "",
dep.var.labels.include = FALSE,
model.names = FALSE)
Difference-in-Differences Results
==============================================
DiD DiD Robust SE
(1) (2)
----------------------------------------------
stateNJ -3.592*** -3.592**
(1.238) (1.688)
timeAfter -2.057 -2.057
(1.573) (1.918)
stateNJ:timeAfter 2.529 2.529
(1.751) (2.052)
Constant 23.598*** 23.598***
(1.112) (1.610)
----------------------------------------------
Observations 632 632
R2 0.014 0.014
Adjusted R2 0.010 0.010
==============================================
Note: *p<0.1; **p<0.05; ***p<0.01
Notes:
state,time, andstate:timerepresent the classic DiD model:state: treated vs controltime: before vs afterstate:time: the DiD interaction (your estimate of interest)
We use
vcovHC()to correct standard errors (HC3 is a good robust default).
Are the coefficients statistically significant? How do we interpret the regression coefficients?
emptot00: average FTE employment in Pennsylvania at T0 (\(\beta_0\))emptot10: average FTE employment in New Jersey at T0 (\(\beta_0\) + \(\beta_1\))emptot01: average FTE employment in Pennsylvania at T1 (\(\beta_0\) + \(\beta_2\))emptot11: average FTE employment in New Jersey at T1 (\(\beta_0\) + \(\beta_1\) + \(\beta_2\) + \(\beta_3\))
What is the correspondence between the betas and the values from the table?
\(\beta_0 = 23.59836\)
# Store vector with diff-in-diff coefficients
coefs <- coefficients(lm1)
# Intercept
coefs[1]
(Intercept)
23.598
\(\beta_1 = emptot10 - emptot00 =\)
20.00588 - 23.59836
[1] -3.5925
# Coefficient of state
coefs[2]
stateNJ
-3.5925
\(\beta_2 = emptot01 - emptot00 =\)
21.54098 - 23.59836
[1] -2.0574
# Coefficient of time
coefs[3]
timeAfter
-2.0574
\(\beta_3 = (emptot11 - emptot10)-(emptot01 - emptot00) =\)
(20.47745 - 20.00588) - (21.54098 - 23.59836)
[1] 2.5289
# Coefficient of interaction term state*time
coefs[4]
stateNJ:timeAfter
2.5289
4.3 Adding Controls
We can control for other factors to get a better estimate of the
treatment effect. Note that the introduction of the co-ownership and
chain controls to the model, did not impact the estimated coefficients
of timeAfter and stateNJ:timeAfter. This
happens when the additional explanatory variables are uncorrelated with
the variables that were already included in the model.
# Extended model with controls
lm2 <- update(lm1, . ~ . + factor(co_owned) + factor(chain))
# Robust standard errors (HC3)
robust_se2 <- sqrt(diag(vcovHC(lm2, type = "HC3")))
# Output regression table with robust SEs
stargazer(lm1, lm2,
se = list(robust_se1, robust_se2),
type = "text",
title = "Difference-in-Differences with and without Controls",
column.labels = c("Simple", "With Controls"),
dep.var.caption = "",
dep.var.labels.include = FALSE,
model.names = FALSE,
no.space = TRUE,
omit.stat = c("ser", "f"),
header = FALSE,
font.size = "small")
Difference-in-Differences with and without Controls
==============================================
Simple With Controls
(1) (2)
----------------------------------------------
stateNJ -3.592** -2.906**
(1.688) (1.475)
timeAfter -2.057 -2.057
(1.918) (1.629)
factor(co_owned)1 -0.859
(0.604)
factor(chain)2 -10.680***
(0.671)
factor(chain)3 -3.187***
(0.800)
factor(chain)4 -1.823*
(1.086)
stateNJ:timeAfter 2.529 2.529
(2.052) (1.752)
Constant 23.598*** 26.770***
(1.610) (1.572)
----------------------------------------------
Observations 632 632
R2 0.014 0.251
Adjusted R2 0.010 0.242
==============================================
Note: *p<0.1; **p<0.05; ***p<0.01
After considering other factors, the diff-in-diff estimate
stateNJ:timeAfter (treatment effect) is still not
statistically significant. There is no significant difference between
the relative changes in employment between states.