Extracted main text — title through conclusion, appendix excluded. This is what our citation measures are computed over, published so the extraction can be checked by eye.
Rendered from LaTeX for readability, not typeset faithfully. Citation keys are highlighted; maths is left as source; figures, tables and equation environments are summarised rather than reproduced; unrecognised commands are greyed out so nothing is silently dropped. Email addresses are removed.
fixest: A fast and feature-rich framework for econometric estimations in R
abstractfixest is an R package for fast and flexible
econometric estimation. It provides a unified framework for applied
research, with comprehensive support for a diverse class of models:
ordinary least squares, instrumental variables, generalized linear
models, maximum likelihood, and difference-in-differences. The package
particularly excels at fixed-effects estimation, supported by a novel
fixed-point acceleration algorithm implemented in C++. This
algorithm achieves rapid convergence across a variety of data contexts
and enables efficient estimation of complex models, including those with
varying slopes. An expressive formula interface facilitates multiple
estimations, stepwise regressions, and variable interpolation in a
single call. Users can adjust inference strategies on the fly, choosing
from an array of built-in robust standard errors. The package also
provides methods for publication-ready regression tables and coefficient
plots. Benchmarks demonstrate that fixest offers best-in-class
performance against leading alternatives in \textsc{R},
\textsc{Python}, and \textsc{Julia}.
Introduction
Fixed-effects estimation is a cornerstone of modern applied
econometrics. By absorbing unobserved heterogeneity through categorical
controls, fixed-effects models allow researchers to isolate causal
relationships in panel data, firm-employee records, trade flows, and
many other settings. The computational challenge is that fixed-effects
can have thousands or millions of levels, making standard estimation
routines impractical. This has motivated a body of work on efficient
algorithms for “demeaning” the fixed-effects out of the data before
estimating the coefficients of interest, grounded in the canonical
Frisch-Waugh-Lovell (FWL) theorem
frisch1933PartialTimeRegressions, lovell1963SeasonalAdjustmentEconomic.
A variety of software packages have been developed to tackle the
demeaning problem. In R rproglang2025, lfe
gaure2013lfe introduced efficient fixed-effects estimation using
the method of alternating projections gaure2013ols, with support
for ordinary least squares (OLS), instrumental varaibles (IV), and
clustered standard errors. alpaca
stammann2018FastFeasibleEstimation extended the same approach to
generalized linear models (GLMs). In Julia
bezanson2017julia, \textbf{\texttt{FixedEffectModels}}
gomez2024FixedEffectModelsJlFast uses the LSMR algorithm
fong2011LSMRIterativeAlgorithm for OLS, while
\textbf{\texttt{GLFixedEffectModels}} boehm2025GLFixedEffectModelsJl handles
GLMs. In \textsc{Python} python2025, \textbf{\texttt{PyFixest}}
fischer2024Pyfixest offers similar functionality.\footnote{In
this paper, our comparison focuses on free and open source software.
Note that in the not free and open source language \textsc{Stata},
correia2017_reghdfe and correia2020fast use a variant
of the method of alternating projections to handle fixed-effects in
OLS and GLM.}
These packages have generally made fixed-effects estimation accessible
and fast. However, they each address only part of the typical workflow
for applied researchers. For example, lfe does not support GLMs
and is incompatible with the sandwich
zeileis2020VariousVersatileVariances ecosystem for flexible
variance-covariance (VCOV) estimation. alpaca handles GLMs but not
OLS or IV. The core alternating projections algorithm of these two
packages is also not well suited to “sparse” network structures, which
is common in many real-life datasets (e.g., employee-firm settings).
Similarly, the \textbf{\texttt{PyFixest}} library, developed by independent authors,
implements the same algorithm and hence suffers from the same
limitations. It does, however, provide a set of features close to
\textbf{\texttt{fixest}}, since its original objective was to port \textbf{\texttt{fixest}}
syntax and features to \textsc{Python}. In contrast, the two Julia
packages offer strong performance for their respective use-cases, but
with limited post-estimation methods and options (e.g., conducting
inference on different VCOVs). In sum, none of the existing packages
provides a unified interface that spans estimation, inference, and
presentation within a single, highly performant framework.
It is against this background that we introduce the R package
fixest. The package name reflects the fact that fixest
originated as a specialized library for fixed-effects regression.
However, it has since evolved into a general purpose tool that covers a
wide range of econometric methods and use cases. Its defining ambition
is to be both fast and coherent, and thus to fill a gap left by existing
software options. For many applied research projects and researchers,
\texttt{fixest} should suffice as a standalone package able to cover all of
their econometric needs.
fixest offers best-in-class performance for many frequentist
estimators, supported by a unified framework that enables estimation,
inference, and reporting in a single package. It supports OLS, IV, GLM,
and maximum likelihood (MLE) estimation through a common syntax that is
fully compatible with standard R stats methods. For
fixed-effects estimation, specifically, it introduces a novel algorithm
based on fixed-point acceleration using the method of
irons1969version, which achieves fast convergence across many
different data contexts. The performance-critical parts of the codebase
are implemented in parallel C++ via \textbf{\texttt{Rcpp}}
eddelbuettel2011jss_rcpp, further ensuring optimal efficiency.
We illustrate fixest performance in a wide array of benchmarks.
Using simulated data, we generate scenarios with simple or difficult
fixed-effects convergence and compare fixest to leading
alternatives in R, Python and \textsc{Julia}.
\textbf{\texttt{fixest}} is consistently the fastest in all situations, closely
followed by the \textsc{Julia} software. A second set of benchmarks
uses a large real-life data set to illustrate \textbf{\texttt{fixest}} specific
features useful in applied econometrics (detailed below). In those
cases, \textbf{\texttt{fixest}} speed is unmatched.
In addition to speed, fixest contributes several features that
distinguish it from existing software. For example, one of the core
design principles is that inference is separate from estimation. This
means that users can swap out a variety of VCOVs---including
heteroskedasticity-robust, clustered, Newey-West
newey1987simple, Driscoll-Kraay driscoll1998consistent,
Conley conley1999gmm, and other standard errors---at virtually
zero computational cost, since the underlying model does not have to be
re-estimated. An expressive formula interface supports programmatic
variable interpolation, factor manipulation, and multiple estimations in
a single call, where shared computations are pooled across models for
additional speed gains. fixest also provides built-in tools for
publication-ready regression tables and coefficient plots, and invests
heavily in informative error messages that help users diagnose problems
quickly.
This article describes fixest version 0.14.0, available from the
Comprehensive R Archive Network at
\url{https://cran.r-project.org/package=fixest}. We begin with a
self-contained motivating example in Section (ref) that
introduces the core syntax through an end-to-end analysis. Section
(ref) presents the econometric theory and associated
computational considerations for estimating fixed-effects models, as
well detailing fixest's novel fixed-point acceleration solution.
Sections (ref) through (ref) describe
the package's estimation functions, formula interface, VCOV options, and
presentation tools. Section (ref) reports benchmarks
against leading alternatives. Section (ref) discusses
fixest's integration with the broader \textsc{R} ecosystem and
pedagogical context, before concluding with the package's development
philosophy.
Motivating example: Estimating the returns to science
funding
Assume that we have data on scientific researchers in the EU and US. We
are able to track their individual productivity levels (as measured by
the number of articles produced every year), as well as their access to
funding (in \$US'000). Our goal is to estimate the effect that funding
has on scientific production, whilst controlling for differences across
individuals, years and regions.
For demonstration purposes, let us create a dummy dataset called
scipubs with six variables: number of articles
(articles), funding received (funding), region
(eu_us), a dummy representing a policy shock
(policy), scientist identifier (indiv), and the year
(\texttt{year}).
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{library}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"fixest"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.28,0.35,0.67}{data}\textcolor[rgb]{0.00,0.23,0.31}{(base_did)}
\textcolor[rgb]{0.00,0.23,0.31}{scipubs }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{with}\textcolor[rgb]{0.00,0.23,0.31}{(}
\textcolor[rgb]{0.00,0.23,0.31}{ base_did, }
\textcolor[rgb]{0.28,0.35,0.67}{data.frame}\textcolor[rgb]{0.00,0.23,0.31}{(}
\textcolor[rgb]{0.40,0.45,0.13}{articles =} \textcolor[rgb]{0.28,0.35,0.67}{round}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{{-}} \textcolor[rgb]{0.28,0.35,0.67}{min}\textcolor[rgb]{0.00,0.23,0.31}{(y)), }
\textcolor[rgb]{0.40,0.45,0.13}{funding =} \textcolor[rgb]{0.28,0.35,0.67}{round}\textcolor[rgb]{0.00,0.23,0.31}{(x1 }\textcolor[rgb]{0.37,0.37,0.37}{{-}} \textcolor[rgb]{0.28,0.35,0.67}{min}\textcolor[rgb]{0.00,0.23,0.31}{(x1)) }\textcolor[rgb]{0.37,0.37,0.37}{*} \textcolor[rgb]{0.68,0.00,0.00}{10}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{eu_us =} \textcolor[rgb]{0.28,0.35,0.67}{ifelse}\textcolor[rgb]{0.00,0.23,0.31}{(treat, }\textcolor[rgb]{0.13,0.47,0.30}{"EU"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"US"}\textcolor[rgb]{0.00,0.23,0.31}{), }
\textcolor[rgb]{0.40,0.45,0.13}{policy =} \textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{(period }\textcolor[rgb]{0.37,0.37,0.37}{{-}}\textcolor[rgb]{0.00,0.23,0.31}{ id }\textcolor[rgb]{0.37,0.37,0.37}{\textgreater} \textcolor[rgb]{0.68,0.00,0.00}{0}\textcolor[rgb]{0.00,0.23,0.31}{),}
\textcolor[rgb]{0.40,0.45,0.13}{indiv =}\textcolor[rgb]{0.00,0.23,0.31}{ id, }
\textcolor[rgb]{0.40,0.45,0.13}{year =}\textcolor[rgb]{0.00,0.23,0.31}{ period}
\textcolor[rgb]{0.00,0.23,0.31}{ )}
\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
Ordinary least squares
estimation
We start with a simple ordinary least squares (OLS) model, where we
regress science production (articles) on funding
(funding) and a set of control variables. OLS estimations in
fixest are performed with the function feols. In its
simplest guise, feols can be seen as a drop-in replacement for
the base \textsc{R} \texttt{stats::lm} function. For example, we can
use simple indicator (factor) variables to control for individual and
year fixed-effects:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{as.factor}\textcolor[rgb]{0.00,0.23,0.31}{(indiv) }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{as.factor}\textcolor[rgb]{0.00,0.23,0.31}{(year), scipubs)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) 5.777038 1.508346 3.830048 0.00013642 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater funding 0.096365 0.004702 20.492577 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(indiv)2 2.634578 1.943905 1.355302 0.17563956 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(indiv)3 {-}2.925442 1.942340 {-}1.506143 0.13235874 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(indiv)4 1.114538 1.942152 0.573868 0.56619154 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(indiv)5 {-}0.192731 1.942084 {-}0.099239 0.92096903 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(indiv)6 1.950884 1.943177 1.003966 0.31564731 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(indiv)7 2.347249 1.943023 1.208040 0.22732881 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater ... 110 coefficients remaining (display them with summary() or use}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater argument n)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 4.09849 Adj. R2: 0.420879}
\end{Highlighting}
The above feols results are identical to those from an
equivalent stats::lm call.\footnote{Specifically,
lm(articles\ \textasciitilde\ funding\ +\ as.factor(indiv)\ +\ as.factor(year),\ scipubs).}
However, the output is slightly different insofar as feols
automatically displays the results in summary form (including standard
errors, R-squared, etc.) and avoids displaying all the coefficients
unless explicitly requested by the user. In contrast, the default
lm print method would only display the point estimates and
would do so for all 118 coefficients.
This interchangeability between feols and lm is
convenient, since only the function call changes. However, it is not the
preferred way to estimate a fixed-effects model with feols.
Rather, we can take advantage of fixest's specialized architecture
for fixed-effects by invoking the syntax:
\[
\mathtt{dep\_var} \sim \mathtt{indep\_vars} \mathtt{\ |\ } \mathtt{fixed\_effects}
\]
In a fixest formula, any variable after the pipe
(\textbar) is treated as a fixed-effect and passed to a
highly optimized internal routine. Applying this principle to our
example, we have:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, scipubs)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: indiv: 108, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater funding 0.096365 0.004702 20.4926 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 4.09849 Adj. R2: 0.420879}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.30388 }
\end{Highlighting}
Observe that our estimate of the key funding coefficient
remains identical to our previous result of 0.096365. Moreover,
because we specified indiv and year as fixed-effects,
the return object correctly recognizes these variables as controls
(“nuisance” parameters) and avoids displaying the 100+ coefficients on
them. At the same time, it is important to note that the benefits extend
beyond aesthetics. This second estimation completes quicker than the
first one, exactly because the fixed-effects are handled by a
specialized and highly optimized routine. We demonstrate the speed
advantages in a benchmarking section later in the paper.
To interpret the coefficients, we need to know whether they are
statistically significant. Hence, we need to report their
standard-errors. By default feols assumes that the errors are
homoskedastic and non-correlated (labelled for convenience as “IID”,
or “independent and identically distributed” in our return object
above).
But fixest makes it easy to specify a wide variety of alternate
standard-error types. For example, because our data is a panel (i.e.,
repeated cross-sections of the same individuals over time), we may be
worried that the errors are correlated within individuals. In
econometric parlance, this suggests the need to cluster the
standard-errors at the individual level. We can implement this directly
at estimation time by using the argument
vcov\ =\ \textasciitildeindiv:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, scipubs, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{indiv)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: indiv: 108, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: Clustered (indiv) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater funding 0.096365 0.004542 21.2172 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 4.09849 Adj. R2: 0.420879}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.30388 }
\end{Highlighting}
Of course, other standard-error formulations are also possible. While
one may be tempted to re-run multiple versions of the same model with
slight variations to the standard-errors, this quickly becomes
inefficient. A core design principle of fixest is that computation
should be separate from inference. As such, fixest makes it easy
to adjust standard errors for existing models “on the fly”. Almost all
of the package's post-estimation routines and methods allow for this
kind of post hoc adjustment. Here we briefly demonstrate with a
simple side-by-side comparison of two standard types, using the
etable function to display the results in regression table
form:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, scipubs) }\textcolor[rgb]{0.37,0.37,0.37}{|\textgreater}
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"iid"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{indiv))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater feols(articles \textasciitilde.. feols(articles \textasciitilde...1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: articles articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater funding 0.0964*** (0.0047) 0.0964*** (0.0045)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}Effects: {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-} {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater indiv Yes Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year Yes Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ __________________ __________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID by: indiv}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 1,080 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.48367 0.48367}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2 0.30388 0.30388}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
The key argument above is
vcov\ =\ list("iid",\ \textasciitildeindiv)), which prompts
the display (and computation of) of both IID and clustered standard
errors alongside each other. Note that this type of functionality is
easily extended and that fixest supports many other standard-error
adjustments. We defer details until later in the paper and leave this as
a taster.
Let us return to our broader research question. Suppose we want to know
whether the effect of science funding is similar across the EU and US?
While there are multiple approaches to tackling this question, a
potential starting point is to estimate separate models for each region.
The split argument provides a convenient way to do this in a
single call. Again, we can pass the resulting object to etable
for convenient side-by-side display.\footnote{In truth, passing to
etable is redundant since fixest multiple estimation
objects are printed with etable by default. But we do so here
to avoid assuming too much familiarity with the package at this
introductory stage.}
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, scipubs, }\textcolor[rgb]{0.40,0.45,0.13}{split =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{eu_us) }\textcolor[rgb]{0.37,0.37,0.37}{|\textgreater}
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{()}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater feols(articles ..1 feols(articles ..2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Sample (eu_us) EU US}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: articles articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater funding 0.0900*** (0.0061) 0.1035*** (0.0065)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}Effects: {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-} {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater indiv Yes Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year Yes Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ __________________ __________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID IID}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 550 530}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.57106 0.44382}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2 0.30866 0.35002}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
Thus far, we have made minimal assumptions about the causal direction of
our results. An obvious complication is the likely endogeneity of
scientific funding (e.g., better researchers with more publications get
more funding). We may therefore wish to instrument funding with
an unexpected policy shock that changed funding availability in an
exogenous way. For our toy example here, we can use the policy
variable---corresponding to a dummy on certain years---as an instrument.
To perform an instrumental variable (IV) estimation in fixest, we
use the following syntax:
\[
\mathtt{dep\_var} \sim \mathtt{exogenous\_vars} \mathtt{\ |\ } \mathtt{fixed\_effects} \mathtt{\ |\ } \mathtt{endo\_vars} \sim \mathtt{instruments}
\]
Applying this syntax to our research productivity question, we have:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.68,0.00,0.00}{1} \textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ policy, scipubs)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater TSLS estimation}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} D.V. : articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} Endo. : funding}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} Instr. : policy}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |=\textgreater Second Stage}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater | Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: indiv: 108, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater fit_funding {-}0.245182 1.63019 {-}0.150401 0.88048 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 10.4 Adj. R2: 0.168161}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 1.061e{-}4}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater F{-}test (1st stage), funding: stat = 0.05190, p = 0.819832, on 1 and 962 DoF.}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wu{-}Hausman: stat = 0.28442, p = 0.593945, on 1 and 961 DoF.}
\end{Highlighting}
The results of the second stage estimation are reported, as well as some
fit statistics, like the (weak instrument) F-test for the first stage
and the Wu-Hausman endogeneity test.
Generalized linear model
estimation
All of our estimations thus far have modeled articles as the
outcome of interest. But number of articles is a positive count
(integer). Hence it is arguably more natural to model the outcome as a
Poisson process. In fixest this is easily done via the
fepois function:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{fepois}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, scipubs, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{indiv)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Poisson estimation, Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: indiv: 108, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: Clustered (indiv) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error z value Pr(\textgreater|z|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater funding 0.005644 0.000292 19.3214 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Log{-}Likelihood: {-}3,070.4 Adj. Pseudo R2: 0.105804}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater BIC: 6,964.9 Squared Cor.: 0.482025}
\end{Highlighting}
As the reader will note, the syntax is identical to the feols
case; we simply switch out the main function call. The same is true for
a variety of other generalized model families that fixest supports
through its feglm and femlm functions. Again, however,
we leave this a taster and defer details until later in the paper.
Presentation
Having iterated through various models, let us conclude this
introductory example by considering how we might present our findings.
Assume we have settled on the following variants: Poisson model with the
EU/US region split (and full sample), and the OLS-IV model.
The same etable function from before can be used to compile all
of our preferred results into a single \LaTeX table. We need simply
specify a valid file path to write \LaTeX output instead of
printing the table to screen. We also invoke some additional arguments:
itemize{0pt}{0pt}
• the vcov argument is used to adjust the standard-errors for
all models on-the-fly.
• the stage argument is used to display both the 1st and 2nd
stage of the IV estimation.
• the dict argument used to pass a dictionary for displaying
nicer variable names.\\
• the caption and label arguments pass helper macros
to the saved \LaTeX table.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_split }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, scipubs,}
\textcolor[rgb]{0.40,0.45,0.13}{fsplit =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ eu_us)}
\textcolor[rgb]{0.00,0.23,0.31}{est_iv }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.68,0.00,0.00}{1} \textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ policy, scipubs)}
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{(est_split, est_iv,}
\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{indiv,}
\textcolor[rgb]{0.40,0.45,0.13}{stage =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{dict =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{articles =} \textcolor[rgb]{0.13,0.47,0.30}{"\# Articles"}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{funding =} \textcolor[rgb]{0.13,0.47,0.30}{"Funding (\textquotesingle000 $US)"}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{policy =} \textcolor[rgb]{0.13,0.47,0.30}{"Policy"}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{indiv =} \textcolor[rgb]{0.13,0.47,0.30}{"Researcher"}\textcolor[rgb]{0.00,0.23,0.31}{, }
\textcolor[rgb]{0.40,0.45,0.13}{year =} \textcolor[rgb]{0.13,0.47,0.30}{"Year"}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{eu_us =} \textcolor[rgb]{0.13,0.47,0.30}{"Region"}\textcolor[rgb]{0.00,0.23,0.31}{),}
\textcolor[rgb]{0.40,0.45,0.13}{file =} \textcolor[rgb]{0.13,0.47,0.30}{"tables/first_example.tex"}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{caption =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"Compiling the results from five estimations: "}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.13,0.47,0.30}{"Default LaTeX rendering."}\textcolor[rgb]{0.00,0.23,0.31}{),}
\textcolor[rgb]{0.40,0.45,0.13}{label =} \textcolor[rgb]{0.13,0.47,0.30}{"tab_first_ex"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
The resulting table is shown in Table (ref).
table[table omitted — 1,868 chars of source]
In addition to a regression table, we might also wish to present our
results in the form of a coefficient plot via the dedicated
coefplot function. Below we demonstrate with a simple example,
limited to the split-sample Poisson estimation. The resulting plot is
displayed in Figure (ref).
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{coefplot}\textcolor[rgb]{0.00,0.23,0.31}{(est_split, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{indiv)}
\textcolor[rgb]{0.28,0.35,0.67}{legend}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"topleft"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"Full sample"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"EU"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"US"}\textcolor[rgb]{0.00,0.23,0.31}{), }
\textcolor[rgb]{0.40,0.45,0.13}{col =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{3}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{lty =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{pch =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{16}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.68,0.00,0.00}{17}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.68,0.00,0.00}{15}\textcolor[rgb]{0.00,0.23,0.31}{), }\textcolor[rgb]{0.40,0.45,0.13}{bg =} \textcolor[rgb]{0.13,0.47,0.30}{"white"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
figure[figure omitted — 194 chars of source]
Thus concludes our introductory example. Having established the core
intuition and syntax, we now examine the econometric theory and
computational algorithms that make fixest estimations fast and
reliable. We begin a theory review, before turning to a detailed review
of the package's main functions and features.
Fixed-effects: Theory and the fixest
implementation
fixest originated as a package specifically optimized for tackling
fixed-effects estimations. However, since the definition of the
term `fixed-effects' can differ greatly across fields, we first explain
the meaning of a fixed-effects estimation from our perspective. We also
show how it is tightly linked to variable demeaning. We then document
existing solutions to demeaning problems and detail fixest's
specific approach.
We offer two expositional notes before proceeding. First, we focus on
OLS estimation. The extension to generalized linear models is discussed
in Section (ref). Second, we focus on a very simple setup;
namely, a balanced panel with only two fixed-effects dimensions
(individual and time). The logic readily extends to imbalanced panels
and any number of fixed-effects. However, this level of generality would
necessitate ugly notations that can obscure the key insights without
corresponding benefit. Hence, we refrain from attempting this here. See
Berge2018_FENmlm for a treatment of the general case in the
context of maximum likelihood estimation.
Fixed-effects and the FWL theorem: A demeaning
problem
For illustration, consider the setup of a balanced panel in which we
follow individuals, indexed by \(i\), across years, indexed by \(t\).
There are \(N_I\) individuals and \(N_T\) periods, leading to
\(N = N_I\times N_T\) observations. Let \(y\) be the \(N\)-vector
representing the dependent variable and \(X\) a \(N \times K\) matrix of
explanatory variables. We are interested in the following data
generating process:
equation[equation omitted — 115 chars of source]
with \(X_{it}\) the row-vector of explanatory variables for individual
\(i\) and time \(t\), and \(\gamma\) the \(K\)-vector of the
coefficients of interest. In the context of the previous
equation, the coefficients \(\alpha_i\) capture any confounder
(i.e. explanatory variables that are not in the equation) that is
individual-specific and time-invariant. Similarly, \(\beta_t\) captures
any time-specific confounder. Finally, \(\epsilon_{it}\) is a random
variable representing the error term.
It is important to stress that we are not directly interested in the
values of \(\alpha_i\) and \(\beta_t\). Rather, they are effectively
nuisance parameters, insofar as they provide a means of controlling for
a set of confounders. Indeed, \(\alpha_i\) and \(\beta_t\) here
represent the fixed effects of the estimation, since they capture
invariant (fixed) confounders across the unit and time dimensions,
respectively. Fixed effects are often written as \(\alpha_i\), but this
is really short-hand notation for a set of \(N_I\) indicator variables
for each unit with a coefficient \(\alpha_i\) for each (equivalent to
factor(unit) in a regression). Extending this idea more
generally, we define “fixed-effects estimation” in this article simply
as an estimation that includes categorical variables as controls.
Now let us link fixed-effects with demeaning. Consider the estimation of
the coefficients of Equation (ref) by OLS. With a hat
referring to the estimated coefficients and \(r_{it}\) to the residual,
we have:
equation[equation omitted — 124 chars of source]
Let us define \(\tilde{y}\) as the residual of the OLS estimation of
\(y\) on the fixed-effects only:
\[
\tilde{y}_{it} \overset{\text{def}}{=} y_{it} - \hat{\alpha}_i^y - \hat{\beta}_t^y,
\]
with \(\hat{\alpha}_i^y\) the OLS estimator of the fixed-effect
coefficient.
In a similar fashion, let us define matrix \(\tilde{X}\) whose
\(k^{th}\) column is defined as the residual of the estimation of the
\(k^{th}\) column of \(X\) on the fixed-effects only. If we estimate
\(\tilde{y}\) on \(\tilde{X}\) by OLS, we have:
equation[equation omitted — 121 chars of source]
The Frisch-Waugh-Lovell
frisch1933PartialTimeRegressions, lovell1963SeasonalAdjustmentEconomic
theorem states that the coefficients \(\hat{\gamma}\) and the residuals
\(r_{it}\) of Equation (ref) are the same as the ones in
Equation (ref). That is
\(\hat{\gamma} = \hat{\gamma}_{bis}\) and \(r_{it} = r_{it}^{bis}\). It
follows that the variance-covariance matrices of the coefficients are
also similar:
\(V\left(\hat{\gamma}\right)=s\times V\left(\hat{\gamma}_{bis}\right)\),
with \(s \in \left[0;1\right]\) a scaling factor.\footnote{See
ding2021FrischWaughLovell for intuitive demonstrations.}
The equivalence between Equation (ref) and Equation
(ref) means that instead of estimating
\(N_I + N_T + K\) coefficients, we can estimate only \(K\) coefficients.
Given that solving an OLS problem is of complexity
\(\mathcal{O}\left(n_{coef}^3\right)\) with \(n_{coef}\) the number of
coefficients to estimate, avoiding the estimation of \(N_I + N_T\)
coefficients greatly reduces the dimensionality of the
problem.\footnote{For large $N_I$, or $N_T$, the direct estimation of Equation (ref) is simply intractable.}
However, to estimate Equation (ref), one still needs to
compute \(\tilde{y}\) and \(\tilde{X}\) first. The good news is that
computing these values is generally fast, thanks the simple structure of
the fixed-effects. Specifically, the computing step amounts to a
demeaning exercise, which we now illustrate.
To compute \(\tilde{y}\), we need to find the coefficients
\(\hat{\alpha}_i^y\) and \(\hat{\beta}_t^y\) that solve the following
OLS minimization problem:
equation[equation omitted — 195 chars of source]
We cannot solve for \(\alpha_i\) and \(\beta_t\) directly, since we
would be confronted by the aforementioned
\(\mathcal{O}\left(n_{coef}^3\right)\) complexity of the OLS solution.
Instead, we can solve it iteratively by minimizing over \(\alpha_i\) and
\(\beta_t\) sequentially until we obtain the optimal coefficients. Let
\(\alpha_i^{(m)}\) be the coefficients minimizing Equation
(ref) given \(\beta_t^{(m-1)}\) at iteration \(m\). We
define \(\beta_t^{(m)}\) similarly, with
\(\beta_t^{(0)} = 0, \forall t\). The first order conditions (FOC) of
Equation (ref) lead to:
equation[equation omitted — 204 chars of source]
As we can see, the coefficients satisfying the FOCs are simple group
means; hence our describing it as a demeaning problem. The complexity of
obtaining all \(N_I\) coefficients \(\alpha_i^{m}\) is
\(\mathcal{O}\left(n_{obs}\right)\). Let \(n_{iter}\) be the number of
iterations required for convergence, such that
\(|\alpha_i^{(n_{iter}+1)}-\alpha_i^{(n_{iter})}| \approx 0, \forall i\)
and
\(|\beta_t^{(n_{iter}+1)}-\beta_t^{(n_{iter})}| \approx 0, \forall t\).
Then for large \(N_I\), \(N_T\) and for small \(n_{iter}\), obtaining
\(\hat{\gamma}\) from Equation (ref) is much faster
than directly estimating Equation (ref).
To estimate \(\hat{\gamma}\) we end up with a total complexity of
\(\mathcal{O}\left(K \times n_{iter} \times n_{obs} \times n_{fixef}^2\right)\).
We can see that the number of iterations, \(n_{iter}\), plays a critical
role in reducing complexity. Defining an algorithmic routine that
ensures a low value of \(n_{iter}\) is thus essential to reducing
computing time and cost. Before detailing fixest's novel approach,
we first describe existing solutions offered by other software.
Fixed-effects solutions in other
software
As we have shown, the key to a fast OLS estimation with fixed-effects is
finding a computationally efficient solution to the fixed-effects-only
regression described by Equation (ref). In fact, this
problem corresponds to a sparse linear regression, and various solutions
have been offered to solve it. The most widely known of which is the
method of alternating projects.
Alternating projections
The method of alternating projections gaure2013ols is almost
equivalent to the demeaning problem defined in the previous section. The
only difference is that, instead of finding the solution coefficients,
only the residuals of the OLS regression is computed (\(\tilde{y}\)).
To illustrate this method, let us rewrite the problem with matrices. We
drop the double indexing \(it\) and use a single index \(o\) to refer to
the observations. Let \(D_I\) be the \(N \times N_I\) matrix such that
\((D_I)_{o,i}\) is equal to 1 if observation \(o\) refers to individual
\(i\) and 0 otherwise. We define \(D_T\) similarly.
Using these matrix notations, the regression of \(y\) on the individual
fixed-effects leads to the classic OLS solution
\(\hat\alpha = (D_I^\top D_I)^{-1}D_I^\top y\). Let the \(N \times N\)
matrix \(R_I\) be the residual maker, defined as:
\[
R_I \overset{\text{def}}{=} I_N - D_I(D_I^\top D_I)^{-1}D_I^\top.
\]
The product \(R_I y\) gives the residual of \(y\) such that
\((R_I y)_{o} = y_o - \hat\alpha_i\), with observation \(o\) referring
to individual \(i\). You can see a direct equivalence between the value
of this residual and the right hand numerators in Equation
(ref).
With \(R_T\) defined similarly, we can reach the residual of the OLS
estimation on the two fixed-effects by sequentially applying
\(R_I\) and \(R_T\), such that:
\[
\tilde{y} = R_T R_I ... R_T R_I y.
\]
One algorithm to obtain \(\tilde y\) is: set \(y^{(0)} = y\), then
compute iteratively \(y^{m} = R_T R_I y^{(m-1)}\) until
\(||y^{m} - y^{(m-1)}||^\infty \approx 0\). Each update of \(y^{(m)}\)
is known as an Halperin projection. This approach avoids to compute
explicitly the coefficients of the OLS estimations at each step and is
then slightly faster than the demeaning algorithm of the previous
section, although they have similar convergence properties.\footnote{The
astute reader may wonder: why not simply applying a quick
exponentiation to \((R_T R_I)\) before right multiplying by \(y\)? The
problem is that while \(R_T\) and \(R_I\) are sparse matrices, their
product is dense, which quickly becomes problematic for \(N \times N\)
matrices.}
This method is used in the R packages lfe
gaure2013lfe and alpaca
stammann2018FastFeasibleEstimation, and in the Python
package \textbf{\texttt{PyFixest}} fischer2024Pyfixest.
Other methods
In the language Stata, reghdfe
correia2016feasible uses the method of
kelner2013SimpleCombinatorialAlgorithm to solve the linear
system of equations given by Equation (ref).
In the language Julia, the software FixedEffectModels
gomez2024FixedEffectModelsJlFast solves the linear system using
the LSMR method fong2011LSMRIterativeAlgorithm.
The fixest solution: A fixed-point problem with
acceleration
The package fixest uses a demeaning algorithm to compute
\(\tilde{y}\) and \(\tilde{X}\). But it does not use the method of
alternating projections shown above. Instead it estimates the solution
coefficients directly. Depending on the structure of the fixed-effects,
how they are distributed across observations, the number of iterations
required to reach convergence can be arbitrarily high. To ensure good
convergence properties in all circumstances, fixest uses a
fixed-point acceleration algorithm whose implementation we now detail.
Before starting, we must say that the current example of a balanced
panel converges in exactly one iteration, making the demeaning algorithm
of Equation (ref) the fastest available for this
situation and any acceleration unnecessary. Even though we proceed with
this balanced example for illustration purposes, it may be assumed that
our method is applied to more difficult contexts where convergence
requires a high number of iterations.\footnote{A canonical example of
difficult convergence is the context of an estimation with employees
and firms fixed-effects. Typically the majority of employees will be
employed in only a handful of firms across their lifetime and the
probability that two employees are employed in the same company is
low. From a network perspective, where employees are connected to
other employees through companies, this means that the average network
distance between all employees is very high; making shocks very slow
to propagate. This contrasts sharply against the balanced panel case,
where the average network distance between each individual is exactly
one, and shocks propagate instantly. In Section (ref),
we provide a simple benchmark example where changing the structure of
the fixed-effects increases the computational time by several orders
of magnitude.}
Let us rewrite the demeaning algorithm as a fixed-point problem. Having
the demeaning algorithm in this form proves useful as it unlocks the use
of fixed-point acceleration algorithms.
We define the function
\(f_I:\mathbb{R}^{N_T} \rightarrow \mathbb{R}^{N_I}\) such that
\(\alpha^{(m + 1)} = f_I (\beta^{(m)})\), with \(\alpha^{(m + 1)}\) and
\(\beta^{(m)}\) the vector versions of the values in Equation
(ref). Defining
\(f_T: \mathbb{R}^{N_I} \rightarrow \mathbb{R}^{N_T}\) similarly, we can
rewrite Equation (ref) using these functions:
\[
split[split omitted — 96 chars of source]
\]
Replacing \(\alpha^{(m + 1)}\) by this definition leads to:
\[
\beta^{(m + 1)} = f_T (f_I (\beta^{(m)})).
\]
We clearly see that \(\beta^{(m + 1)}\) is only a function of
\(\beta^{(m)}\) and does not depend on \(\alpha^{(m + 1)}\) in any way.
Defining \(F = f_T \circ f_I\),
\(F: \mathbb{R}^{N_T} \rightarrow \mathbb{R}^{N_T}\) our problem reduces
to finding \(\beta^*\) such that:
\[
\beta^* = F(\beta^*).
\]
This is a fixed-point problem with \(N_T\) parameters and, hence, a
canonical fixed-point algorithm can be leveraged to hasten the
convergence process ramiere2015iterative. In the
fixest case, we use algorithm proposed by
irons1969version.
Starting at \(\beta^0_t=0, \forall t\), the Irons and Tuck algorithm
replaces \(\beta^{(m + 1)} = F(\beta^{(m)})\) with the following
iteration:
\[
\beta^{(m+1)} = F(F(\beta^{(m)})) - \dfrac{\Delta F(\beta^{(m)}) \cdot \Delta^2 \beta^{(m)}}{||\Delta^2 \beta^{(m)}||^2}\Delta F(\beta^{(m)}),
\]
where \(\Delta \beta^{(m)} = F(\beta^{(m)}) - \beta^{(m)}\),
\(\Delta F(\beta^{(m)}) = F(F(\beta^{(m)})) - F(\beta^{(m)})\), and
\(\Delta^2 \beta^{(m)} = \Delta F(\beta^{(m)}) - \Delta \beta^{(m)}\).
Once the coefficients \(\beta^*\) are found, we can reconstruct the
other coefficients \(\alpha^* = f_I (\beta^*)\), compute the demeaned
variables \(\tilde{y}\) and \(\tilde{X}\), and obtain our coefficients
of interest \(\hat{\gamma}\) as the OLS coefficients of estimating
\(\tilde{y}\) on \(\tilde{X}\).
Varying slopes
On top of regular fixed-effects estimations, a unique strength of
fixest is that it efficiently handles variables with varying
slopes. The concept of varying slopes parallels that of random slopes in
the mixed-effects literature, where the coefficient on one variable is
modulated by the value of another (categorical) variable. For instance,
we might wish to model individual-specific time trends in our panel,
under the following data generating process:
\[
y_{it} = \alpha_{i} + t \times \beta_i + X_{it} \gamma + \epsilon_{it}.
\]
Each individual in this setting is modeled as having their own
intercept, \(\alpha_i\), as well as their own linear time trend
coefficient \(\beta_i\). Hence there would be \(N_I\) trends to
estimate. fixest is able to estimate the set of coefficients
\(\beta_i\) all at once. We briefly expose the idea of this algorithm.
Generalizing the above example, consider a model with individual
fixed-effects and a varying slopes variable \(z_{it}\):
\[
y_{it} = \alpha_{i} + z_{it} \times \beta_i + X_{it} \gamma + \epsilon_{it}.
\]
The first order conditions for the OLS problem leads to the following
optimal values for \(\alpha_i\) and \(\beta_i\):
\[
\alpha_{i} = \dfrac{1}{n} \sum_{t=0}^{N_T}\left( y_{it} - z_{it} \beta_i - X_{it} \gamma \right) \quad \beta_{i} = \dfrac{1}{\sum_{t=0}^{N_T} z_{it}^2} \sum_{t=0}^{N_T}\left( y_{it} - \alpha_i - X_{it} \gamma \right)
\]
Taking all other values as given, we can solve for \(\alpha_i\) and
\(\beta_i\) at once, since this is a system of two equations with two
unknowns. There is a unique solution for them.\footnote{Internally, when
\(\sum_{t=0}^{N_T} z_{it}^2 = 0\), we assume the absence of the
coefficient \(\beta_i\).} Hence, to find all the coefficients
\(\alpha_i\) and \(\beta_i\), we need only solve \(N_I\) tiny systems of
equations; no iteration is needed. Internally, fixest uses
Gaussian elimination to solve these systems.
Finally, to obtain the coefficients \(\gamma\), we proceed exactly as in
the previous section. We regress \(y\) on the fixed-effects and the
variables with varying slopes to obtain the residual \(\tilde{y}\), and
obtain \(\tilde{X}\) similarly. Then we regress \(\tilde{y}\) on
\(\tilde{X}\). Using the technique mentioned above, the regression to
obtain \(\tilde{y}\) is direct and does not require any iteration.
Now let us generalize the previous example. Instead of having one
fixed-effect and one variable with varying slopes, we have a set of
\(L_I\) variables with varying slopes contained in the \(N \times L_I\)
matrix \(Z_I\), with the coefficients varying across the individual
dimension. For an individual \(i\), we have to estimate \(L_I\)
coefficients, i.e. the vector \(\vec{\alpha}_i\). We can contrast this
against the simple fixed-effects case, where we had only had to estimate
one coefficient \(\alpha_i\). Similarly for the time dimension, we have
a set of \(L_T\) variables with varying slopes in the matrix \(Z_T\).
The model we estimate is:
\[
y_{it} = Z_I\vec{\alpha}_{i} + Z_T \vec{\beta}_t + X_{it} \gamma + \epsilon_{it}.
\]
Note that the individual fixed-effects, not attached to a variable, can
be represented by a column of 1s in the matrix \(Z_I\).
As always, to obtain the coefficient of interest \(\gamma\), we first
need to obtain the residual of the OLS regression of \(y\) on all the
coefficients with varying slopes (in \(Z_I\) and \(Z_T\)). To obtain the
\(L_I\) coefficients of \(\vec{\alpha}_{i}\) and the \(L_T\)
coefficients of \(\vec{\beta}_{t}\), we apply the same fixed-point
algorithm detailed in the previous section, where we iterate across
these two dimensions.
The fact that all coefficients in a given dimension (e.g.,
\(\vec{\alpha}_{i}\)) are found at once---irrespective of the number of
variable with varying slopes---greatly reduces the complexity of the
algorithm and increases numerical stability. To the best of our
knowledge, this feature is unique to fixest.
Extending OLS to GLM and
MLE
Our exposition thus far has been limited to the OLS framework. However,
fixest also handles fixed-effects efficiently in the context of
generalized linear models (GLM) estimations and maximum likelihood
estimation (MLE). Here we briefly outline fixest's GLM and MLE
implementations, and discuss the pros and cons of each approach for
similar likelihood families.
In OLS, the conditional expectation of the dependent variable is a
linear combination of the predictor variables and the coefficients:
\(E\left(y \ |\ X\right) = X\gamma\). GLM/MLE offers more flexibility as
they model the conditional expectation of \(y\) as a function of the
linear combination of the predictors and their coefficients:
\(E\left(y \ |\ X\right) = f\left(X\gamma\right)\). Further, in both GLM
and MLE the dependent variable is modeled with a certain likelihood. The
objective of these algorithms is to maximize that likelihood with
respect to the coefficients \(\gamma\). For example, if \(y\) is a count
variable (i.e. only zero or positive integers), one natural way to model
it is with a Poisson model, such that its expectation is modeled as:
\(E\left(y \ |\ X\right) = \exp\left(X\gamma\right)\).
In fixest, the maximization algorithms of GLM and MLE differ: GLM
uses the iteratively re-weighted least squares (IRLS), while MLE uses
the direct maximum likelihood estimation with a Newton-Raphson
algorithm. We describe the key differences between these two methods
below.
fixest's GLM estimation
Given that GLM's IRLS is a succession of OLS estimations, the OLS
algorithm detailed in the previous section can be directly leveraged.
Hence fixed-effects can be estimated directly, as well as variables with
varying slopes. Further, the method works for all likelihood families
that can be estimated via GLM (Gaussian, Poisson, binomial, Gamma, etc).
fixest's MLE estimation
fixest implements the MLE method for four likelihoods: Gaussian,
Poisson, Logit and Negative Binomial. Fixed-effects are implemented via
a concentration of the likelihood, now briefly detailed. See
Berge2018_FENmlm for a full description.
Let \(\alpha\) represent all the fixed-effect coefficients and
\(\gamma\) the coefficients of interest. The log-likelihood can be
written as \(\ln L(y, X, \gamma, \alpha)\) and we want to find the
maximizing coefficients \(\hat\alpha\) and \(\hat\gamma\). Let
\(\ln \tilde L(y, X, \gamma)\) be the concentrated likelihood such that
\(\ln \tilde L(y, X, \gamma) = \max_{\alpha}\ln L(y, X, \gamma, \alpha)\).
In the case of the Gaussian likelihood, the coefficients to concentrate
the likelihood are obtained in a similar fashion as the OLS problem,
given that the first order conditions are similar. For the Poisson
likelihood, closed forms for the coefficients also exist while this is
not true for the other two likelihoods. For those last two, various
tricks are leveraged to hasten the computation of the concentrated
likelihood, detailed in Berge2018_FENmlm.
Differences between GLM and MLE procedures in
fixest
Contrary to GLM, fixest's MLE method suffers from the following
limitations: i) variables with varying slopes cannot be used, ii)
analytical weights cannot be used, iii) the families are limited to the
four hard coded ones, iv) the algorithm is more sensitive to starting
values.
However, contrary to GLM, the MLE estimation has two main advantages.
First, it includes the Negative Binomial. This likelihood family has an
ancillary parameter governing overdispersion, which cannot be directly
estimated via GLM. Second, the functional form of the right hand side is
not restricted to linear in parameter predictors. Indeed, the IRLS
algorithm of GLM estimations cannot be used directly when the predictors
are non-linearly related.
Econometric estimations with
fixest
Core estimation functions
Table (ref) summarizes the core fixest functions for
performing econometric estimations:
table[table omitted — 812 chars of source]
The user-facing API for these six core functions is virtually identical,
lowering the switching cost of moving between different estimation
routines. For example, the estimation functions share many of the same
arguments, of which the main ones are summarized in Table
(ref). Among the listed arguments, fml and
vcov deserve special mention, since they govern much of
fixest's special behavior. We cover these two arguments in
separate deep dives in Sections (ref) and (ref),
respectively.
In addition to the high-level estimation functions, fixest also
offers low-level routines for OLS (feols.fit) and GLM
(feglm.fit), respectively. Similar to base
stats::(g)lm.fit, these low-level functions offer performance
boosts at the cost of placing greater strictures on the inputs;
i.e. they require a matrix of predictors alongside the dependent
variable in vector form.
\makesavenoteenv{tabular}
\makesavenoteenv{table}
table[table omitted — 3,548 chars of source]
The fixest object class and associated
methods
Consider a simple estimation on the base R
airquality dataset:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ Temp }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ Month, airquality)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }\textcolor[rgb]{0.68,0.00,0.00}{NOTE}\textcolor[rgb]{0.37,0.37,0.37}{: 37 observations removed because of NA values (LHS: 37).}
\textcolor[rgb]{0.28,0.35,0.67}{class}\textcolor[rgb]{0.00,0.23,0.31}{(est)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] "fixest"}
\end{Highlighting}
The core fixest estimation functions all return an object of class
fixest.\footnote{An exception is the case of multiple
estimation, which returns an object of (sister) class
fixest_multi. However, the distinction is not important
here. We revisit multiple estimation in Section (ref).}
This object is a list containing many elements that are useful for
post-estimation routines, including the model scores (first
derivatives), the unscaled VCOV, the coefficients, etc. While it is
possible to access these list elements directly---e.g,
est\$nobs---that is not the recommended approach. Rather,
\texttt{fixest} implements a wide array of dedicated methods and functions
that can be applied to \texttt{fixest} objects. Users should thus invoke
these methods---e.g., \texttt{nobs(est)}---since they provide additional
convenience facilities and safeguards.
Methods from the stats
package
The base R stats package implements a variety of
generic statistical methods. Table (ref) lists the
stats methods supported by fixest objects, sometimes with
bespoke modifications.
table[table omitted — 1,890 chars of source]
Here is a simple example, where we use the update.fixest method
to add another explanatory variable (Wind) to our previous
model. We'll also save the resulting estimation for re-use:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{est2 =} \textcolor[rgb]{0.28,0.35,0.67}{update}\textcolor[rgb]{0.00,0.23,0.31}{(est, . }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ . }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Wind))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }\textcolor[rgb]{0.68,0.00,0.00}{NOTE}\textcolor[rgb]{0.37,0.37,0.37}{: 37 observations removed because of NA values (LHS: 37).}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: Ozone}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 116}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: Month: 5}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp 2.10485 0.330074 6.37692 4.4764e{-}09 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wind {-}2.78170 0.668766 {-}4.15945 6.3835e{-}05 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 20.7 Adj. R2: 0.57962 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.478995}
\end{Highlighting}
As noted, fixest supports bespoke customizations of stats
methods. Here is an illustration where we apply the
predict.fixest method to the est2 object that we just
created. The first two \texttt{predict} calls adopt standard arguments
to generate the same output as from, say, an equivalent
\texttt{update.lm} call. However, the third call demonstrates a bespoke
\textbf{\texttt{fixest}} customization, where we request predicted values for the
full original data sample (i.e, before any \texttt{NA} values have been
removed).
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{head}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{predict}\textcolor[rgb]{0.00,0.23,0.31}{(est2))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] 35.46936 44.32461 35.73850 13.54012 12.50175 27.92161}
\textcolor[rgb]{0.28,0.35,0.67}{predict}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.40,0.45,0.13}{newdata =} \textcolor[rgb]{0.28,0.35,0.67}{data.frame}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{Temp =} \textcolor[rgb]{0.68,0.00,0.00}{72}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{Wind =} \textcolor[rgb]{0.68,0.00,0.00}{8.5}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{Month =} \textcolor[rgb]{0.68,0.00,0.00}{6}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] 26.57805}
\textcolor[rgb]{0.28,0.35,0.67}{head}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{predict}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.40,0.45,0.13}{sample =} \textcolor[rgb]{0.13,0.47,0.30}{"original"}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] 35.46936 44.32461 35.73850 13.54012 NA 12.50175}
\end{Highlighting}
fixest-specific methods and
functions
In addition to the generic stats methods, fixest introduces
a variety of bespoke methods and functions for dedicated use with
fixest objects. These are summarized in Table
(ref), with a further set of worked examples provided
in the main text below.
table[table omitted — 1,968 chars of source]
\oldparagraph{Coefficients}
The goal of the fixest-specific function and methods is to
supplement and, in many cases, expand upon the generic methods defined
by base R. Consider the difference between the (generic)
coef method and the more detailed coeftable output:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{coef}\textcolor[rgb]{0.00,0.23,0.31}{(est2)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp Wind }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 2.104854 {-}2.781701 }
\textcolor[rgb]{0.28,0.35,0.67}{coeftable}\textcolor[rgb]{0.00,0.23,0.31}{(est2)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp 2.104854 0.3300739 6.376918 4.476401e{-}09}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wind {-}2.781701 0.6687662 {-}4.159452 6.383503e{-}05}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater attr(,"vcov_type")}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] "IID"}
\end{Highlighting}
Readers will note the special attribute "vcov_type" associated
with the coefficient table. Like many fixest functions,
coeftable accepts a vcov argument for on-the-fly VCOV
adjustment. We return to this idea in the VCOV deep-dive in Section
(ref).
As detailed by our theory review in Section (ref),
fixest views the fixed-effects as a set of nuisance parameters
that need to be controlled for---as efficiently as possible---in order
to derive valid results for the main parameters of interest. However, it
can still be useful to interrogate the fixed-effects coefficients
themselves (e.g., to see if they accord with intuition). These
coefficients can be retrieved with the fixef method. Here we
show that fixef yields identical coefficients to an equivalent,
but less computationally efficient, estimation with dummy variables:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est2_dummies }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ Wind }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Temp }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{as.factor}\textcolor[rgb]{0.00,0.23,0.31}{(Month), airquality)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }\textcolor[rgb]{0.68,0.00,0.00}{NOTE}\textcolor[rgb]{0.37,0.37,0.37}{: 37 observations removed because of NA values (LHS: 37).}
\textcolor[rgb]{0.28,0.35,0.67}{rbind}\textcolor[rgb]{0.00,0.23,0.31}{(}
\textcolor[rgb]{0.28,0.35,0.67}{coef}\textcolor[rgb]{0.00,0.23,0.31}{(est2_dummies, }\textcolor[rgb]{0.40,0.45,0.13}{keep =} \textcolor[rgb]{0.13,0.47,0.30}{"Month"}\textcolor[rgb]{0.00,0.23,0.31}{),}
\textcolor[rgb]{0.28,0.35,0.67}{fixef}\textcolor[rgb]{0.00,0.23,0.31}{(est2)}\textcolor[rgb]{0.37,0.37,0.37}{$}\textcolor[rgb]{0.00,0.23,0.31}{Month[}\textcolor[rgb]{0.37,0.37,0.37}{{-}}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.00,0.23,0.31}{] }\textcolor[rgb]{0.37,0.37,0.37}{{-}} \textcolor[rgb]{0.28,0.35,0.67}{fixef}\textcolor[rgb]{0.00,0.23,0.31}{(est2)}\textcolor[rgb]{0.37,0.37,0.37}{$}\textcolor[rgb]{0.00,0.23,0.31}{Month[}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.00,0.23,0.31}{]}
\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(Month)6 as.factor(Month)7 as.factor(Month)8 as.factor(Month)9}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1,] {-}16.35571 {-}8.769567 {-}7.967637 {-}17.40844}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [2,] {-}16.35571 {-}8.769567 {-}7.967637 {-}17.40844}
\end{Highlighting}
\oldparagraph{Data accessors}
The data-accessing methods of fixest deserve special attention
because they enable important internal functionality, as well as provide
building blocks for downstream packages that depend on fixest. Let
us consider some brief examples, continuing with our est2
estimation object.
The airquality dataset contains missing values and, as a
result, not all of the previous observations were used in the preceding
estimations. We can extract the indices of the used observations with
obs:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{head}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{obs}\textcolor[rgb]{0.00,0.23,0.31}{(est2))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] 1 2 3 4 6 7}
\end{Highlighting}
(Notice that the fifth observation is missing.)
Similarly, we can access the full data set with fixest_data,
or the left-hand-side and right-hand-sides with model.matrix:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{head}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{fixest_data}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.40,0.45,0.13}{sample =} \textcolor[rgb]{0.13,0.47,0.30}{"estimation"}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Ozone Solar.R Wind Temp Month Day}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 1 41 190 7.4 67 5 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 2 36 118 8.0 72 5 2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 3 12 149 12.6 74 5 3}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 4 18 313 11.5 62 5 4}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 6 28 NA 14.9 66 5 6}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 7 23 299 8.6 65 5 7}
\textcolor[rgb]{0.28,0.35,0.67}{head}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{model.matrix}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.40,0.45,0.13}{type =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"lhs"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"rhs"}\textcolor[rgb]{0.00,0.23,0.31}{)))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Ozone Temp Wind}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 1 41 67 7.4}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 2 36 72 8.0}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 3 12 74 12.6}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 4 18 62 11.5}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 6 28 66 14.9}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 7 23 65 8.6}
\end{Highlighting}
\oldparagraph{Fit statistics}
While fixest supports several functions for assessing model fit,
the single most important one to remember is fitstat. The
fitstat function provides a general interface for computing and
accessing fit statistics for fixest estimations. It is tightly
coupled with other \textbf{\texttt{fixest}} methods and functions like
\texttt{print.fixest}, where it serves as the internal engine for
retrieving relevant model fit information.
Consider an example where we want to report the following statistics
from our est2 object: (a) the number of observations, (b) R2
(R-squared), (c) adjusted-R2, and (d) the Wald test of joint coefficient
nullity.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{fitstat}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"n"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"r2"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"ar2"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"wald"}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 116}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2: 0.601553}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Adj. R2: 0.57962}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wald (joint nullity): stat = 50.106, p = 3.697e{-}16, on 2 and 109 DoF, VCOV: IID.}
\end{Highlighting}
fitstat returns an object of class fixest_fitstat
which has a dedicated print method for pretty printing, as illustrated
above. Since this object is a list, each of the statistics can be easily
accessed programmatically. We can also pass the desired fit arguments as
a one-sided formula instead of a character vector. In the example below,
we again compute the R2 and Wald test, but this time assign it to an
object. This facilitates convenient post-computation access:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{some_stats }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{fitstat}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{r2 }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ wald)}
\textcolor[rgb]{0.00,0.23,0.31}{some_stats}\textcolor[rgb]{0.37,0.37,0.37}{$}\textcolor[rgb]{0.00,0.23,0.31}{r2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} r2 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} 0.6015532 }
\textcolor[rgb]{0.00,0.23,0.31}{some\_stats}\textcolor[rgb]{0.37,0.37,0.37}{$}\textcolor[rgb]{0.00,0.23,0.31}{wald}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater $stat}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} [1] 50.10555}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} $p}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] 3.69724e{-}16}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater $df1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} [1] 2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater{} $df2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] 109}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater $vcov}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater [1] "IID"}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\end{Highlighting}
As already mentioned, fitstat is tightly linked with other
fixest methods and functions like print.fixest and
etable. Indeed, many of these downstream functions accept a
fitstat argument for customizing their return output. Consider
a simple example, where we override the default \texttt{print.fixest}
return and instead retrieve the (raw) R2 and Wald test as the required
statistics:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{print}\textcolor[rgb]{0.00,0.23,0.31}{(est2, }\textcolor[rgb]{0.40,0.45,0.13}{fitstat =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{r2 }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{wald)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: Ozone}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 116}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: Month: 5}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp 2.10485 0.330074 6.37692 4.4764e{-}09 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wind {-}2.78170 0.668766 {-}4.15945 6.3835e{-}05 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2: 0.601553}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wald (joint nullity): stat = 50.106, p = 3.697e{-}16, on 2 and 109 DoF, VCOV: IID.}
\end{Highlighting}
Note that it is also possible to enable these customizations
globally via the family of setFixest_*
functions.\footnote{We have elided over them for brevity, but the
setFixest_* functions (and their getFixest_*
counterparts) provide convenient APIs for enabling and customizing
many global settings. More information can be found in the package
vignettes and relevant help files: ?setFixest_estimation,
?setFixest_etable, etc.} For example with the following
command, the \texttt{print} methods always display the R2 and the Wald
test:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{setFixest_print}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{fitstat =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{r2 }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ wald)}
\end{Highlighting}
This can be easily reverted to the factory default with:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{setFixest_print}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{fitstat =} \textcolor[rgb]{0.56,0.35,0.01}{NULL}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
Although we do not show it here, the exact same idea can be applied to
etable, which also accepts a fitstat argument. We make
two additional observations, which apply to both print.fixest
and etable. First, when using a formula, you can add to the
default fit statistics---rather than override them---by using a point
(e.g., \textasciitilde.\ +\ wald). Second, when a particular
fit statistic contains sub-elements, like the Wald test, you can refer
to specific component directly (e.g, wald.p for the p-value,
\texttt{wald.stat} for the statistic, etc.)
Finally, please note that many other fit statistics are available
beyond those that we have used here; see
fitstat(show_types = TRUE) for a full list. It is also very
easy to add your own custom fit statistics with the function
fitstat_register. Any custom statistics that are defined in
this way will automatically be available for use with downstream methods
and functions like print and etable. Again, we must
omit examples for brevity, but readers may consult the
?fitstat_register documentation for more information.
\oldparagraph{Presentation}
We refrain from a discussion of the fixest presentation methods
here, deferring instead to the deep-dive in Section
(ref).
The fixest formula
Any fixest estimation must start with a formula describing the
relationship between variables. This formula plays a critical role in
fixest. Indeed, many features are implemented directly within the
formula and cannot be otherwise accessed. In this section we describe
how to: i) add fixed-effects, ii) estimate instrumental variable models,
iii) implement multiple estimations, iv) interpolate variables, v)
creating categorical variables and interactions, and vi) use
fixest's panel-data features. All these features are enabled via
the formula.
Fixed-effects
Table (ref) summarizes the various ways by which
fixed-effects can be included in a formula. The table also provides
equivalences to non-fixest syntax. While we provide this reference
as a convenient “cheat sheet” guide, we expand on specific
fixed-effects use cases in more detail below.
table[table omitted — 1,374 chars of source]
Individual fixed effects
To add fixed-effects in an estimation, you must add them after a pipe,
\textbar, immediately after the independent variables. The
basic syntax is:
\[
\mathtt{dep\_var} \sim \mathtt{indep\_vars} \mathtt{\ |\ } \mathtt{fixed\_effects}
\]
Let us consider an example from the trade dataset, which is
included in fixest.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{data}\textcolor[rgb]{0.00,0.23,0.31}{(trade)}
\textcolor[rgb]{0.28,0.35,0.67}{head}\textcolor[rgb]{0.00,0.23,0.31}{(trade)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Destination Origin Product Year dist_km Euros}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 1 LU BE 1 2007 139.5719 2966697}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 2 BE LU 1 2007 139.5719 6755030}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 3 LU BE 2 2007 139.5719 57078782}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 4 BE LU 2 2007 139.5719 7117406}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 5 LU BE 3 2007 139.5719 17379821}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 6 BE LU 3 2007 139.5719 2622254}
\end{Highlighting}
Below we estimate a so-called “gravity” trade model, where the trade
volume between two countries (in \texteuro) is modeled as a function of
their geographic distance. Alongside controlling for product and year
fixed-effects, note that we also include origin and destination
fixed-effects to control for bilateral trade relationships. As shown in
the third line of the output, there are 15 countries of origin and
destination, and 20 different products.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{log}\textcolor[rgb]{0.00,0.23,0.31}{(Euros) }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{log}\textcolor[rgb]{0.00,0.23,0.31}{(dist_km) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ Origin }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Destination }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Product }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Year,}
\textcolor[rgb]{0.40,0.45,0.13}{data =}\textcolor[rgb]{0.00,0.23,0.31}{ trade)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: log(Euros)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 38,325}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: Origin: 15, Destination: 15, Product: 20, Year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater log(dist_km) {-}2.16988 0.020928 {-}103.685 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 1.74337 Adj. R2: 0.705139}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.219322}
\end{Highlighting}
Combined fixed-effects
Suppose that we believe the confounding effect of any particular product
on the gravity equation depends on the original and destination country.
fixest provides a special \^ (caret) operator to
quickly combine fixest effects. Here we combine product with the origin
and destination country, respectively:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{log}\textcolor[rgb]{0.00,0.23,0.31}{(Euros) }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{log}\textcolor[rgb]{0.00,0.23,0.31}{(dist_km) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ Origin}\textcolor[rgb]{0.37,0.37,0.37}{\^}\textcolor[rgb]{0.00,0.23,0.31}{Product }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Destination}\textcolor[rgb]{0.37,0.37,0.37}{\^}\textcolor[rgb]{0.00,0.23,0.31}{Product }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Year,}
\textcolor[rgb]{0.40,0.45,0.13}{data =}\textcolor[rgb]{0.00,0.23,0.31}{ trade)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: log(Euros)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 38,325}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: Origin\^Product: 300, Destination\^Product: 300, Year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater log(dist_km) {-}2.19662 0.017087 {-}128.557 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 1.40871 Adj. R2: 0.804663}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.304684}
\end{Highlighting}
Now there are 300 country-product coefficients (the number of origin
countries times the number of products). The important increase in the
number of regressors lead to an improvement of the fit statistics, as
shown with the RMSE going down from 1.7 to 1.4.
Varying slopes
As described in Section (ref), fixest is able to
handle varying slopes very efficiently. Again, we provide a special
formula syntax to denote variables with varying slopes. Say fe
is the fixed-effect and z1 and z2 are variables whose
slopes vary according to fe. Then we specify the model as:
\[
\mathtt{dep\_var} \sim \mathtt{indep\_vars} \mathtt{\ |\ } \mathtt{fe[z_1, z_2]}
\]
Continuing with our trade example, we can add a unique year trend for
each country of origin as follows:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{log}\textcolor[rgb]{0.00,0.23,0.31}{(Euros) }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{log}\textcolor[rgb]{0.00,0.23,0.31}{(dist_km) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ Origin[Year] }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Destination, }\textcolor[rgb]{0.40,0.45,0.13}{data =}\textcolor[rgb]{0.00,0.23,0.31}{ trade)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: log(Euros)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 38,325}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: Origin: 15, Destination: 15}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Varying slopes: Year (Origin): 15}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater log(dist_km) {-}2.07276 0.027099 {-}76.4873 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 2.25895 Adj. R2: 0.505112}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.132569}
\end{Highlighting}
\[
\ln(Euros)_{ijt} = \alpha_i + \beta_j + t \times \theta_i + \gamma \ln(dist\_km) + \epsilon,
\]
with \(i\) the index of the country of origin, \(j\) the country of
destination, and \(t\) the year.\footnote{This estimation is analogous,
albeit much faster and with an exact solution, to a mixed-effects
model of form:
lme4::lmer(log(Euros) \textasciitilde log(dist_km) + Year + (Year \textbar Origin) + (1 \textbar Destination),\ trade).}
Instrumental variables
While it is not available for all model families, fixest supports
two stage least squares (2SLS) instrumental variables (IV) estimations
for linear models in feols. Table (ref) provides a
summary cheat sheet for how to perform 2SLS estimations, including
without fixed-effects.
table[table omitted — 1,326 chars of source]
Similar to the fixed-effects component, the IV part of the formula is
specified in its own slot after a \textbar. The syntax is:
\[
\mathtt{dep\_var} \sim \mathtt{exogenous\_vars} \mathtt{\ |\ } \mathtt{fixed\_effects} \mathtt{\ |\ } \mathtt{endo\_vars} \sim \mathtt{instruments}
\]
Again, we emphasize the order importance of our multipart formula. The
fixed-effect part is optional (and comes second). The IV part always
comes always last. If there is no exogenous regressor, the part
containing the exogenous regressors should be replaced with a single
1.s
In 2SLS estimations, by default only the second stage is reported. To
access the first stage(s), use the argument stage of the
summary method (Section (ref)). Let us use data
from the Fulton Fish Market the introductory example from Section
(ref), where we instrument science funding with a policy
shock to estimate the causal effect on productivity. We display both the
second stage and the first stage:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_iv }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.68,0.00,0.00}{1} \textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ indiv }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ funding }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ policy, scipubs)}
\textcolor[rgb]{0.28,0.35,0.67}{summary}\textcolor[rgb]{0.00,0.23,0.31}{(est_iv, }\textcolor[rgb]{0.40,0.45,0.13}{stage =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater IV: First stage: funding}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater TSLS estimation}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} D.V. : articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} Endo. : funding}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} Instr. : policy}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |=\textgreater First Stage}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater | Current Dep. Var.: funding}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: indiv: 108, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater policy {-}1.70991 7.50542 {-}0.227824 0.81983 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 28.1 Adj. R2: 0.014444}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 5.395e{-}5}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater F{-}test (1st stage): stat = 0.05190, p = 0.819832, on 1 and 962 DoF.}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater IV: Second stage}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater TSLS estimation}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} D.V. : articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} Endo. : funding}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-} Instr. : policy}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |=\textgreater Second Stage}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater | Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: indiv: 108, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater fit_funding {-}0.245182 1.63019 {-}0.150401 0.88048 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 10.4 Adj. R2: 0.168161}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 1.061e{-}4}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater F{-}test (1st stage), funding: stat = 0.05190, p = 0.819832, on 1 and 962 DoF.}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wu{-}Hausman: stat = 0.28442, p = 0.593945, on 1 and 961 DoF.}
\end{Highlighting}
The function fitstat can access the following fit statistics
when applied to instrumental variables estimations:
itemize{0pt}{0pt}
• ivf: the F-test of the first stage (weak instrument test)
• ivwald: the Wald-test of the first stage (weak instrument
test)
• cd: the Cragg-Donald test for weak instruments
• wh: the Wu-Hausman endogeneity test
• sargan: the Sargan test of overidentifying restrictions
Categorical variables and interactions:
i()
To tell an estimation procedure to handle numeric variables as
categorical variables, as.factor is the common way to do it in
R. fixest introduces a simple function i, to
handle categorical variables and provides easy access to: i) reordering,
ii) binning, iii) interactions.
Use the function i to treat any variable as a categorical
variable. This specific use can be seen as a replacement for
as.factor. When i is applied, the names of the
categories is as follows: "var_name::value", whereas when
using as.factor the new names are
"as.factor(var_name)value". Further, the function \texttt{i}
allows for richer integration with some \textbf{\texttt{fixest}} functions like
\texttt{etable} or \texttt{iplot}.
In the example below, let us treat the numeric variable year as
categorical, either using as.factor, either using i.
With coef we then show i's specific formatting of the
coefficient names. We end by showing the interaction with
etable and how we end up with a display of greater clarity.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_factor }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{as.factor}\textcolor[rgb]{0.00,0.23,0.31}{(year), scipubs)}
\textcolor[rgb]{0.00,0.23,0.31}{est_i }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(year), scipubs)}
\textcolor[rgb]{0.28,0.35,0.67}{coef}\textcolor[rgb]{0.00,0.23,0.31}{(est_i)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) year::2 year::3 year::4 year::5 year::6 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 14.7777778 0.7500000 0.6203704 1.1388889 1.0740741 2.8611111 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year::7 year::8 year::9 year::10 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 3.3703704 4.1296296 4.9444444 4.6018519 }
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{(est_factor, est_i, }\textcolor[rgb]{0.40,0.45,0.13}{drop.section =} \textcolor[rgb]{0.13,0.47,0.30}{"stat"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater est_factor est_i}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: articles articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Constant 14.78*** (0.5250) 14.78*** (0.5250)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)2 0.7500 (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)3 0.6204 (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)4 1.139 (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)5 1.074 (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)6 2.861*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)7 3.370*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)8 4.130*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)9 4.944*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater as.factor(year)10 4.602*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 2 0.7500 (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 3 0.6204 (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 4 1.139 (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 5 1.074 (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 6 2.861*** (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 7 3.370*** (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 8 4.130*** (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 9 4.944*** (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 10 4.602*** (0.7424)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _________________ _________________ _________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID IID}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
The function i offers two useful arguments to manipulate the
categorical variable it creates:
itemize{0pt}{0pt}
• ref: to define the reference of the categorical variable, it
can accept several values
• bin: to merge several categorical values into one
Using the example above, let us first take Month\ =\ 9 as a
reference (instead of 5), then run the same initial estimation but
grouping the months 8 and 9, and finally display the results with
etable.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_ref }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(year, }\textcolor[rgb]{0.40,0.45,0.13}{ref =} \textcolor[rgb]{0.68,0.00,0.00}{9}\textcolor[rgb]{0.00,0.23,0.31}{), scipubs)}
\textcolor[rgb]{0.00,0.23,0.31}{est_bin }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(year, }\textcolor[rgb]{0.40,0.45,0.13}{bin =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"8 {-} 9"} \textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.68,0.00,0.00}{8}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{9}\textcolor[rgb]{0.00,0.23,0.31}{)), scipubs)}
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{(est_i, est_ref, est_bin)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater est_i est_ref est_bin}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: articles articles articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Constant 14.78*** (0.5250) 19.72*** (0.5250) 14.78*** (0.5250)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 2 0.7500 (0.7424) {-}4.194*** (0.7424) 0.7500 (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 3 0.6204 (0.7424) {-}4.324*** (0.7424) 0.6204 (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 4 1.139 (0.7424) {-}3.806*** (0.7424) 1.139 (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 5 1.074 (0.7424) {-}3.870*** (0.7424) 1.074 (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 6 2.861*** (0.7424) {-}2.083** (0.7424) 2.861*** (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 7 3.370*** (0.7424) {-}1.574* (0.7424) 3.370*** (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 8 4.130*** (0.7424) {-}0.8148 (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 9 4.944*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 10 4.602*** (0.7424) {-}0.3426 (0.7424) 4.602*** (0.7425)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 1 {-}4.944*** (0.7424) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater year = 8 {-} 9 4.537*** (0.6430)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ _________________ __________________ _________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID IID IID}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 1,080 1,080 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.09357 0.09357 0.09255}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Adj. R2 0.08594 0.08594 0.08577}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
As we can see, the first two estimations are the same, only the
reference of the categorical variable has changed, leading to different
coefficient estimates (but same R2, also note that
\(76.90 - 11.35 = 65.55\)). In the last estimation where we grouped the
coefficients for the two last months, we can see that the estimates for
the other coefficients stay the same.
Now say we want to estimate yearly time trends for the EU and the US
separately, we can do so with i(cat,\ numerical):
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(eu_us) }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(eu_us, year), scipubs)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) 12.593939 0.471652 26.70174 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::US 2.661407 0.673280 3.95289 8.2252e{-}05 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year 1.055978 0.076014 13.89195 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::US:year 0.099714 0.077435 1.28772 1.9812e{-}01 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 5.11086 Adj. R2: 0.194856}
\end{Highlighting}
If we wanted to include indicators for region by year fixed effects with
i(cat,\ i.cat2). The i. specifies that the second
variable is intended to be treated as a categorical as well (borrowing
the syntax from Stata). We could alternatively specify this in the
fixed-effect portion with \textbar\ eu_us\^year.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(articles }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(eu_us) }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(eu_us, i.year), scipubs)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater The variable \textquotesingleeu_us::US:year::10\textquotesingle has been removed because of collinearity (see}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater $collin.var).}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: articles}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) 14.254545 0.690330 20.648878 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::US 1.179417 0.985442 1.196841 2.3164e{-}01 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year::2 0.872727 0.976274 0.893936 3.7156e{-}01 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year::3 1.836364 0.976274 1.880991 6.0247e{-}02 . }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year::4 1.654545 0.976274 1.694754 9.0416e{-}02 . }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year::5 2.090909 0.976274 2.141723 3.2443e{-}02 * }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year::6 4.436364 0.976274 4.544177 6.1487e{-}06 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater eu_us::EU:year::7 6.454545 0.976274 6.611405 6.0220e{-}11 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater ... 12 coefficients remaining (display them with summary() or use}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater argument n)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater ... 1 variable was removed because of collinearity (eu_us::US:year::10)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 5.072 Adj. R2: 0.195084}
\end{Highlighting}
Multiple estimations
A core design feature of fixest is that it should be easy to
estimate multiple models (or functional forms) all at once in a single
function call. In this section we focus on: i) multiple dependent
variables and ii) stepwise regressions. Again, we provide a cheat sheet
for fixest's multiple estimation syntax in Table
(ref).
table[table omitted — 1,941 chars of source]
Multiple dependent
variables
To estimate a model for multiple dependent variables, you can nest them
within the c() function on the formula LHS:
\[
\mathtt{c(y_1, y_2)} \sim \mathtt{indep\_vars} \mathtt{\ |\ } \mathtt{fixed\_effects}.
\]
This will run two regressions: one with \(\mathtt{y_1}\) as dependent
variable and one with \(\mathtt{y_2}\). Here is a simple example using
the built-in airquality dataset. Note that the default print
method automatically reverts to etable, conveniently displaying
both regressions alongside each other.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone, Solar.R) }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ Wind }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Temp, airquality)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater x.1 x.2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: Ozone Solar.R}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Constant {-}71.03** (23.58) {-}76.36 (82.00)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wind {-}3.055*** (0.6633) 2.211 (2.308)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp 1.840*** (0.2500) 3.075*** (0.8778)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ __________________ _________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID IID}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 116 146}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.56871 0.08198}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Adj. R2 0.56108 0.06914}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
Beyond syntactic convenience, estimating multiple dependent outcomes in
this way can yield dramatic performance benefits. The bulk of the
computing time in a fixed-effects estimation is spent dealing with the
fixed-effects themselves. In a multiple estimation, fixest is able
to pool this computationally-expensive step across the various models,
thus avoiding the effort duplication that would arise from estimating
the models separately. The practical upside is that estimating, say, ten
models will typically take the same amount of time as estimating a
single model on its own.\footnote{An important caveat is that this
pooling acceleration shortcut only applies to OLS. For GLM models, the
more complex model structure means that the same shortcut cannot
readily be used.} We benchmark these gains with a large dataset in
Section (ref).
Stepwise regressions
Multiple estimation is also supported on the formula RHS. For example,
all fixest estimations support a family of special functions for
stepwise regressions: sw (stepwise), sw0 (stepwise,
starting from the empty element), csw (cumulative stepwise),
csw0 (cumulative stepwise starting with the empty element), and
\texttt{mvsw} (multiverse stepwise, i.e. crossing). These functions
accept any number of variables as argument, and can be used directly in
a formula as regular variables. Moreover, they can be used in both the
regular part of the formula RHS (containing the explanatory variables),
or in the fixed-effects part. However, note that only stepwise function
is allowed per part.
Here is an example where we run six estimations in a single
feols call. Notice how expressive the syntax is, despite its
compactness. Once you know that sw stands for
stepwise and csw for cumulative
\textbf{s}tep\textbf{w}ise, and that a \texttt{0} suffix enforces an
empty starting element, the code is very easy to understand.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{sw}\textcolor[rgb]{0.00,0.23,0.31}{(Wind, Temp) }\textcolor[rgb]{0.37,0.37,0.37}{|} \textcolor[rgb]{0.28,0.35,0.67}{csw0}\textcolor[rgb]{0.00,0.23,0.31}{(Month, Day), airquality)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }\textcolor[rgb]{0.68,0.00,0.00}{NOTE}\textcolor[rgb]{0.37,0.37,0.37}{: 37 observations removed because of NA values (LHS: 37).}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater |{-}\textgreater this msg only concerns the variables common to all estimations}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater x.1 x.2 x.3}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: Ozone Ozone Ozone}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Constant 96.87*** (7.239) {-}147.0*** (18.29) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wind {-}5.551*** (0.6904) {-}4.643*** (0.7019)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp 2.429*** (0.2331) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}Effects: {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-} {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-} {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Month No No Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Day No No No}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ __________________ _________________ __________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID IID IID}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 116 116 116}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.36186 0.48771 0.45290}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2 {-}{-} {-}{-} 0.28462}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater x.4 x.5 x.6}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: Ozone Ozone Ozone}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Constant }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Wind {-}4.454*** (0.7554) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Temp 2.704*** (0.3182) 2.952*** (0.3579)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}Effects: {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-} {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-} {-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Month Yes Yes Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Day No Yes Yes}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ _________________ __________________ _________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type IID IID IID}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 116 115 115}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.53831 0.66733 0.74210}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2 0.39630 0.30290 0.45957}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
Formula interpolation
Another key design feature of fixest formulas is the ease of
interpolating variables. Programmatic formula manipulation in
R is a long-standing issue, for which users often resort to
ad hoc solutions.\footnote{For example,
as.formula(paste(...)). While stats::reformulate
provides a mechanism for programmatic formula construction, it is
limited to simple (single part) formulas and does not support
interpolation.} In contrast, \textbf{\texttt{fixest}} support three primary ways
to interpolate variables directly in the formula itself: i) with formula
macros, ii) with regular expressions, and iii) with the
dot-square-bracket operator.
A cheat sheet of all possible interpolations in Table
(ref). We also work through some explicit examples
below, and explain the role of the xpd function, which powers
all of fixest's interpolation features under the hood.
table[table omitted — 2,851 chars of source]
Formula macros
Users can define (global) formula macros in fixest with the
function setFixest_fml. While the function accepts any number
of arguments, each argument name must start with two dots and the
arguments themselves must be either a character vector or a one-sided
formula.\footnote{The ..\textlessmacro\textgreater naming
restriction ensures that macros can easily be distinguished from
regular variables.} For example
setFixest_fml(..controls\ =\ c("Wind",\ "Temp")) creates a
macro variable named ..controls. This variable can now be used
in any \textbf{\texttt{fixest}} estimation as shown below:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{setFixest_fml}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{..controls =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{Wind }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Temp)}
\textcolor[rgb]{0.28,0.35,0.67}{formula}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ ..controls, airquality, }\textcolor[rgb]{0.40,0.45,0.13}{notes =} \textcolor[rgb]{0.56,0.35,0.01}{FALSE}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Ozone \textasciitilde Wind + Temp}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater \textlessenvironment: 0x000002372212fea8\textgreater}
\end{Highlighting}
Users may create as many macros as they wish through
setFixest_fml. Macros can also contain fixed-effects if they
are created with a formula,
e.g. ..controls\ =\ \textasciitildeWind\ \textbar\ Month.
Macros can prove especially useful in analysis scripts, with clear
definitions of recurring sets of variables. One caveat, though is that
we don't recommend using macros in loops. For this use case,
interpolation with the dot-square-bracket operator, later described, is
better suited.
Regex interpolation
Another way to interpolate variables in formulas is through regular
expressions. Use ..("regex") or regex("regex") to
include in the formula any variable in the data set that matches the
regular expression. When a variable name in the formula ends with two
dots, any variable in the data set starting with the same letters are
included. Here is an example of these two cases:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{formula}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{..}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"Wi|Tem"}\textcolor[rgb]{0.00,0.23,0.31}{) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ Mo.., airquality, }\textcolor[rgb]{0.40,0.45,0.13}{notes =} \textcolor[rgb]{0.56,0.35,0.01}{FALSE}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Ozone \textasciitilde Wind + Temp | Month}
\end{Highlighting}
Dot-square-bracket
Finally, a powerful way to interpolate variables is with the
dot-square-bracket operator. When expressions of the form
.{[}x{]} are found in a formula, the value of the variable
x is directly inserted in the formula. When x is a
vector, the values are combined with a +.
For example, let us reimplement our formula macro via the
dot-square-bracket operator:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{x }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"Wind"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"Temp"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.28,0.35,0.67}{formula}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ .[x], airquality, }\textcolor[rgb]{0.40,0.45,0.13}{notes =} \textcolor[rgb]{0.56,0.35,0.01}{FALSE}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Ozone \textasciitilde Wind + Temp}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater \textlessenvironment: 0x000002371dac77a0\textgreater}
\end{Highlighting}
This interpolation makes it very easy to work with formulas
programmatically within loops.
As illustrated above, when the interpolation is on several variables,
they are combined with a plus (i.e. if x\ =\ c("a",\ "b"), then
.{[}x{]} becomes a\ +\ b in the formula). However,
sometimes we may want to combine them with a comma, especially when
inserting the variables as several arguments of a function. You can
override the default behavior but adding a comma right after the opening
bracket, as in .{[},x{]}. Let us estimate multiple models that
way and access the coefficients:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{x }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"Wind"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"Temp"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{sw}\textcolor[rgb]{0.00,0.23,0.31}{(.[,x]), airquality, }\textcolor[rgb]{0.40,0.45,0.13}{notes =} \textcolor[rgb]{0.56,0.35,0.01}{FALSE}\textcolor[rgb]{0.00,0.23,0.31}{) }\textcolor[rgb]{0.37,0.37,0.37}{|\textgreater}
\textcolor[rgb]{0.28,0.35,0.67}{coef}\textcolor[rgb]{0.00,0.23,0.31}{()}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater id rhs (Intercept) Wind Temp}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 1 1 Wind 96.87289 {-}5.550923 NA}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater 2 2 Temp {-}146.99549 NA 2.428703}
\end{Highlighting}
xpd: generalized formula interpolation and
expansion
Under the hood, all interpolation features in fixest are powered
by the function xpd. This function ensures that interpolated
formulas are expanded to their correct full form. For example, we could
have used xpd to verify that the expanded formula in our
previous example, prior to running the actual estimation:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{xpd}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{sw}\textcolor[rgb]{0.00,0.23,0.31}{(.[,x]))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Ozone \textasciitilde sw(Wind, Temp)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater \textlessenvironment: 0x000002371fd59ac8\textgreater}
\end{Highlighting}
As the reader may have noticed, this example also demonstrates that
xpd can be used on “regular” formulas outside of the main
fixest estimation functions. The only difference is that these
native fixest functions automatically use xpd to expand
their formulas internally and thus do not need an explicit \texttt{xpd}
call.
The ?xpd documentation covers a variety of use-cases and
features, which we do not recapitulate here. Instead, we content
ourselves with a final example where the desired outcome is
interpolating over variables with numerical suffixes. In this particular
case, assume that we wish include the variables x1--x5
in a regression. When the dot-square-bracket operator is attached to a
variable, this variable can be used as the interpolation prefix, as
follows:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{xpd}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ x.[}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{5}\textcolor[rgb]{0.00,0.23,0.31}{])}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater y \textasciitilde x1 + x2 + x3 + x4 + x5}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater \textlessenvironment: 0x000002371fdf3568\textgreater}
\end{Highlighting}
Formula features for panel
data
The last features we will highlight in this section are functions that
are designed to work with time-series or panel data. In panel data
settings, where you have unit-time identifiers, it is often useful to
quickly compute the leads, lags, and/or differences of variables. The
fixest formula provides a family of convenience functions for
computing these transformations on the fly: l, f and
d. However, to access these functions \texttt{fixest} must first
know what the unit and time identifiers are. This information can be
passed either via the run-time \texttt{panel.id} argument, or by
explicitly setting the dataset as a panel via the \texttt{panel}
function.
Let us demonstrate with a quick example of the former approach. Here we
specify the panel identifiers as formula to panel.id, which in
turn allows us to concisely estimate the lead of the dependent variable
(f(y)) on the dependent variable and its three first lags
(l(x1,\ 0:3)).
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{data}\textcolor[rgb]{0.00,0.23,0.31}{(base_did)}
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{f}\textcolor[rgb]{0.00,0.23,0.31}{(y) }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{l}\textcolor[rgb]{0.00,0.23,0.31}{(x1, }\textcolor[rgb]{0.68,0.00,0.00}{0}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{3}\textcolor[rgb]{0.00,0.23,0.31}{), base_did, }\textcolor[rgb]{0.40,0.45,0.13}{panel.id =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{id }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ period)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }\textcolor[rgb]{0.68,0.00,0.00}{NOTE}\textcolor[rgb]{0.37,0.37,0.37}{: 432 observations removed because of NA values (LHS: 108, RHS: 324).}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: f(y)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 648}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) 3.138156 0.228711 13.721072 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater l(x1, 0) 0.024668 0.080943 0.304762 0.760646 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater l(x1, 1) 0.095704 0.080963 1.182069 0.237615 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater l(x1, 2) 0.069380 0.078556 0.883194 0.377462 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater l(x1, 3) 0.162121 0.077900 2.081127 0.037817 * }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 5.79176 Adj. R2: 0.004676}
\end{Highlighting}
It should be emphasized that fixest's panel functionality offers
rich support for handling ambiguous and irregular timesteps, as well as
missing observations. For example, users can pass a time.step
argument to the panel function to delineate between daily data
that excludes weekends. Again, we refer the interested reader to the
package documentation (e.g., ?panel) for more examples.
Differences-in-differences
fixest also has a set of tools for estimating
difference-in-differences (DiD) models, which are a particularly popular
research design in applied econometrics settings
huntington2021effect. Using the base_stagg data set
provided in fixest, let us illustrate a DiD analysis. In this
example we interact the variable period, used as categorical,
with the variable \texttt{treat} and set the reference to the period 5,
the period right before the treatment takes place. We also include unit
and time fixed-effects.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{data}\textcolor[rgb]{0.00,0.23,0.31}{(base_stagg, }\textcolor[rgb]{0.40,0.45,0.13}{package =} \textcolor[rgb]{0.13,0.47,0.30}{"fixest"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.00,0.23,0.31}{est_did }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(time_to_treatment, }\textcolor[rgb]{0.40,0.45,0.13}{ref =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.37,0.37,0.37}{{-}}\textcolor[rgb]{0.68,0.00,0.00}{1000}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.37,0.37,0.37}{{-}}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.00,0.23,0.31}{)) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ id }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, }
\textcolor[rgb]{0.00,0.23,0.31}{ base_stagg)}
\textcolor[rgb]{0.00,0.23,0.31}{est_did}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: y}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 950}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Fixed{-}effects: id: 95, year: 10}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: IID }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}9 3.146558 1.190534 2.642982 0.0083731 ** }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}8 0.664518 0.882894 0.752659 0.4518683 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}7 1.049788 0.745073 1.408973 0.1592180 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}6 0.815505 0.661099 1.233560 0.2177166 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}5 0.349854 0.602687 0.580490 0.5617420 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}4 0.870255 0.559299 1.555975 0.1200957 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}3 0.384655 0.526132 0.731100 0.4649248 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater time_to_treatment::{-}2 0.552281 0.500778 1.102846 0.2704143 }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater ... 9 coefficients remaining (display them with summary() or use}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater argument n)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 2.13618 Adj. R2: 0.445625}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Within R2: 0.33628 }
\end{Highlighting}
Alternatively, there has been new proposals for robust DID estimators
that are valid when treatment-timing is staggered. The sunab()
function implements the method from sun2021estimating.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_did_sunab }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde} \textcolor[rgb]{0.28,0.35,0.67}{sunab}\textcolor[rgb]{0.00,0.23,0.31}{(year_treated, year) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ id }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year, base_stagg)}
\end{Highlighting}
It is very easy to plot both of these estimates thanks to the
iplot function.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{iplot}\textcolor[rgb]{0.00,0.23,0.31}{(est_did, est_did_sunab, }\textcolor[rgb]{0.40,0.45,0.13}{drop =} \textcolor[rgb]{0.13,0.47,0.30}{"1000"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.28,0.35,0.67}{legend}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"topleft"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"TWFE"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.13,0.47,0.30}{"Sun and Abraham"}\textcolor[rgb]{0.00,0.23,0.31}{), }\textcolor[rgb]{0.40,0.45,0.13}{col =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{lty =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
The graph is represented in Figure (ref).
figure[figure omitted — 178 chars of source]
Adjusting the
standard-errors
Next to the coefficients themselves, the variance-covariance matrix
(VCOV) is arguably the most important element of any estimation. This is
the arbiter of statistical significance and the determinant for any
inferential claims about our results. Given its prominence, fixest
aims to streamline the way VCOVs are selected through a simple and
intuitive interface. Specifically, all of the core estimating functions,
as well as many of the methods, accept a named vcov argument.
This interface is tightly integrated with the rest of package and
enables many synergies, such as accessing multiple VCOVs at estimation
time or when exporting the results. Moreover, a core design principle of
fixest is that it should be easy to change a model's VCOV
post estimation, which in turn enables on-the-fly inference
adjustments to standard-errors.
In this section, we first present fixest's built-in VCOVs and how
to parameterize them. We then show how to integrate the package with
other software to compute additional VCOVs.
Built-in VCOVs
fixest provides native support for a variety of VCOVs. These
built-in VCOVs cover some of the most common adjustments that are
required in applied econometrics research and also offer best-in-class
performance. The full set of these built-in VCOVs are documented in
Table (ref), including specification options and
required arguments. For the sake of brevity, we list the keyword
shortcuts for these built-in VCOVs below:
itemize{0pt}{0pt}
• "iid": the default VCOV, without any adjustment, where it is
assumed that the errors are homoskedastic and non-correlated
• "hc(1-3)": various heteroskedasticity-robust VCOVs, where
each error has its own variance a la
white1980heteroskedasticity and
mackinnon1985heteroskedasticity; note that "hetero"
is an alias for "hc1"
• "cluster": clustered VCOV, where arbitrary correlation within
groups (clusters) is allowed
• \texttt{"twoway"}: double clustering of the VCOV
• \texttt{"conley"}: VCOV that accounts for spatial correlation across
errors, \emph{a la} conley1999gmm
• \texttt{"NW"}: in a time-series or panel time-series context, the
Newey-West VCOV accounts for serial correlation within units and
heteroskedasticity newey1987simple
• \texttt{"DK"}: in a time-series or panel time-series context, the
Driscoll-Kraay VCOV accounts for serial correlation within units and
arbitrary cross-sections; correlation across units within a time
period driscoll1998consistent
To invoke any of these built-in VCOVs, users need simply pass the
relevant keyword to the vcov argument. As we have tried to
emphasize, this can be done either at estimation time or as part of a
post-estimation method. Many fixest functions accept the
vcov argument and in the below example we demonstrate using the
se function, which retrieves standard errors. First, we specify
heteroskedastic-robust errors at estimation, before reverting to
spherical errors for comparison in a subsequent call.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_vcov }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ x1, base_did, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.13,0.47,0.30}{"hc1"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.28,0.35,0.67}{rbind}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{hc1 =} \textcolor[rgb]{0.28,0.35,0.67}{se}\textcolor[rgb]{0.00,0.23,0.31}{(est_vcov),}
\textcolor[rgb]{0.40,0.45,0.13}{iid =} \textcolor[rgb]{0.28,0.35,0.67}{se}\textcolor[rgb]{0.00,0.23,0.31}{(est_vcov, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.13,0.47,0.30}{"iid"}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) x1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater hc1 0.1490257 0.05101605}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater iid 0.1491554 0.05011910}
\end{Highlighting}
While this example is very simple, it demonstrates the ease with which
fixest allows users to adjust VCOVs and, thus, standard errors.
Moreover, the on-the-fly adjustment underscores a key
fixest design principle: estimation is separate from inference.
This principle enables user-level flexibility in addition to
facilitating computational efficiency, since we only need to recompute
the model scores instead of re-estimating the entire model.
Beyond keyword strings, the vcov argument also accepts various
other inputs. Again, these are detailed in full in Table
(ref). To quickly highlight one particularly common
use-case in panel-based econometrics, users can specify clustered
standard errors by passing a one-sided formula denoting which
variable(s) to cluster on. For example:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ x1, base_did, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{id)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: y}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: Clustered (id) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) 1.988753 0.194352 10.2327 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater x1 0.983110 0.046789 21.0115 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 4.89686 Adj. R2: 0.262357}
\end{Highlighting}
Continuing with non-keyword inputs to vcov, note that some
VCOVs may require additional information. For example, Driscoll-Kraay
VCOVs require unit-time identifiers, while Conley VCOVs require
geographic information. In general, fixest tries to deduce any
additional VCOV arguments using simple but robust rules of
thumb.\footnote{For example,
feols(depth\ \textasciitilde\ mag,\ quakes,\ vcov\ =\ "conley")
specifies Conley standard errors in the base R quakes dataset
to allow for arbitrary spatial auto correlation. Normally this
requires explicit latitude and longitude information. But \texttt{fixest}
automatically deduces that this information is furnished by the
\texttt{lat} and \texttt{long} columns of the dataset.} But users can
also pass additional arguments explicitly in one of two ways. First, by
specifying a special helper function, whose name is the VCOV keyword
prefixed with \texttt{vcov_}. For example, \texttt{vcov_cluster} has
the argument \texttt{cluster} accepting a one-sided formula or a
character scalar specifying the cluster variable. Second, by invoking a
formula with the following syntax:
\[
\mathtt{vcov} = \mathtt{VCOV\_name} \sim \mathtt{variables}.
\]
Here, VCOV_name is a function that takes additional arguments.
For example, NW and DK accept the argument lag, and
conley accept the arguments cutoff, pixel and
\texttt{distance}.
Re-using the same estimation from above, let us report the Newey-West
and clustered standard-errors using these two approaches. This time,
we'll pass the key vcov argument a list of VCOVs from an
etable call.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{(est_vcov, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"hc1"}\textcolor[rgb]{0.00,0.23,0.31}{, NW }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ id }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ period, }\textcolor[rgb]{0.28,0.35,0.67}{vcov_cluster}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ id)))}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater est_vcov est_vcov.1 est_vcov.2}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Dependent Var.: y y y}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Constant 1.989*** (0.1490) 1.989*** (0.1741) 1.989*** (0.1944)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater x1 0.9831*** (0.0510) 0.9831*** (0.0527) 0.9831*** (0.0468)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater _______________ __________________ __________________ __________________}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater S.E. type Heteroskedas.{-}rob. Newey{-}West (L=1) by: id}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations 1,080 1,080 1,080}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater R2 0.26304 0.26304 0.26304}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Adj. R2 0.26236 0.26236 0.26236}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\end{Highlighting}
As a final remark about the built-in VCOVs, fixest applies a small
sample correction of \(N / (N - K)\), where \(N\) is the number of
observations and \(K\) the number of variables. However, this small
sample correction can be disabled or adjusted in various ways via the
ssc function. Small sample corrections can play a surprisingly
prominent role in lining up results from different econometrics
software, particularly in the context of clustered VCOVs (where multiple
corrections are potentially valid from a theory perspective). We omit
further discussion for the sake of brevity here. But the interested
reader is referred to the dedicated
“\href{https://lrberge.github.io/fixest/articles/standard_errors.html}{On
standard errors}” vignette for an exhaustive discussion and comparison
between fixest and other popular panel econometrics packages.
table[table omitted — 3,936 chars of source]
Access to external VCOVs
Beyond the built-in VCOVs, the vcov argument also accepts
custom functions and VCOVs from packages. For example, fixest has
a relatively good compatibility with the well-known sandwich
package
zeileis2006ObjectorientedComputationSandwich, zeileis2020VariousVersatileVariances,
which specializes in computing the variance-covariance matrices.
However, there are a few important caveats. Some functions from
\texttt{sandwich} do not work well with \textbf{\texttt{fixest}} estimations when
fixed-effects are present, due to the way that these fixed-effects are
handled. Similarly, while most \textbf{\texttt{sandwich}} functions have a
\texttt{fixest} method, there is not a drop-in replacement for
\texttt{vcovBS()}, \texttt{vcovJK()}, and \texttt{vcovOPG()}. For these
reasons, we recommend that users rely on native \textbf{\texttt{fixest}} VCOVs where
possible.
Users can also define their custom VCOVs and pass them to
summary, etable, coeftable, etc. In this next
example, we compute a bootstrapped VCOV and display the associated
results.\footnote{As an aside, fixest provides a dedicated
estimation environment that, among other things, allows for extremely
efficient Bayesian bootstrap computation. See the ?est_env
documentation for an example.} Note that we pass our custom VCOV
through a named list for pretty printing and display.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{data}\textcolor[rgb]{0.00,0.23,0.31}{(iris)}
\textcolor[rgb]{0.00,0.23,0.31}{est }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Petal.Length }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ Sepal.Length, iris)}
\textcolor[rgb]{0.28,0.35,0.67}{set.seed}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.00,0.23,0.31}{n }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{nrow}\textcolor[rgb]{0.00,0.23,0.31}{(iris)}
\textcolor[rgb]{0.00,0.23,0.31}{all_coefs }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{lapply}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{99}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.00,0.23,0.31}{function}\textcolor[rgb]{0.00,0.23,0.31}{(i) \
\textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Petal.Length }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ Sepal.Length, iris[}\textcolor[rgb]{0.28,0.35,0.67}{sample}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.00,0.23,0.31}{n, n, }\textcolor[rgb]{0.40,0.45,0.13}{replace =} \textcolor[rgb]{0.56,0.35,0.01}{TRUE}\textcolor[rgb]{0.00,0.23,0.31}{), ], }
\textcolor[rgb]{0.40,0.45,0.13}{only.coef =} \textcolor[rgb]{0.56,0.35,0.01}{TRUE}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.00,0.23,0.31}{\})}
\textcolor[rgb]{0.00,0.23,0.31}{all_coefs }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{do.call}\textcolor[rgb]{0.00,0.23,0.31}{(rbind, all_coefs)}
\textcolor[rgb]{0.00,0.23,0.31}{boot_vcov }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{var}\textcolor[rgb]{0.00,0.23,0.31}{(all_coefs)}
\textcolor[rgb]{0.00,0.23,0.31}{est_boot }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{summary}\textcolor[rgb]{0.00,0.23,0.31}{(est, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"My Bootstrapped SEs"} \textcolor[rgb]{0.00,0.23,0.31}{=}\textcolor[rgb]{0.00,0.23,0.31}{ boot_vcov))}
\textcolor[rgb]{0.28,0.35,0.67}{print}\textcolor[rgb]{0.00,0.23,0.31}{(est_boot)}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater OLS estimation, Dep. Var.: Petal.Length}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Observations: 150}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Standard{-}errors: My Bootstrapped SEs }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Estimate Std. Error t value Pr(\textgreater|t|) }
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater (Intercept) {-}7.10144 0.414480 {-}17.1334 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Sepal.Length 1.85843 0.067392 27.5764 \textless 2.2e{-}16 ***}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater {-}{-}{-}}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater Signif. codes: 0 \textquotesingle***\textquotesingle 0.001 \textquotesingle**\textquotesingle 0.01 \textquotesingle*\textquotesingle 0.05 \textquotesingle.\textquotesingle 0.1 \textquotesingle \textquotesingle 1}
\textcolor[rgb]{0.37,0.37,0.37}{\#\textgreater RMSE: 0.86201 Adj. R2: 0.758333}
\end{Highlighting}
Presentation
In this section, we discuss in turn how to display the results in a
table or in a graph.
Formatting the results into a
table
As we have already seen from the preceding examples, the etable
function allows us to export the results from (multiple) fixest
estimations to a nicely formatted regression table. The table can either
be displayed directly in the R console or exported as a \LaTeX table.
It has the following main arguments:
itemize{0pt}{0pt}
• ...: this is where we can pass any number of estimations
(fixest of fixest_multi objects)
• vcov: to define the way the VCOV is computed for all
estimations, see Section (ref) for details. It is possible to
provide a list of VCOVs in which case the models are replicated for
each VCOV.
• dict: a dictionary in the form of a named vector whose names
are the variable names and values are the labels to be displayed
• \texttt{keep}, \texttt{drop}, \texttt{order}: vectors of regular
expressions to keep, drop or order the coefficients in the table. It
is possible to negate the regular expression by starting it with an
exclamation mark.
• \texttt{tex}: logical. If \texttt{FALSE}, the function returns a
\texttt{data.frame} of class \texttt{etable_df} with a custom print
method. If \texttt{TRUE}, the function returns a character vector
containing a \LaTeX table. By default, it is equal to \texttt{FALSE}
if the argument \texttt{file} is missing, and true otherwise.
• \texttt{file}: path to a file where to write the table
• \texttt{style.df}: an object returned by \texttt{style.df()}. How to
style the table displayed in the console.
• \texttt{style.tex}: an object returned by \texttt{style.tex()}. How to
style the \LaTeX, table.
The factory defaults of etable are usually good enough for
displaying results in the R console and there is little need for further
customization. However, the situation is more complicated when creating
\LaTeX publication table, since these often require fine-grained control
and detailing. The default \LaTeX table produced by etable is
quite plain, yet contains all of the necessary information to uniquely
identify the context of an estimation, as well as interpret it.
Specifically, we report: the sample (if the sample was split), the type
of standard errors, the family when several estimations have different
families (e.g., OLS or Poisson), convergence state when one model did
not converge, etc. We can therefore think of the default etable
output as appropriate for an exploratory table, where we want as much
information as possible without worrying too much about aesthetics.
Table (ref), from our motivating example back in Section
(ref), provides an example of such a table.
For published tables, the ability to style, as well as precisely add
pieces of information, take on heightened importance. etable
caters to the needs of publication-focused users by offering over 60
arguments to customize their table. Striking the balance for usability
here requires nuance, since the number of functions can be overwhelming.
However, the basic philosophy is to simply set your table preferences
once and then forget about them. Thanks to the function
setFixest_etable() it is possible to set the styling arguments
globally. Further, the function setFixest_dict() sets the
labels of the arguments globally.
To illustrate, let us revisit our earlier Table (ref)
example. We re-use the saved est_split and est_iv
estimations from before and our etable call also remains
unchanged (we pass the exact same arguments). Our only changes are i)
adding labels to the variables with setFixest_dict, and ii)
changing the default \LaTeX style with setFixest_etable. The
resulting table is displayed in Table (ref) and
reveal a number of aesthetic improvements.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{setFixest_dict}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{articles =} \textcolor[rgb]{0.13,0.47,0.30}{"\# Articles"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{funding =} \textcolor[rgb]{0.13,0.47,0.30}{"Funding (\textquotesingle000 $US)"}\textcolor[rgb]{0.00,0.23,0.31}{, }
\textcolor[rgb]{0.40,0.45,0.13}{year =} \textcolor[rgb]{0.13,0.47,0.30}{"Year"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{eu_us =} \textcolor[rgb]{0.13,0.47,0.30}{"Region"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{policy =} \textcolor[rgb]{0.13,0.47,0.30}{"Policy"}\textcolor[rgb]{0.00,0.23,0.31}{, }
\textcolor[rgb]{0.40,0.45,0.13}{indiv =} \textcolor[rgb]{0.13,0.47,0.30}{"Researcher"}\textcolor[rgb]{0.00,0.23,0.31}{))}
\textcolor[rgb]{0.28,0.35,0.67}{setFixest_etable}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{style.tex =} \textcolor[rgb]{0.28,0.35,0.67}{style.tex}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"aer"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{signif.code =} \textcolor[rgb]{0.56,0.35,0.01}{NA}\textcolor[rgb]{0.00,0.23,0.31}{), }
\textcolor[rgb]{0.40,0.45,0.13}{fitstat =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{n }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ my)}
\textcolor[rgb]{0.28,0.35,0.67}{etable}\textcolor[rgb]{0.00,0.23,0.31}{(est_split, est_iv, }
\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{indiv,}
\textcolor[rgb]{0.40,0.45,0.13}{stage =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{, }
\textcolor[rgb]{0.40,0.45,0.13}{caption =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"Compiling the results from five estimations: "}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.13,0.47,0.30}{"LaTeX rendering with options."}\textcolor[rgb]{0.00,0.23,0.31}{),}
\textcolor[rgb]{0.40,0.45,0.13}{label =} \textcolor[rgb]{0.13,0.47,0.30}{"tab_first_ex_options"}\textcolor[rgb]{0.00,0.23,0.31}{, }
\textcolor[rgb]{0.40,0.45,0.13}{file =} \textcolor[rgb]{0.13,0.47,0.30}{"tables/first_example_options.tex"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
table[table omitted — 1,431 chars of source]
There are many etable options beyond the ones presented here,
including special integrations for R Markdown and Quarto documents, as
well as support for coefficient highlighting, three-part tables, etc.
The fixest website provides further details on how to customize
regression tables in the form of standalone documentation
(e.g. \href{https://lrberge.github.io/fixest/reference/etable.html}{?etable},
\href{https://lrberge.github.io/fixest/reference/style.tex.html}{?style.tex})
and two vignettes
(“\href{https://lrberge.github.io/fixest/articles/exporting_tables.html}{Exporting
estimation tables}” and
“\href{https://lrberge.github.io/fixest/articles/etable_new_features.html}{etable:
new features in \texttt{fixest} 0.10.2}”).
Plotting the coefficients
Alongside tabling support, fixest also provides two dedicated
plotting functions for plotting the coefficients and confidence
intervals: coefplot and iplot. coefplot plots
the point estimates and confidence intervals of coefficients from one or
several \texttt{fixest} estimations. \texttt{iplot} does the same, but
limited to categorical variables or interaction terms created with the
function \texttt{i()}; see Section (ref).
Some of the main arguments are:
itemize{0pt}{0pt}
• ...: it receives fixest estimations or a list of
fixest estimations
• vcov: how to compute the VCOV for the estimations in input,
it can be a list of different ways to compute the VCOVs
• \texttt{ci_level}: the level of the confidence interval, the default
is 0.95
• \texttt{keep}, \texttt{drop}, \texttt{order}: vectors of regular
expressions to keep, drop or order the coefficients in the table. It
is possible to negate the regular expression by starting it with an
exclamation mark
• \texttt{pt.col}, \texttt{pt.cex}, \texttt{pt.lwd}, \texttt{pt.pch}:
color, size, width and point type of the coefficient estimate
• \texttt{ci.col}, \texttt{ci.lty}, \texttt{ci.lwd}: color, line type
and line width of the edge of the confidence interval
• \texttt{pt.join}, \texttt{pt.join.par}: whether the point estimated
should be joined with a line and, in that case, the parameters of the
line (a list of \texttt{lwd}, \texttt{col}, and \texttt{lty})
• \texttt{ci.join}, \texttt{ci.join.par}: same as above, for the edges
of the confidence intervals
• \texttt{ci.fill}, \texttt{ci.fill.par}: whether the space between the
confidence intervals should be filled with a color, and the parameters
of such filling (\texttt{col} and \texttt{alpha})
• \texttt{group}: a list containing variables to appear grouped, by
default it groups the factors
Paralleling etable, users can set any of these parameters
globally via the setFixest_coefplot() function, and thus
define a preferred aesthetic for all of their plots.
In Figure (ref) below, we combine several examples to
demonstrate fixest's plotting facilities. We start by performing a
single estimation (with "iid" errors) and then obtaining a
second version with clustered standard errors.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_airq }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(Ozone }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ Temp }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ Wind }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(Month), airquality)}
\textcolor[rgb]{0.00,0.23,0.31}{est_airq_clu }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{summary}\textcolor[rgb]{0.00,0.23,0.31}{(est_airq, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{Day)}
\end{Highlighting}
Our first graph (top left) is a simple coefplot call,
demonstrating the default behavior and the ease with which multiple
estimations are passed.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{coefplot}\textcolor[rgb]{0.00,0.23,0.31}{(est_airq, est_airq_clu, }\textcolor[rgb]{0.40,0.45,0.13}{drop =} \textcolor[rgb]{0.13,0.47,0.30}{"Constant"}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
The second graph (top right) is a tweaked version of the first, flipping
the plot axes and passing various other customizations arguments.
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{coefplot}\textcolor[rgb]{0.00,0.23,0.31}{(est_airq, est_airq_clu, }\textcolor[rgb]{0.40,0.45,0.13}{drop =} \textcolor[rgb]{0.13,0.47,0.30}{"Constant"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{horiz =} \textcolor[rgb]{0.56,0.35,0.01}{TRUE}\textcolor[rgb]{0.00,0.23,0.31}{, }
\textcolor[rgb]{0.40,0.45,0.13}{lwd =} \textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{pt.pch =} \textcolor[rgb]{0.28,0.35,0.67}{c}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{20}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{21}\textcolor[rgb]{0.00,0.23,0.31}{), }\textcolor[rgb]{0.40,0.45,0.13}{ci.lty =} \textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{zero.par =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{lty =} \textcolor[rgb]{0.68,0.00,0.00}{3}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{col =} \textcolor[rgb]{0.13,0.47,0.30}{"darkgrey"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{lwd =} \textcolor[rgb]{0.68,0.00,0.00}{3}\textcolor[rgb]{0.00,0.23,0.31}{))}
\end{Highlighting}
The third graph (bottom left) is the same data plotted with
iplot and its default values, which then focuses only on the
categorical variable that was included with i(). Moreover,
instead of having two estimation objects with different standard-errors,
here we demonstrate that users can also have only one estimation object
and pass several VCOVs using the argument vcov:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.28,0.35,0.67}{iplot}\textcolor[rgb]{0.00,0.23,0.31}{(est_airq, }\textcolor[rgb]{0.40,0.45,0.13}{vcov =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.13,0.47,0.30}{"iid"}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{Day))}
\end{Highlighting}
The last graph (bottom right) reports the coefficients of an event study
plotted with iplot and a custom style displaying a ribbon:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{est_did }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{feols}\textcolor[rgb]{0.00,0.23,0.31}{(y }\textcolor[rgb]{0.37,0.37,0.37}{\textasciitilde}\textcolor[rgb]{0.00,0.23,0.31}{ x1 }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{i}\textcolor[rgb]{0.00,0.23,0.31}{(period, treat, }\textcolor[rgb]{0.68,0.00,0.00}{5}\textcolor[rgb]{0.00,0.23,0.31}{) }\textcolor[rgb]{0.37,0.37,0.37}{|}\textcolor[rgb]{0.00,0.23,0.31}{ id }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ period, base_did)}
\textcolor[rgb]{0.28,0.35,0.67}{iplot}\textcolor[rgb]{0.00,0.23,0.31}{(est_did, }\textcolor[rgb]{0.40,0.45,0.13}{ci.fill =} \textcolor[rgb]{0.56,0.35,0.01}{TRUE}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{ci.fill.par =} \textcolor[rgb]{0.28,0.35,0.67}{list}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{col =} \textcolor[rgb]{0.13,0.47,0.30}{"lightblue"}\textcolor[rgb]{0.00,0.23,0.31}{), }
\textcolor[rgb]{0.40,0.45,0.13}{ci.lwd =} \textcolor[rgb]{0.68,0.00,0.00}{0}\textcolor[rgb]{0.00,0.23,0.31}{, }\textcolor[rgb]{0.40,0.45,0.13}{pt.join =} \textcolor[rgb]{0.56,0.35,0.01}{TRUE}\textcolor[rgb]{0.00,0.23,0.31}{)}
\end{Highlighting}
figure[figure omitted — 268 chars of source]
Even though coefplot/iplot accommodate many options to
customize the graphs, it may not be enough to create very precise
publication graphs. In that case, the user can access the dataset used
to build the graph (without plotting) by invoking the
only.params\ =\ TRUE argument. Similarly, these fixest
plotting functions yield base R graphics, which may or may not be to the
user's taste. The \texttt{ggfixest} package ggfixest offers
equivalent functionality in the form of dedicated \textbf{\texttt{ggplot2}}
wickham2016Ggplot2ElegantGraphics methods for \textbf{\texttt{fixest}}
objects, as well as a set of ancillary plotting features.
Benchmarks
In this section, we present a set of benchmarks that demonstrate
fixest's performance against alternative state of the art
software, across a broad range of settings. For the sake of parsimony
and reproducibility, we restrict our comparisons to free and open-source
software (FOSS) which specialize in fixed-effects
estimations.\footnote{The alternatives that we do not benchmark here,
including base R routines such as (g)lm and
proprietary software such as Stata, are generally slower
than our comparison FOSS group for this set of tasks.} Specifically,
we benchmark \texttt{fixest} against \textbf{\texttt{alpaca}}
stammann2018FastFeasibleEstimation and \textbf{\texttt{lfe}}
gaure2013lfe in \textsc{R}, \textbf{\texttt{FixedEffectModels}}
gomez2024FixedEffectModelsJlFast and \textbf{\texttt{GLFixedEffectModels}}
boehm2025GLFixedEffectModelsJl in \textsc{Julia}, and
\textbf{\texttt{PyFixest}} fischer2024Pyfixest in
\textsc{Python}.\footnote{For the sake of clarification, please note
that \textbf{\texttt{PyFixest}} is developed by independent authors. As the name
suggests, it is a port of \textbf{\texttt{fixest}}'s syntax and features into
\textsc{Python}, although some of the core internal routines are
different; as we documented in Section (ref). The
\textbf{\texttt{PyFixest}} team recently began work to replace their existing
alternating projections routine with \textbf{\texttt{fixest}}'s fixed-point
algorithm. This means that the performance of these two libraries
should become closer in the near future, as they will both rely on the
same method and implementation.} We use the latest available versions
of these software, as of 15 January 2026. The benchmarks presented here
are run on a Windows PC with 128GB of RAM and an
Intel\textsuperscript{\tiny\textregistered} Core\textsuperscript{TM}
i7-10850H CPU @ 2.70GHz processor. Separate benchmarks run on an Apple
Mac M4 Pro laptop with 48 GB of RAM yield outcomes that are
proportionally very similar.
Before continuing, we issue the standard disclaimer that benchmarks
should always be interpreted with a degree of caution, given the
inherent sensitivity to arbitrary data and estimation choices.
Nonetheless, we believe that we provide a fair and representative
overview, which seeks to minimize bias by running multiple benchmarks
over many use-cases, using both simulated and real-life data. In
particular, we present two broad sets of benchmarks. The first uses
simulated datasets with different convergence properties, providing
insight into how the performance of our different software scales with
the number of observations. The second evaluates performance on a
demanding real-life dataset, where we vary the type of model estimation
and focus on specific advantages provided by fixest's feature set.
All benchmarking code is provided with the replication packet for this
paper.
Simulated data
Our simulated dataset draws inspiration from labor economics by
generating a panel of employees and firms over time. Our specific
regression task is estimating wages as a function of covariates (like
training), whilst controlling for individual, firm and year
fixed-effects. Importantly, we consider two data generating processes
(DGPs) for our simulated data: one for which the convergence of the
estimated fixed-effects is “simple” and another for which it is
“difficult”. The next two paragraphs provide intuition for why
convergence can be simple (fast) or difficult (slow). Readers
uninterested in the intuition can safely skip these paragraphs and jump
straight to the results in Figures (ref) and
(ref).
Consider a balanced panel of employees. The assignation of firms to
employees is the key determinant of convergence speed, which in turn
elevates the importance of algorithmic routines for estimating the
individual and firm fixed-effects. In the “simple” case, we assign
firms randomly to employees, resulting in each firm being connected to
many different employees. From a network perspective, this creates a
dense network where firms share common employees, which in turn means
that any changes to a single firm coefficient will propagate quickly
through the system, enabling fast convergence. In contrast, for the
“difficult” case, we assign firms sequentially to employees, resulting
in firms being connected to very few employees. Now our network is
extremely sparse with few connections between firms, which means that
changes to firm coefficients are slow to propagate, yielding slow
convergence.
Our DGP is as follows. For \(N\) observations, we generate a panel of
\(N_I = N/N_T\) individuals over \(N_T = 10\) years. These employees
work across \(N_F = N_I / 23\) companies. In the simple case, we assign
companies randomly to employees. In the difficult case, we assign them
sequentially. All the fixed-effect coefficients are generated with a
standard normal distribution. There are two covariates \(x_1\),
generated from a standard normal distribution, and \(x_2 = x_1^2\).
Finally the outcome, \(y\) is obtained as follows: \[
y_{ift} = x_{1, ift} + 0.05 x_{2, ift} + \mu_i + \lambda_t + \nu_f + \varepsilon_{ift},
\]
where \(\varepsilon_{ift}\) is drawn from a standard normal
distribution. We report below the code to generate the data:
Shaded\begin{Highlighting}[]
\textcolor[rgb]{0.00,0.23,0.31}{base_dgp }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.00,0.23,0.31}{function}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.40,0.45,0.13}{n =} \textcolor[rgb]{0.68,0.00,0.00}{1000}\textcolor[rgb]{0.00,0.23,0.31}{) \
\textcolor[rgb]{0.00,0.23,0.31}{ nb_year }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.68,0.00,0.00}{10}
\textcolor[rgb]{0.00,0.23,0.31}{ nb_indiv_per_firm }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.68,0.00,0.00}{23}
\textcolor[rgb]{0.00,0.23,0.31}{ nb_indiv }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{round}\textcolor[rgb]{0.00,0.23,0.31}{(n }\textcolor[rgb]{0.37,0.37,0.37}{/}\textcolor[rgb]{0.00,0.23,0.31}{ nb_year)}
\textcolor[rgb]{0.00,0.23,0.31}{ nb_firm }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{round}\textcolor[rgb]{0.00,0.23,0.31}{(nb_indiv }\textcolor[rgb]{0.37,0.37,0.37}{/}\textcolor[rgb]{0.00,0.23,0.31}{ nb_indiv_per_firm)}
\textcolor[rgb]{0.00,0.23,0.31}{ indiv_id }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rep}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.00,0.23,0.31}{nb_indiv, }\textcolor[rgb]{0.40,0.45,0.13}{each =}\textcolor[rgb]{0.00,0.23,0.31}{ nb_year)}
\textcolor[rgb]{0.00,0.23,0.31}{ year }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rep}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.00,0.23,0.31}{nb_year, }\textcolor[rgb]{0.40,0.45,0.13}{times =}\textcolor[rgb]{0.00,0.23,0.31}{ nb_indiv)}
\textcolor[rgb]{0.00,0.23,0.31}{ firm_id_simple }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{sample}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.00,0.23,0.31}{nb_firm, n, }\textcolor[rgb]{0.40,0.45,0.13}{replace =} \textcolor[rgb]{0.56,0.35,0.01}{TRUE}\textcolor[rgb]{0.00,0.23,0.31}{)}
\textcolor[rgb]{0.00,0.23,0.31}{ firm_id_difficult }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rep}\textcolor[rgb]{0.00,0.23,0.31}{(}\textcolor[rgb]{0.68,0.00,0.00}{1}\textcolor[rgb]{0.37,0.37,0.37}{:}\textcolor[rgb]{0.00,0.23,0.31}{nb_firm, }\textcolor[rgb]{0.40,0.45,0.13}{length.out =}\textcolor[rgb]{0.00,0.23,0.31}{ n)}
\textcolor[rgb]{0.00,0.23,0.31}{ unit_fe }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rnorm}\textcolor[rgb]{0.00,0.23,0.31}{(nb_indiv)[indiv_id]}
\textcolor[rgb]{0.00,0.23,0.31}{ year_fe }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rnorm}\textcolor[rgb]{0.00,0.23,0.31}{(nb_year)[year]}
\textcolor[rgb]{0.00,0.23,0.31}{ firm_fe }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rnorm}\textcolor[rgb]{0.00,0.23,0.31}{(nb_firm)[firm_id_simple]}
\textcolor[rgb]{0.00,0.23,0.31}{ x1 }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{rnorm}\textcolor[rgb]{0.00,0.23,0.31}{(n)}
\textcolor[rgb]{0.00,0.23,0.31}{ y }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.68,0.00,0.00}{1} \textcolor[rgb]{0.37,0.37,0.37}{*}\textcolor[rgb]{0.00,0.23,0.31}{ x1 }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.68,0.00,0.00}{0.05} \textcolor[rgb]{0.37,0.37,0.37}{*}\textcolor[rgb]{0.00,0.23,0.31}{ x1}\textcolor[rgb]{0.37,0.37,0.37}{\^}\textcolor[rgb]{0.68,0.00,0.00}{2} \textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ firm_fe }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ unit_fe }\textcolor[rgb]{0.37,0.37,0.37}{+}\textcolor[rgb]{0.00,0.23,0.31}{ year_fe }\textcolor[rgb]{0.37,0.37,0.37}{+} \textcolor[rgb]{0.28,0.35,0.67}{rnorm}\textcolor[rgb]{0.00,0.23,0.31}{(n)}
\textcolor[rgb]{0.00,0.23,0.31}{ df }\textcolor[rgb]{0.00,0.23,0.31}{=} \textcolor[rgb]{0.28,0.35,0.67}{data.frame}\textcolor[rgb]{0.00,0.23,0.31}{(}
\textcolor[rgb]{0.40,0.45,0.13}{indiv_id =}\textcolor[rgb]{0.00,0.23,0.31}{ indiv_id, }
\textcolor[rgb]{0.40,0.45,0.13}{year =}\textcolor[rgb]{0.00,0.23,0.31}{ year,}
\textcolor[rgb]{0.40,0.45,0.13}{firm_id =}\textcolor[rgb]{0.00,0.23,0.31}{ firm_id_simple,}
\textcolor[rgb]{0.40,0.45,0.13}{firm_id_difficult =}\textcolor[rgb]{0.00,0.23,0.31}{ firm_id_difficult, }
\textcolor[rgb]{0.40,0.45,0.13}{x1 =}\textcolor[rgb]{0.00,0.23,0.31}{ x1,}
\textcolor[rgb]{0.40,0.45,0.13}{x2 =}\textcolor[rgb]{0.00,0.23,0.31}{ x1}\textcolor[rgb]{0.37,0.37,0.37}{\^}\textcolor[rgb]{0.68,0.00,0.00}{2}\textcolor[rgb]{0.00,0.23,0.31}{,}
\textcolor[rgb]{0.40,0.45,0.13}{y =}\textcolor[rgb]{0.00,0.23,0.31}{ y}
\textcolor[rgb]{0.00,0.23,0.31}{ )}
\textcolor[rgb]{0.28,0.35,0.67}{return}\textcolor[rgb]{0.00,0.23,0.31}{(df)}
\textcolor[rgb]{0.00,0.23,0.31}{\}}
\end{Highlighting}
Our simulated benchmarks evaluate both the OLS and Poisson GLM
estimators. Specifically, the benchmarks estimate a set of models where:
i) the dependent variable is \(y\) for OLS and \(exp(y)\) for Poisson,
ii) the explanatory variables are \(x_1\) and \(x_2\), iii) there are
two or three sets of fixed-effects, and iv) the convergence of the
fixed-effects can be “simple” or “difficult”. Two fixed-effects
correspond to individual and firms, while three fixed-effects adds the
year. Finally we also vary the number of observations from 10,000 to
10M.
figure[figure omitted — 242 chars of source]
The results of the OLS benchmark are represented in Figure
(ref). It represents the average computing time across
three replications, in logarithmic scale. The top row represents the
results for the “simple” case. There we see that
fixest::feols is consistently faster than the alternative
software, with a computing time of about 1s for 10M observations and two
fixed-effects, while on the other end of the scale lfe::felm
takes an order of magnitude longer at 10s.
An interesting observation about this “simple” case is that there are
effectively no scaling effects; the relative differences between
software do not meaningfully change with the number of observations.
However, this is not true for our second, “difficult” set of results,
which we report in the bottom row of Figure (ref). Both
fixest and Julia's FixedEffectModels continue to
scale almost linearly as the dataset grows. Somewhat remarkably, these
two implementations are effectively identical for our most difficult
case of three-fixed effects and 10M observations at just over 60s. In
contrast, the convergence times of \texttt{lfe} and \textbf{\texttt{PyFixest}} explode
in this more difficult setting. We do not report benchmark times for
these two libraries above 100,000 observations, since neither could fit
the model within our constraints.\footnote{Either erroring due to
absence of convergence, or exceeding our 1hr timeout.}
Our Poisson benchmarks are reported in Figure (ref)
and follow much the same pattern as the OLS results. Again, we observe
that fixest times are consistently faster across the board, with
good scaling properties for both the “simple” (top) and “difficult”
(bottom) DGP cases. GLFixedEffectModels is nearest in performance
and also scales well across our different settings. In contrast,
alpaca and \textbf{\texttt{PyFixest}} struggle in the difficult convergence
case, and were unable to complete within our 1hr timeout constraint for
1M or more observations.
figure[figure omitted — 258 chars of source]
NYC taxi data
Having evaluated the impact that different convergence properties can
have on simulated data, we now illustrate fixest's performance on
a demanding real-life example. Specifically, we use the well-known NYC
Taxi and Limousine Commission dataset.\footnote{See:
\url{https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page}.}
Our sample covers three months of taxi trip data from Jan--Mar 2012 and
is rather large at over 42M observations.
The results, which are the average timings across three runs, are
reported in Table (ref), where we run four basic
specifications that are intended to highlight fixest-specific
features:
enumerate{0pt}{0pt}
• Baseline. Standard OLS with two variables and three
fixed-effects
• Varying slopes. Same as \#1 but adding varying slopes to one
explanatory variable
• Multiple outcomes. Same as \#1 but with two outcome variables
• Multiple VCOVs. Same as \#1 but with with both standard and
clustered VCOVs
table[table omitted — 1,247 chars of source]
Starting with the baseline estimation in the top row of Table
(ref), fixest is fastest at only 9.9s, followed by
FixedEffectModels at 18s and then PyFixest at 23s.
Adding varying slopes to one explanatory variable reduces the estimation
time for fixest and FixedEffectModels to 8.9s and 15s
respectively, while this feature is unavailable in PyFixest.
Estimating two outcomes simultaneously adds less than 4s in
fixest, thanks to its highly efficient handling of multiple
estimations. Since this feature in unavailable in
FixedEffectModels, the estimation time is effectively doubled to
35s. PyFixest also handles multiple estimations, although their
computation time increases by 80% to 43s.
Finally, as we have emphasized, fixest's design separates
estimation from inference. This means that estimating two models with
different VCOVs (standard-errors) incurs virtually no overhead.
Conversely, for FixedEffectModels the time is doubled.
PyFixest also separates inference from estimation, albeit not as
efficiently, thus its timing increases by 60%.
Conclusion and discussion
The fixest package represents a mature econometric toolkit that we
hope serves diverse user communities effectively. While this article has
focused on the core user-facing features, we also wish to highlight two
additional user groups: package developers and educators.
For package developers, fixest provides a robust foundation with
numerous convenience features. Many econometric methods are implemented
via a regression model and, by relying on fixest for estimation,
package developers automatically gain access to all of these features.
This allows developers to focus on methodological innovations, rather
than worrying about speed or re-implementing standard features like
fixed-effects handling, robust standard errors, plotting, tabling, and
basic method support. Moreover, the fixest ecosystem extends
seamlessly through integrations with \textbf{\texttt{broom}}
robinson2025BroomConvertStatistical, \textbf{\texttt{parameters}}
parameters, \textbf{\texttt{marginaleffects}}
arel-bundock2024marginaleffects, \textbf{\texttt{gtsummary}}
gtsummary, and \textbf{\texttt{modelsummary}}
arel-bundock2022modelsummary, providing even more functionality
that package developers can leverage.
Second, we believe that fixest can prove useful for educators. The
range of fixest features are intended to cover large swathes of
the applied econometrics toolkit. More to the point, incorporating all
of these features into a single package with a unified syntax allows
instructors to focus on methodology, rather than software proliferation.
As a leading example, consider The Effect textbook
huntington2021effect, which aims to teach causal inference at
the undergraduate level. Code covering basic regression models, robust
inference, instrumental variables, fixed-effects, DiD, and regression
discontinuity all are written using the single function feols.
We would like to conclude with a few words on fixest's development
philosophy. This software is built on three cornerstones: speed,
robustness, and stability.
On speed. All data intensive operations in fixest have been
implemented with as many optimizations as possible, involving
methodological innovations when needed. This is highlighted in the
benchmarks for the core estimating functions, but the same philosophy
extends to many other functions.
On robustness. Error-handling is central to fixest since it is
critical to a good user experience. We have tried our best to anticipate
any potential problems from the user side, even unlikely ones, and the
fixest codebase contains hundreds, possibly thousands, of custom
error messages and exceptions. For the remaining bugs that are
inevitably left over, we could count on the numerous feedback from a
fantastic community that has helped iron them out. fixest is by
now a mature package, forged by battle-hardened testing over several
years and across an array of contexts.
On Stability. As core developers and users of fixest, we value
stability very highly. Any design choices made in consideration of the
codebase and user-facing API are done with that desideratum in mind. We
would make three observations in support of this claim. First,
fixest has no hard dependencies that are at risk themselves. The
core codebase relies only on three non-base R packages,
namely \texttt{Rcpp} rcpp2024software, \textbf{\texttt{stringmagic}}
berge2025stringmagic and \textbf{\texttt{dreamerr}}
berge2025dreamerr. The former already acts as the solid
foundation of thousands of \textsc{R} packages, while the latter two
are spinoffs from \textbf{\texttt{fixest}} itself. Second, only a handful of
breaking changes to non-core \textbf{\texttt{fixest}} functions have been introduced
over its seven years of existence. This, despite the introduction of
many new features and considerable growth of the codebase. Third, we
strive to avoid making rushed design decisions. Rather, we introspect
carefully and then seek feedback to ensure that any new features are as
stable and future proof as they can.
By continuing to build on these foundations, we hope that fixest
will prove to be a valuable and reliable companion for empirical work in
the years to come.
Acknowledgments
Laurent Bergé wishes to thank Grant McDermott whose early publicity and
genuine enthusiasm led to unforeseen levels of development, Dirk
Eddelbuettel for creating and maintaining Rcpp (without this key
technology fixest would have never existed), the community which
made this software infinitely more robust thanks to bug reports, and the
R authors and the CRAN team for making such an unbelievably
reliable, and fun, programming language.