11/27/25, 3:44 PM difference-in-differences-in-python
The difference-in-differences method is a quasi-experimental approach
that compares the changes in outcomes over time between a population
enrolled in a program (the treatment group) and a population that is not
(the comparison group). It is a useful tool for data analysis.
The dataset is adapted from the dataset in Card and Krueger (1994), which estimates
the causal effect of an increase in the state minimum wage on the employment.
On April 1, 1992, New Jersey raised the state minimum wage from 4.25 USD to 5.05
USD while the minimum wage in Pennsylvania stays the same at 4.25 USD.
data about employment in fast-food restaurants in NJ (0) and PA (1) were collected
in February 1992 and in November 1992.
384 restaurants in total after removing null values
The calculation of DID is simple:
mean PA (control group) employee per restaurant before/after the treatment is
23.38/21.1, so the after/before difference for the control group is -2.28 (21.1 - 23.38)
mean NJ (treatment group) employee per restaurant before/after the treatment is
20.43/20.90, so the after/before difference for the treatment group is 0.47 (20.9 -
20.43)
the difference-in-differences (DID) is 2.75 (0.47 + 2.28), which is (the after/before
difference of the treatment group) - (the after/before difference of the control
group)
The same DID result can be obtained via regression, which allows adding control
variables if needed:
y = β0 + β1 ∗ g + β2 ∗ t + β3 ∗ (t ∗ g) + ε
g is 0 for the control group and 1 for the treatment group
t is 0 for before and 1 for after
we can insert the values of g and t using the table below and see that coefficient ( ) of β3
the interaction of g and t is the value for DID
Control Group (g=0) Treatment Group (g=1)
Before (t=0) β0 β0 + β1
After (t=1) β0 + β2 β0 + β1 + β2 + β3
Difference β2 β2 + β3 β3 (DID)
The p-value for in this example is not significant, which means that the average total
β3
employees per restaurant increased after the minimal salary raise by 2.75 FTE (full-time
[Link] 1/3
11/27/25, 3:44 PM difference-in-differences-in-python
equivalent) but the result may be just due to random factors.
In [1]: import pandas as pd
In [2]: df = pd.read_csv('../input/propensity-score-matching/[Link]')
[Link]()
<class '[Link]'>
RangeIndex: 384 entries, 0 to 383
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 state 384 non-null int64
1 total_emp_feb 384 non-null float64
2 total_emp_nov 384 non-null float64
dtypes: float64(2), int64(1)
memory usage: 9.1 KB
In [3]: [Link]()
Out[3]: state total_emp_feb total_emp_nov
0 0 40.50 24.0
1 0 13.75 11.5
2 0 8.50 10.5
3 0 34.00 20.0
4 0 24.00 35.5
In [4]: [Link]('state').mean()
Out[4]: total_emp_feb total_emp_nov
state
0 23.380000 21.096667
1 20.430583 20.897249
In [5]: # check by calculating the mean for each group directly
# 0 PA control group, 1 NJ treatment group
mean_emp_pa_before = [Link]('state').mean().iloc[0, 0]
mean_emp_pa_after = [Link]('state').mean().iloc[0, 1]
mean_emp_nj_before = [Link]('state').mean().iloc[1, 0]
mean_emp_nj_after = [Link]('state').mean().iloc[1, 1]
print(f'mean PA employment before: {mean_emp_pa_before:.2f}')
print(f'mean PA employment after: {mean_emp_pa_after:.2f}')
print(f'mean NJ employment before: {mean_emp_nj_before:.2f}')
print(f'mean NJ employment after: {mean_emp_nj_after:.2f}')
pa_diff = mean_emp_pa_after - mean_emp_pa_before
[Link] 2/3
11/27/25, 3:44 PM difference-in-differences-in-python
nj_diff = mean_emp_nj_after - mean_emp_nj_before
did = nj_diff - pa_diff
print(f'DID in mean employment is {did:.2f}')
mean PA employment before: 23.38
mean PA employment after: 21.10
mean NJ employment before: 20.43
mean NJ employment after: 20.90
DID in mean employment is 2.75
In [ ]: # group g: 0 control group (PA), 1 treatment group (NJ)
# t: 0 before treatment (min wage raise), 1 after treatment
# gt: interaction of g * t
# data before the treatment
df_before = df[['total_emp_feb', 'state']]
df_before['t'] = 0
df_before.columns = ['total_emp', 'g', 't']
# data after the treatment
df_after = df[['total_emp_nov', 'state']]
df_after['t'] = 1
df_after.columns = ['total_emp', 'g', 't']
# data for regression
df_reg = [Link]([df_before, df_after])
# create the interaction
df_reg['gt'] = df_reg.g * df_reg.t
df_reg
In [ ]: # regression via sklearn
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
X = df_reg[['g', 't', 'gt']]
y = df_reg.total_emp
[Link](X, y)
lr.coef_ # the coefficient for gt is the DID, which is 2.75
In [ ]: # regression via statsmodels
# result is not significant
from [Link] import ols
ols = ols('total_emp ~ g + t + gt', data=df_reg).fit()
print([Link]())
[Link] 3/3