I am trying to understand the differences in estimating diff-in-diff in different formulations. To exemplify, I'm using data from Abadie, et al., (2010), in which the authors estimate the effect of a tobacco control program implemented in California in 1989. Ps: I know in the paper they used the Synthetic Control Method, and not diff-in-diff, just took the data as example, as they also compare they results to DiD.

First, I estimate the canonical diff-in-diff model

$$Y_{it} = \beta_0 + \beta_1 \textrm{Post}_t + \beta_2 \textrm{Treated}_i + \beta_3 \textrm{Treated}_i \textrm{Post}_t + e_{it}$$

import pandas as pd
import statsmodels.api as sm
from linearmodels import PanelOLS

data[['Treated', 'Post']]=0
data.loc[data['state']=='California', 'Treated']=1
data.loc[data['year']>1988, 'Post']=1
X=data[['Post', 'Treated', 'PostxTreated']]
X = sm.add_constant(X)

And got the following results:

                  coef    std err          t      P>|t|      [0.025      0.975]
const          130.5695      1.087    120.112      0.000     128.437     132.702
Post           -28.5114      1.747    -16.318      0.000     -31.939     -25.084
Treated        -14.3590      6.789     -2.115      0.035     -27.678      -1.040
PostxTreated   -27.3491     10.911     -2.506      0.012     -48.756      -5.942

The estimated treatment effect: $\hat{\beta_{3}} = -27.349$. I understand this is numerically equivalent to the (Two-Way) Fixed Effects model, so when I estimate this (being only interested in $\tau$ I use the package for panel and add options to time and unit fixed effects):

$$y_{it}=\alpha_{i}+ \gamma_{t} +\tau_{it}TreatedxPost +\epsilon_{it}$$

df_panel=data.set_index(['state', 'year'])
model=PanelOLS(Y,X, entity_effects=True, time_effects=True )

I got this:

                              Parameter Estimates                               
              Parameter  Std. Err.     T-stat    P-value    Lower CI    Upper CI
const            119.16     0.3423     348.14     0.0000      118.49      119.84
PostxTreated    -27.349     4.4095    -6.2024     0.0000     -36.001     -18.698

As expected, $\hat{\tau}=\hat{\beta_{3}} = -27.349$

1st QUESTION: Why are the standard errors so different? Which one is correct?

2nd QUESTION: Which specification should I use if I want to include covariates?

If I include the covariate retprice, results from both estimations are completely different: In the first model, replacing X=data[['Post', 'Treated', 'PostxTreated']] by X=data[['Post', 'Treated', 'PostxTreated', 'retprice]] I got:

                   coef    std err          t      P>|t|      [0.025      0.975]
const          146.0978      1.889     77.352      0.000     142.392     149.803
Post            -1.4389      3.216     -0.447      0.655      -7.749       4.871
Treated        -13.8457      6.532     -2.120      0.034     -26.662      -1.030
PostxTreated   -21.2837     10.517     -2.024      0.043     -41.917      -0.650
retprice        -0.2407      0.024     -9.874      0.000      -0.289      -0.193

In the second model, using X=df_panel[['PostxTreated', 'retprice']], results are:

                              Parameter Estimates                               
              Parameter  Std. Err.     T-stat    P-value    Lower CI    Upper CI
const            171.71     4.6146     37.211     0.0000      162.66      180.77
PostxTreated    -15.101     4.3142    -3.5002     0.0005     -23.565     -6.6360
retprice        -0.4861     0.0426    -11.415     0.0000     -0.5697     -0.4026

What am I estimating in each of these cases? Which of the estimators is biased, and why?

  • $\begingroup$ You tag people like this by typing @ followed by the name. You should delete the comment on the other post. $\endgroup$
    – dimitriy
    Commented Mar 2, 2023 at 3:56
  • $\begingroup$ Thank you @dimitriy . I think the other post was deleted $\endgroup$
    – Oalvinegro
    Commented Mar 2, 2023 at 17:04

2 Answers 2


In your first question, differences in the standard errors might be related to serial correlation of residuals. Look at Bertrand, Marianne, Esther Duflo, and Sendhil Mullainathan. "How much should we trust differences-in-differences estimates?." The Quarterly journal of economics 119.1 (2004): 249-275.


It would be better to use the second model rather than OLS since that will have the more correct degrees of freedom. The panel model adjusts for the fact that you calculated the mean in each state before transforming your data. If you add state dummies to your OLS model, you should get something very close to the panel version.

I think you should also use

result = model.fit(cov_type='clustered', cluster_entity=True)

This clusters the standard errors by state to allow correlation in shocks within each state.

For these two reasons, the second model with clustered SEs should be used with time-varying covariates.

You are estimating the post-pre change in cigarette consumption in CA less the corresponding change in the other ~38 states, possibly adjusted for covariates. This gives you the average treatment effect of the tax for CA (assuming DID assumptions hold).

The reason your effect changes so much when you add the control is that retprice is the average retail price per pack of cigarettes (in cents) in that year in each state, and it includes state sale taxes (see Appendix A in the linked paper). The treatment in Proposition 99 increased California’s cigarette excise tax by 25 cents per pack, earmarked the tax revenues to health and anti-smoking education budgets, funded anti-smoking media campaigns, and spurred local clean indoor-air ordinances throughout the state. So with this control, you are effectively removing the part of the effect that has to do with higher taxes. Not surprisingly, the effect of the policy now gets cut in half.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.