26  Instrumental variables in fixed-effects Poisson models

Published

August 29, 2024

26.1 Why a control function is needed

Suppose a panel count-data model has the conditional mean

\[E[y_{it} \mid x_{it}, c_i] = c_i \exp(x_{it} \beta)\]

If a variable in \(x_{it}\) is endogenous, linear two-stage least squares does not fit this exponential conditional mean. Nonlinear IV commands such as Stata’s ivpoisson also cannot absorb high-dimensional individual and time fixed effects.

Lin and Wooldridge (2019) propose a two-step control-function estimator for this setting.

26.2 The two-step estimator

Let \(w_{it}\) be a continuous endogenous regressor and \(z_{it}\) an excluded instrument. The first stage regresses \(w_{it}\) on the instrument and exogenous controls while absorbing all fixed effects:

\[w_{it} = z_{it}\pi + x_{it1}\gamma + c_i + f_t + v_{it}\]

We save the OLS residual \(\hat v_{it}\) and include it in a fixed-effects Poisson model:

\[E[y_{it} \mid w_{it}, x_{it1}, c_i, f_t, \hat{v}_{it}] = c_i f_t \exp(w_{it}\beta_1 + x_{it1}\beta_2 + \rho \hat{v}_{it})\]

In Stata, the two commands are reghdfe ..., resid and ppmlhdfe. In R, we can use fixest::feols() and fixest::fepois(). Testing \(H_0:\rho=0\) gives a test of exogeneity. Because \(\hat v_{it}\) is estimated, the usual second-stage standard errors are not valid. I use a cluster bootstrap that repeats both stages.

26.3 A continuous endogenous variable in Stata

I use Stata’s website.dta, which has 500 observations. visits is the count outcome, time is the endogenous regressor, phone is the instrument, and frfam is an exogenous control. The model absorbs fixed effects for the 13 advertising campaigns and for female.

Code
webuse website, clear

* Step 1: Linear first stage absorbing both fixed effects
reghdfe time phone frfam, absorb(ad female) resid
predict double u2h_fe, resid

* Step 2: FE Poisson with control function residual
ppmlhdfe visits time u2h_fe frfam, absorb(ad female)
(Visits to website)

(dropped 1 singleton observations)
(MWFE estimator converged in 3 iterations)

HDFE Linear regression                            Number of obs   =        499
Absorbing 2 HDFE groups                           F(   2,    484) =      29.02
                                                  Prob > F        =     0.0000
                                                  R-squared       =     0.3137
                                                  Adj R-squared   =     0.2938
                                                  Within R-sq.    =     0.1071
                                                  Root MSE        =     2.2239

------------------------------------------------------------------------------
        time | Coefficient  Std. err.      t    P>|t|     [95% conf. interval]
-------------+----------------------------------------------------------------
       phone |   .2705429   .0626834     4.32   0.000     .1473778     .393708
       frfam |   .2539231   .0619373     4.10   0.000     .1322239    .3756222
       _cons |   1.406026   .2549245     5.52   0.000     .9051305    1.906921
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+

(1 missing value generated)

Iteration 1:   deviance = 3.7303e+02  eps = .         iters = 3    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.57  P   
Iteration 2:   deviance = 3.5849e+02  eps = 4.06e-02  iters = 3    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.59      
Iteration 3:   deviance = 3.5840e+02  eps = 2.46e-04  iters = 3    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.60      
Iteration 4:   deviance = 3.5840e+02  eps = 3.17e-08  iters = 2    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.60      
Iteration 5:   deviance = 3.5840e+02  eps = 9.72e-16  iters = 2    tol = 1.0e-0
> 5                                                                            
>    min(eta) =  -0.60   S  
Iteration 6:   deviance = 3.5840e+02  eps = 1.62e-16  iters = 1    tol = 1.0e-0
> 9                                                                            
>    min(eta) =  -0.60   S O
-------------------------------------------------------------------------------
> -----------------------------
(legend: p: exact partial-out   s: exact solver   h: step-halving   o: epsilon 
> below tolerance)
Converged in 6 iterations and 14 HDFE sub-iterations (tol = 1.0e-08)

HDFE PPML regression                              No. of obs      =        499
Absorbing 2 HDFE groups                           Residual df     =        483
                                                  Wald chi2(3)    =     169.93
Deviance             =  358.4027719               Prob > chi2     =     0.0000
Log pseudolikelihood = -993.8547331               Pseudo R2       =     0.2976
------------------------------------------------------------------------------
             |               Robust
      visits | Coefficient  std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
        time |   .0448103   .0448665     1.00   0.318    -.0431265    .1327471
      u2h_fe |   .0765144   .0459404     1.67   0.096    -.0135272     .166556
       frfam |   .0004108   .0205161     0.02   0.984       -.0398    .0406215
       _cons |   1.510566   .1033122    14.62   0.000     1.308078    1.713054
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+

The coefficient on time is \(0.0448\) (\(p = 0.318\)). The residual coefficient is \(\hat{\rho} = 0.0765\) (\(p = 0.096\)), indicating weak positive selection.

26.3.1 A comparison with linear models

In a linear model, the control-function estimator gives the same coefficient as 2SLS. I fit both as a check on the two-step code:

Code
clear all
webuse website, clear

* 1. Linear 2SLS
ivreghdfe visits frfam (time=phone), absorb(ad female)

* 2. Linear control function
reghdfe time phone frfam, absorb(ad female) resid
predict double u2h_fe, resid
reghdfe visits time u2h_fe frfam, absorb(ad female)

* 3. Naive models (ignoring endogeneity)
reghdfe visits time frfam, absorb(ad female)
ppmlhdfe visits time frfam, absorb(ad female)
(Visits to website)

(dropped 1 singleton observations)
(MWFE estimator converged in 3 iterations)

IV (2SLS) estimation
--------------------

Estimates efficient for homoskedasticity only
Statistics consistent for homoskedasticity only

                                                      Number of obs =      499
                                                      F(  2,   484) =     8.31
                                                      Prob > F      =   0.0003
Total (centered) SS     =  3335.918099                Centered R2   =   0.3971
Total (uncentered) SS   =  3335.918099                Uncentered R2 =   0.3971
Residual SS             =  2011.119506                Root MSE      =    2.038

------------------------------------------------------------------------------
      visits | Coefficient  Std. err.      t    P>|t|     [95% conf. interval]
-------------+----------------------------------------------------------------
        time |   .5642802   .2123745     2.66   0.008     .1469904      .98157
       frfam |    -.04045   .0922873    -0.44   0.661    -.2217832    .1408833
------------------------------------------------------------------------------
Underidentification test (Anderson canon. corr. LM statistic):          18.494
                                                   Chi-sq(1) P-val =    0.0000
------------------------------------------------------------------------------
Weak identification test (Cragg-Donald Wald F statistic):               18.628
Stock-Yogo weak ID test critical values: 10% maximal IV size             16.38
                                         15% maximal IV size              8.96
                                         20% maximal IV size              6.66
                                         25% maximal IV size              5.53
Source: Stock-Yogo (2005).  Reproduced by permission.
------------------------------------------------------------------------------
Sargan statistic (overidentification test of all instruments):           0.000
                                                 (equation exactly identified)
------------------------------------------------------------------------------
Instrumented:         time
Included instruments: frfam
Excluded instruments: phone
Partialled-out:       _cons
                      nb: total SS, model F and R2s are after partialling-out;
                          any small-sample adjustments include partialled-out
                          variables in regressor count K
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+

(dropped 1 singleton observations)
(MWFE estimator converged in 3 iterations)

HDFE Linear regression                            Number of obs   =        499
Absorbing 2 HDFE groups                           F(   2,    484) =      29.02
                                                  Prob > F        =     0.0000
                                                  R-squared       =     0.3137
                                                  Adj R-squared   =     0.2938
                                                  Within R-sq.    =     0.1071
                                                  Root MSE        =     2.2239

------------------------------------------------------------------------------
        time | Coefficient  Std. err.      t    P>|t|     [95% conf. interval]
-------------+----------------------------------------------------------------
       phone |   .2705429   .0626834     4.32   0.000     .1473778     .393708
       frfam |   .2539231   .0619373     4.10   0.000     .1322239    .3756222
       _cons |   1.406026   .2549245     5.52   0.000     .9051305    1.906921
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+

(1 missing value generated)

(MWFE estimator converged in 3 iterations)

HDFE Linear regression                            Number of obs   =        499
Absorbing 2 HDFE groups                           F(   3,    483) =     117.11
                                                  Prob > F        =     0.0000
                                                  R-squared       =     0.7677
                                                  Adj R-squared   =     0.7605
                                                  Within R-sq.    =     0.4211
                                                  Root MSE        =     1.9996

------------------------------------------------------------------------------
      visits | Coefficient  Std. err.      t    P>|t|     [95% conf. interval]
-------------+----------------------------------------------------------------
        time |   .5642802   .2083276     2.71   0.007       .15494    .9736205
      u2h_fe |   .1827169   .2122987     0.86   0.390    -.2344262    .5998601
       frfam |    -.04045   .0905287    -0.45   0.655    -.2183288    .1374288
       _cons |   3.357879   .4427337     7.58   0.000     2.487957    4.227801
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+

(dropped 1 singleton observations)
(MWFE estimator converged in 3 iterations)

HDFE Linear regression                            Number of obs   =        499
Absorbing 2 HDFE groups                           F(   2,    484) =     175.38
                                                  Prob > F        =     0.0000
                                                  R-squared       =     0.7673
                                                  Adj R-squared   =     0.7606
                                                  Within R-sq.    =     0.4202
                                                  Root MSE        =     1.9991

------------------------------------------------------------------------------
      visits | Coefficient  Std. err.      t    P>|t|     [95% conf. interval]
-------------+----------------------------------------------------------------
        time |   .7402254   .0400951    18.46   0.000     .6614435    .8190073
       frfam |  -.1034523    .053248    -1.94   0.053     -.208078    .0011735
       _cons |   3.027348   .2202211    13.75   0.000     2.594641    3.460055
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+

(dropped 1 observations that are either singletons or separated by a fixed effe
> ct)
Iteration 1:   deviance = 3.7573e+02  eps = .         iters = 3    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.64  P   
Iteration 2:   deviance = 3.6107e+02  eps = 4.06e-02  iters = 3    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.64      
Iteration 3:   deviance = 3.6098e+02  eps = 2.57e-04  iters = 3    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.64      
Iteration 4:   deviance = 3.6098e+02  eps = 3.76e-08  iters = 2    tol = 1.0e-0
> 4                                                                            
>    min(eta) =  -0.64      
Iteration 5:   deviance = 3.6098e+02  eps = 1.45e-15  iters = 2    tol = 1.0e-0
> 5                                                                            
>    min(eta) =  -0.64   S  
Iteration 6:   deviance = 3.6098e+02  eps = 0.00e+00  iters = 1    tol = 1.0e-0
> 9                                                                            
>    min(eta) =  -0.64   S O
-------------------------------------------------------------------------------
> -----------------------------
(legend: p: exact partial-out   s: exact solver   h: step-halving   o: epsilon 
> below tolerance)
Converged in 6 iterations and 14 HDFE sub-iterations (tol = 1.0e-08)

HDFE PPML regression                              No. of obs      =        499
Absorbing 2 HDFE groups                           Residual df     =        484
                                                  Wald chi2(2)    =     164.67
Deviance             =  360.9797175               Prob > chi2     =     0.0000
Log pseudolikelihood = -995.1432059               Pseudo R2       =     0.2967
------------------------------------------------------------------------------
             |               Robust
      visits | Coefficient  std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
        time |   .1187612   .0095895    12.38   0.000     .0999662    .1375563
       frfam |  -.0281572   .0099614    -2.83   0.005    -.0476812   -.0086332
       _cons |   1.345121   .0497098    27.06   0.000     1.247692    1.442551
------------------------------------------------------------------------------

Absorbed degrees of freedom:
-----------------------------------------------------+
 Absorbed FE | Categories  - Redundant  = Num. Coefs |
-------------+---------------------------------------|
          ad |        12           0          12     |
      female |         2           1           1     |
-----------------------------------------------------+
Model Estimator time Coef. Std. Err. \(p\)-value
Naive Linear FE reghdfe 0.7402 0.0401 \(<0.001\)
Linear 2SLS ivreghdfe 0.5643 0.2124 0.008
Linear Control Function Manual two-step 0.5643 0.2083 0.007
Naive FE Poisson ppmlhdfe (no residual) 0.1188 0.0096 \(<0.001\)
Control-Function FE Poisson ppmlhdfe (with \(\hat{v}\)) 0.0448 0.0449 0.318

The manual linear control function reproduces the 2SLS coefficient exactly. In the Poisson model, adding the control-function residual reduces the time coefficient from \(0.1188\) to \(0.0448\).

26.3.2 Cluster bootstrap in Stata

The bootstrap can draw the same cluster more than once. Each copy must get a new identifier through idcluster(); otherwise Stata pools the copies into one fixed effect.

Code
clear all
capture program drop ppmlhdfe_cf
program ppmlhdfe_cf, rclass
  reghdfe time phone frfam, absorb(newid female) resid
  predict double u2h_fe, resid
  ppmlhdfe visits time u2h_fe frfam, absorb(newid female)
  return scalar b_time = _b[time]
  return scalar b_u2h  = _b[u2h_fe]
  drop u2h_fe
  xtset, clear
end

webuse website, clear
xtset, clear
bootstrap r(b_time) r(b_u2h), reps(1000) seed(123) cluster(ad) idcluster(newid) nodots: ppmlhdfe_cf
(Visits to website)



Bootstrap results                                        Number of obs =   499
                                                         Replications  = 1,000

      Command: ppmlhdfe_cf
        _bs_1: r(b_time)
        _bs_2: r(b_u2h)

                                     (Replications based on 12 clusters in ad)
------------------------------------------------------------------------------
             |   Observed   Bootstrap                         Normal-based
             | coefficient  std. err.      z    P>|z|     [95% conf. interval]
-------------+----------------------------------------------------------------
       _bs_1 |   .0448103   .0477457     0.94   0.348    -.0487697    .1383902
       _bs_2 |   .0765144   .0486568     1.57   0.116    -.0188511    .1718799
------------------------------------------------------------------------------

The bootstrap standard error on time is \(0.0477\) (compared to the unadjusted analytic standard error of \(0.0449\)).

26.4 The same estimator in R

In R, I use fixest::feols() and fixest::fepois(). feols() drops a singleton cluster here (ad == 10), so we use obs() to align the residuals with the rows kept in the first stage:

Code
library(fixest)
library(haven)

df <- as.data.frame(read_dta("https://www.stata-press.com/data/r18/website.dta"))
df$ad     <- as.factor(df$ad)
df$female <- as.factor(df$female)

# Step 1: Linear first stage
fs <- feols(time ~ phone + frfam | ad + female, data = df)

# Align rows (feols drops singletons)
d <- df[obs(fs), ]
d$u2h_fe <- residuals(fs)

# Step 2: FE Poisson with and without control function
m_naive <- fepois(visits ~ time + frfam          | ad + female, data = d, notes = FALSE)
m_cf    <- fepois(visits ~ time + u2h_fe + frfam | ad + female, data = d, notes = FALSE)

etable(m_naive, m_cf, headers = c("Naive FE Poisson", "Control Function"), se.below = TRUE)
                         m_naive             m_cf
                Naive FE Poisson Control Function
Dependent Var.:           visits           visits
                                                 
time                   0.1188***          0.0448 
                      (0.0076)           (0.0477)
frfam                 -0.0282**           0.0004 
                      (0.0108)           (0.0211)
u2h_fe                                    0.0765 
                                         (0.0487)
Fixed-Effects:        ----------         --------
ad                           Yes              Yes
female                       Yes              Yes
_______________       __________         ________
S.E. type                    IID              IID
Observations                 499              499
Squared Cor.             0.75130          0.75528
Pseudo R2                0.29670          0.29761
BIC                      2,083.5          2,087.1
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

The point estimates match Stata to seven digits: \(0.1187612\) (naive) and \(0.0448103\) (control function).

26.4.1 Cluster bootstrap in R

Code
set.seed(123)
B <- 1000
clusters   <- unique(d$ad)
boot_coefs <- matrix(NA_real_, nrow = B, ncol = 2, dimnames = list(NULL, c("time", "resid")))

for (b in seq_len(B)) {
  drawn <- sample(clusters, length(clusters), replace = TRUE)
  boot_data <- do.call(rbind, lapply(seq_along(drawn), function(k) {
    sub <- d[d$ad == drawn[k], ]
    sub$boot_id <- k
    sub
  }))
  boot_data$boot_id <- as.factor(boot_data$boot_id)

  fs_b <- feols(time ~ phone + frfam | boot_id + female, data = boot_data, notes = FALSE)
  bb   <- boot_data[obs(fs_b), ]
  bb$u_b <- residuals(fs_b)

  m_b <- try(fepois(visits ~ time + u_b + frfam | boot_id + female, data = bb, notes = FALSE), silent = TRUE)
  if (!inherits(m_b, "try-error")) boot_coefs[b, ] <- coef(m_b)[c("time", "u_b")]
}

cat("Completed replications :", sum(complete.cases(boot_coefs)), "\n")
Completed replications : 1000 
Code
cat("Bootstrap SE, time     :", round(sd(boot_coefs[, "time"],  na.rm = TRUE), 4), "\n")
Bootstrap SE, time     : 0.045 
Code
cat("Bootstrap SE, residual :", round(sd(boot_coefs[, "resid"], na.rm = TRUE), 4), "\n")
Bootstrap SE, residual : 0.0457 

The R bootstrap yields \(\text{SE} = 0.0450\). With only 12 clusters in ad, small differences across software packages (\(0.0477\) in Stata vs. \(0.0450\) in R) reflect finite-sample sensitivity in how rare degenerate resamples and singletons are handled.

26.5 A binary endogenous regressor

When \(D_{it}\) is binary, as with program participation, a linear first stage is misspecified because its error cannot satisfy the required conditional-independence restriction.

26.5.1 A CRE logit first stage

A first-stage logit with a dummy for every unit has an incidental-parameters problem when \(T\) is small. For example, its slopes can be about twice as large as they should be when \(T=2\). I instead use pooled logit with a Mundlak adjustment, adding the unit means of the time-varying covariates:

\[P(D_{it} = 1 \mid z_{it}, \bar{z}_i) = \Lambda(z_{it}\gamma + \bar{z}_i \pi)\]

For logit, the generalized residual is simply the response residual,

\[\hat r_{it}=D_{it}-\hat p_{it}.\]

We then include \(\hat r_{it}\) in the fixed-effects Poisson model.

26.5.2 A simulation

Let’s simulate 2,000 units with unbalanced panels, where \(T_i\) ranges from 2 to 6. The treatment \(D\) is binary and endogenous, and its true coefficient is \(\beta=0.30\).

Code
preserve
clear
set seed 20260731
set obs 2000
gen i = _n
gen double ci = rnormal()*0.5
gen T = 2 + int(runiform()*5)        /* 2-6 observations per unit */
expand T
bysort i: gen t = _n

gen double z = rnormal()             /* instrument */
gen double e = rnormal()
gen double u = 0.8*e + rnormal()*0.6 /* correlated with e -> endogeneity */
gen byte   d = (-0.2 + 0.9*z + ci + e > 0)
gen double mu = exp(0.3*d + ci + 0.5*u)
gen y = rpoisson(mu)                 /* true beta = 0.3 */

* 1. Naive FE Poisson (ignores endogeneity)
quietly ppmlhdfe y d, absorb(i)
scalar b_naive = _b[d]

* 2. CF with CRE (Mundlak) logit first stage
bysort i: egen double z_i = mean(z)
quietly logit d z z_i
predict double phat, pr
gen double gr = d - phat
quietly ppmlhdfe y d gr, absorb(i)
scalar b_cre = _b[d]

* 3. CF with linear first stage
quietly reghdfe d z, absorb(i) resid
predict double v_lin, resid
quietly ppmlhdfe y d v_lin, absorb(i)
scalar b_lin = _b[d]

di "True coefficient on d       = 0.300000"
di "Naive FE Poisson            = " %8.6f b_naive
di "CF, CRE (Mundlak) Logit     = " %8.6f b_cre
di "CF, Linear First Stage      = " %8.6f b_lin
restore
Number of observations (_N) was 0, now 2,000.




(5,969 observations created)




















True coefficient on d       = 0.300000

Naive FE Poisson            = 0.747206

CF, CRE (Mundlak) Logit     = 0.293829

CF, Linear First Stage      = 0.277644

The naive fixed-effects Poisson estimate is \(0.747\), more than 2.4 times the true value. The CRE logit control function gives \(0.294\), while the linear first-stage control function gives \(0.278\).

In an application, I would report both control-function specifications as a sensitivity check.

26.6 What to remember

The control-function approach adds the first-stage residual to the exponential outcome model. For a continuous endogenous variable, we can combine reghdfe with ppmlhdfe in Stata, or feols() with fepois() in R. Inference should bootstrap both stages by cluster. For a binary endogenous variable, a CRE logit first stage avoids the incidental-parameters problem from unit dummies, and its residual is \(d-\hat p\).