Open mikedenly opened 4 weeks ago
Hi @mikedenly ,
I think there are a few reasons for differences in standard errors. First, fixest
and reghdfe
might not implement the exact same algorithms for solving the iterated least squares problem. E.g. the demeaning algos for fixed effects differ and pplmhdfe
additionally implements a set of accelerations that fixest::fepois
might or might not include. Most importantly, both algorithms will rely on different convergence thresholds. Last, I think that you might observe differences in standard errors because the matrix inversion solve(crossprod(X))
is used twice in the computation of SEs (vs just once for the OLS coefs) and as a result, standard errors should have a larger "numerical error" than point estimates. I have experienced this a lot with my unit tests for pyfixest, where matching fixest::fepois() coefs at small tolerance was relatively easy, but for clustered SEs, not so much! 😄
You might be able to test this theory by decreasing the tolerance of the convergence algorithms (both for fixed effect demeaning and the IWLS iterations), in which case we should see a decrease in the difference between standard errors.
Beyond, there are other reasons why standard errors might differ - i.e. the packages might use different small sample / degree of freedom corrections for clustered standard errors. There area a lot of details on what fixest does here: link.
Having said that, if I had to bet, I'd say it's what you observe is a combination of slightly different algorithms + numerical error.
Hi @s3alfisc, thank you so much for very your thoughtful answer! The link that you provided on standard errors also confirms that fixest
calculates standard errors similar to reghdfe
in Stata, and according to here that should not be an issue with fixest::fepois
and ppmlhdfe
in Stata as well. I am thus a bit perplexed about how to proceed, especially because I would like to calculate marginal effects after the regular estimation, and my closed issue on @vincentarelbundock 's incredible marginaleffects
package shows that these differences you point out can lead to such vast differences between Stata and R output -- or, in your case, Python and pyfixest
as well. To avoid potential mistakes, I sent my paper to the journal with interpretations based on exponentiated coefficients because they match across software programs -- even though, clearly, having a marginal effect is, all else equal , much more interpretable. However, it seems like that all else equal assumption is a bit of a stretch here for fixed effect poisson estimation given everything that you describe.
Hi @mikedenly, I'm glad you find this software useful! :-)
On the issue you're raising, I'm afraid that if there is a deviation from the standard, it may be on ppmlhdfe
's side.
The SEs in fixest
replicate the SEs that you can obtain from GLM (modulo the small sample correction), as shown below:
# run regular model with state & year fixed effects
est_fixest = fepois(fatal ~ beertax | state + year, data=Fatalities, cluster=~state)
est_glm = glm(fatal ~ beertax + as.factor(state) + as.factor(year),
data = Fatalities, family = poisson())
coef(est_fixest) - coef(est_glm)["beertax"]
#> beertax
#> -8.881784e-15
library(sandwich)
se_fixest = se(est_fixest, ssc = ssc(fixef.K = "full"))
se_glm = se(vcovCL(est_glm, cluster = ~state, type = "HC1"))["beertax"]
se_fixest - se_glm
#> beertax
#> -3.339575e-10
@s3alfisc, your remarks are interesting. Did your benchmarks differ systematically? What were the conditions to make a divergence apparent (sample size, FEs, number of variables)? What were the tolerance levels you used to make the difference disappear? Could you share problematic setups?
I actually went back and did some checks - overall, pyfixest
gets closer than I recalled:
%load_ext autoreload
%autoreload 2
import numpy as np
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri
from rpy2.robjects.packages import importr
import pyfixest as pf
from pyfixest.utils.utils import ssc
import pandas as pd
pandas2ri.activate()
fixest = importr("fixest")
stats = importr("stats")
data = pf.get_data(N = 100, model = "Fepois")
adj = False
cluster_adj = False
fml = "Y ~ X1 + X2 | f1"
vcov = "hetero"
py_mod = pf.fepois(
fml, data=data, vcov=vcov, ssc=ssc(adj=adj, cluster_adj=cluster_adj)
)
r_mod = fixest.fepois(
ro.Formula(fml),
data=data,
vcov=vcov,
ssc=fixest.ssc(adj, "none", cluster_adj, "min", "min", False),
)
py_mod_vcov = py_mod._vcov
r_mod_vcov = stats.vcov(r_mod)
py_mod.coef() - stats.coef(r_mod)
# Coefficient
# X1 -3.490416e-10
# X2 2.342791e-11
# Name: Estimate, dtype: float64
py_mod_vcov - r_mod_vcov
# array([[-7.14779327e-07, 8.56358303e-09],
# [ 8.56358303e-09, -3.96472177e-08]])
So quite close after all 😅 (but not exactly identical).
Dear Laurent (@lrberge) et al, I am writing to kindly request your assistance because I am unable to have
fixest::fepois()
fully replicateppmlhdfe
in Stata. Per this thread, I understand thatfepois()
is supposed to produce the exact same output asppmlhdfe
in Stata. Initially, I thought my problem was amarginaleffects::
one given the vastly different marginal effects confidence intervals betweenfixest::fepois()
andppmlhdfe
, so I brought it to @vincentarelbundock 's attention in this issue. However, Vincent pointed out that the regular, non-marginalized standard errors were not exactly the same, which he suggests could produce different variance-covariance matrices and, in turn, vastly different marginal effects confidence intervals. Accordingly, I thought I would post the issue here with the same reproducible code for only the regular estimation. Do you have any thoughts on the matter? In any case, thank you so much for your incredible work with this package -- it has really changed R, and I teach your work to all of my students.Here is the
fepois()
code:Here is the Stata code:
And here is
sessionInfo()
: