@@ -21,6 +21,9 @@ install.packages("fixest")
2121# install.packages("fixest", repos = "https://fastverse.r-universe.dev")
2222```
2323
24+ ** Important:** In the code below, we assume that you are running ** fixest**
25+ v0.13.0 or above (which is available on CRAN as of early September 2025).
26+
2427Once ** fixest** is installed, don't forget to load it whenever you want to
2528use it. Unlike Stata, you have to re-load a package every time you start a new R
2629session.
@@ -133,16 +136,10 @@ feols(wage ~ educ + i(treat, ref = 1), dat)
133136<div >
134137
135138``` stata
136- reghdfe wage educ, absorb(countyfips) cluster(countyfips)
137-
138-
139-
140-
141-
139+ reghdfe wage educ, absorb(countyfips)
140+ reghdfe wage educ, absorb(countyfips) cluster(countyfips)
142141
143- reghdfe wage educ, absorb(countyfips)
144-
145- * Add more fixed effects...
142+ * Add more fixed effects (and clusted SEs)...
146143reghdfe wage educ, absorb(countyfips year) ///
147144 vce(cluster countyfips year)
148145reghdfe wage educ, absorb(countyfips#year) ///
@@ -152,20 +149,14 @@ reghdfe wage educ, absorb(countyfips#year) ///
152149<div >
153150
154151``` r
152+ feols(wage ~ educ | countyfips , dat )
155153feols(wage ~ educ | countyfips , dat , vcov = ~ countyfips )
156- # feols(wage ~ educ | countyfips, dat) # same (see below)
157-
158- # Note: fixest automatically clusters SEs by the first
159- # fixed effect (if there are any). We'll get to SEs
160- # later, but if you just want iid errors for a fixed
161- # effect model:
162- feols(wage ~ educ | countyfips , dat , vcov = ' iid' )
163154
164- # Add more fixed effects...
165- feols(wage ~ educ | countyfips + year ,
166- dat , vcov = ~ countyfips + year )
167- feols(wage ~ educ | countyfips ^ year ,
168- dat ) # defaults to vcov = ~countyfips^year
155+ # Add more fixed effects (and clusted SEs) ...
156+ feols(wage ~ educ | countyfips + year , dat ,
157+ vcov = ~ countyfips + year )
158+ feols(wage ~ educ | countyfips ^ year , dat ,
159+ vcov = ~ countyfips ^ year )
169160```
170161</div >
171162</div >
@@ -234,7 +225,7 @@ logit marr age black hisp
234225
235226* https://github.com/sergiocorreia/ppmlhdfe
236227ppmlhdfe educ age black hisp, absorb(statefips year) ///
237- vce(cluster statefips )
228+ vce(robust )
238229```
239230</div >
240231<div >
@@ -248,7 +239,8 @@ feglm(marr ~ age + black + hisp | statefips + year,
248239 dat , family = ' probit' )
249240
250241# fepois() is there for Poisson regression
251- fepois(educ ~ age + black + hisp | statefips + year , dat )
242+ fepois(educ ~ age + black + hisp | statefips + year ,
243+ dat , vcov = ' hc1' )
252244```
253245</div >
254246</div >
@@ -480,17 +472,18 @@ reghdfe wage educ, absorb(statefips#year)
480472
481473* Varying slopes (e.g. time trend for each state)
482474reghdfe wage educ, absorb(statefips#c.year) ///
483- vce(cluster statefips#c.year)
475+ vce(cluster statefips#c.year)
484476```
485477</div >
486478<div >
487479
488480``` r
489481# Combine fixed effects
490- feols(wage ~ educ | statefips ^ year , dat )
482+ feols(wage ~ educ | statefips ^ year , dat )
491483
492484# Varying slopes (e.g. time trend for each state)
493- feols(wage ~ educ | statefips [year ], dat )
485+ feols(wage ~ educ | statefips [year ], dat ,
486+ vcov = ~ statefips : year )
494487```
495488</div >
496489</div >
@@ -500,10 +493,10 @@ feols(wage ~ educ | statefips[year], dat)
500493
501494While you can specify standard errors inside the original ** fixest** model call
502495(just like Stata), a unique feature of R is that you can adjust errors for an
503- existing model _ on-the-fly_ . This has [ several
504- benefits] ( https://grantmcdermott.com/better-way-adjust-SEs ) , including being
505- much more efficient since you don't have to re-estimate your whole model. We'll
506- try to highlight examples of both approaches below.
496+ existing model _ on-the-fly_ . This has
497+ [ several benefits] ( https://grantmcdermott.com/posts/ better-way-adjust-ses/ ) ,
498+ including being much more efficient since you don't have to re-estimate your
499+ whole model. We'll try to highlight examples of both approaches below.
507500
508501
509502### HC
@@ -512,23 +505,28 @@ try to highlight examples of both approaches below.
512505<div >
513506
514507``` stata
515- reg wage educ, vce(robust)
508+ reg wage educ, vce(robust)
509+ reg wage educ, vce(hc2)
516510reg wage educ, vce(hc3)
517511```
518512</div >
519513<div >
520514
521515``` r
522516feols(wage ~ educ , dat , vcov = ' hc1' )
523- feols(wage ~ educ , dat , vcov = sandwich :: vcovHC )
517+ feols(wage ~ educ , dat , vcov = ' hc2' )
518+ feols(wage ~ educ , dat , vcov = ' hc3' )
524519
525520# Note: You can also adjust the SEs of an existing model
526521m = feols(wage ~ educ , dat ) # iid
527522summary(m , vcov = ' hc1' ) # switch to HC1
528523```
529524</div >
530525</div >
531-
526+
527+ Aside: You shouldn't be using ` , robust ` for smaller samples... but you
528+ [ knew that] ( http://datacolada.org/99 ) , right?
529+
532530### HAC
533531
534532<div class = ' code--container grid grid-row grid-cols-1 gap-y-2 lg:gap-y-0 lg:grid-cols-2 lg:gap-x-2 [&>*]:!mt-0' >
@@ -570,17 +568,17 @@ reghdfe wage educ, absorb(countyfips#year) ///
570568<div >
571569
572570``` r
573- feols(wage ~ educ | countyfips , dat ) # Auto clusters by FE
574- # feols(wage ~ educ | countyfips, dat, vcov = ~countyfips) # ofc can be explicit too
571+ feols(wage ~ educ | countyfips , dat ,
572+ vcov = ~ countyfips )
575573
576574# Twoway clustering etc.
577- feols(wage ~ educ | countyfips + year ,
578- dat , vcov = ~ countyfips + year )
579- # feols(wage ~ educ | countyfips + year,
580- # dat, vcov = 'twoway') ## same as above
575+ feols(wage ~ educ | countyfips + year , dat ,
576+ vcov = ~ countyfips + year )
577+ # feols(wage ~ educ | countyfips + year, dat,
578+ # vcov = 'twoway') ## same as above
581579
582- feols(wage ~ educ | countyfips ^ year ,
583- dat , vcov = ~ countyfips ^ year )
580+ feols(wage ~ educ | countyfips ^ year , dat ,
581+ vcov = ~ countyfips ^ year )
584582```
585583</div >
586584</div >
@@ -614,8 +612,9 @@ functions, e.g. `etable()`. But here is a quick example using `summary()`:
614612
615613``` r
616614m = feols(wage ~ educ | countyfips + year , dat )
617- m # Clustered by countyfips (default)
618- summary(m , vcov = ' iid' ) # Switch to iid errors
615+ m # IID (default)
616+ summary(m , vcov = ' hc3' ) # Switch to HC3 errors
617+ summary(m , vcov = ~ countyfips ) # Cluster by countyfips
619618summary(m , vcov = ' twoway' ) # Cluster by countyfips and year
620619summary(m , vcov = ~ countyfips ^ year ) # Cluster by countyfips*year interaction
621620```
@@ -673,7 +672,7 @@ time.)
673672etable(est1 , est2 , vcov = ' hc1' )
674673
675674# Report multiple SEs for the same model
676- etable(est1 , vcov = list (' iid' , ' hc1 ' , ~ id , ~ countyfips ))
675+ etable(est1 , vcov = list (' iid' , ' hc2 ' , ~ id , ~ countyfips ))
677676
678677# Multi-model example: Two dep. vars, stepwise coefs,
679678# varying slopes, split sample, etc. (18 models in total!)
0 commit comments