---
title: "Modern Estimators: fixest and alpaca"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Modern Estimators: fixest and alpaca}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment  = "",
  message  = FALSE,
  warning  = FALSE
)
library(stargazer2)
```

`stargazer2` supports `fixest` and `alpaca` model objects natively, with two
features that matter for modern applied work:

1. **Fixed effects are reported as indicator rows** ("Yes / No") at the bottom
   of the table rather than as coefficient rows. This follows the convention in
   the trade and IO literatures where high-dimensional FEs are controls, not
   objects of interest.

2. **Standard error types are auto-detected** from the model object and
   reported in the table note. When different columns use different SE types,
   the note breaks them out by column group automatically.

## Standard error interface

`stargazer2` accepts SEs through three mechanisms, applied in order of
precedence:

| Priority | Mechanism | When to use |
|---|---|---|
| 1 (highest) | `vcov = list(V1, V2, ...)` | Any vcov matrix — most flexible |
| 2 | `se = list(se1, se2, ...)` | Numeric SE vectors (drop-in for original stargazer scripts) |
| 3 (lowest) | Auto-extraction | fixest and alpaca models — SE type read from the model object |

Passing `NULL` for a specific entry in the `vcov` list tells `stargazer2` to
fall back to auto-extraction for that column. This is useful when mixing `lm`
models (with externally supplied vcov matrices) and `fixest` models (which
already carry their SE type).

## Fixed effects with fixest

```{r wage1-setup, eval = requireNamespace("wooldridge", quietly = TRUE)}
library(wooldridge)
data(wage1)

wage1$region <- factor(
  ifelse(wage1$northcen == 1, "northcen",
  ifelse(wage1$south    == 1, "south",
  ifelse(wage1$west     == 1, "west", "northeast"))),
  levels = c("northeast", "northcen", "south", "west")
)

wage1$occupation <- factor(
  ifelse(wage1$profocc == 1, "professional",
  ifelse(wage1$clerocc == 1, "clerical",
  ifelse(wage1$servocc == 1, "service", "other"))),
  levels = c("other", "professional", "clerical", "service")
)

wage1$industry <- factor(
  ifelse(wage1$construc == 1, "construction",
  ifelse(wage1$ndurman  == 1, "nondurable_manuf",
  ifelse(wage1$trcommpu == 1, "transport",
  ifelse(wage1$trade    == 1, "trade",
  ifelse(wage1$services == 1, "services",
  ifelse(wage1$profserv == 1, "prof_services", "other")))))),
  levels = c("other", "construction", "nondurable_manuf",
             "transport", "trade", "services", "prof_services")
)
```

We estimate five log-wage regressions with `feols`, varying the set of fixed
effects and using two-way clustering on region × industry throughout. The
interacted FE specification `region^industry` absorbs a full set of
region-by-industry cells.

```{r fixest-models, eval = requireNamespace("wooldridge", quietly = TRUE) && requireNamespace("fixest", quietly = TRUE)}
library(fixest)

f1 <- feols(lwage ~ educ + exper + tenure + female + married |
              region,               wage1, vcov = ~region^industry)
f2 <- feols(lwage ~ educ + exper + tenure + female + married |
              occupation,           wage1, vcov = ~region^industry)
f3 <- feols(lwage ~ educ + exper + tenure + female + married |
              region + occupation,  wage1, vcov = ~region^industry)
f4 <- feols(lwage ~ educ + exper + tenure + female + married |
              region + occupation + industry,
                                    wage1, vcov = ~region^industry)
f5 <- feols(lwage ~ educ + exper + tenure + female + married |
              region^industry,      wage1, vcov = ~region^industry)
```

A bare call — no labels specified — already produces a complete table.
`stargazer2` extracts the dependent variable name (`lwage`) from the model
formula and, since all five columns share the same estimator, omits the
redundant model-type row:

```{r fixest-table-text, eval = requireNamespace("wooldridge", quietly = TRUE) && requireNamespace("fixest", quietly = TRUE)}
stargazer(f1, f2, f3, f4, f5, type = "text")
```

Labels can be overridden when needed. The LaTeX source below also renames the
covariates for presentation:

```{r fixest-table-latex, eval = requireNamespace("wooldridge", quietly = TRUE) && requireNamespace("fixest", quietly = TRUE)}
stargazer(f1, f2, f3, f4, f5,
          type             = "latex",
          title            = "Log Wages: Varying Fixed Effects",
          label            = "tab:fe-wages",
          dep.var.labels   = "log(Wage)",
          covariate.labels = c("Education", "Experience", "Tenure",
                               "Female", "Married"))
```

Two things to notice in both outputs:

- **FE indicator rows** appear below the coefficient block — one row per unique
  fixed effect across all columns, with "Yes" or "No" per model. The interacted
  specification `region^industry` is rendered as a single "Region x Industry FE"
  row.
- **SE type** is auto-detected from the fixest model and reported uniformly in
  the note, since all five models use the same clustering strategy.

## Multiple estimators and clustering: the gravity model

The `fixest` package's built-in `trade` dataset provides a natural setting for
showcasing different estimators. We estimate a standard gravity equation for
trade flows using OLS, Poisson pseudo-maximum likelihood (PPML), and negative
binomial — all with the same four-way fixed effects.

```{r gravity-models, eval = requireNamespace("fixest", quietly = TRUE)}
data(trade, package = "fixest")

gravity_ols    <- feols(log(Euros) ~ log(dist_km) |
                          Origin + Destination + Product + Year, trade)
gravity_pois   <- fepois(Euros ~ log(dist_km) |
                           Origin + Destination + Product + Year, trade)
gravity_negbin <- fenegbin(Euros ~ log(dist_km) |
                             Origin + Destination + Product + Year, trade)
```

Two further Poisson columns demonstrate the clustering-label machinery:
`~Origin^Destination` clusters by the interaction (one cluster per
origin-destination pair); `~Origin+Destination` is two-way clustering by
origin and by destination separately.

```{r gravity-clustered, eval = requireNamespace("fixest", quietly = TRUE)}
gravity_pois1  <- fepois(Euros ~ log(dist_km) |
                           Origin + Destination + Product + Year,
                         trade, vcov = ~Origin^Destination)
gravity_pois2  <- fepois(Euros ~ log(dist_km) |
                           Origin + Destination + Product + Year,
                         trade, vcov = ~Origin + Destination)
```

With no arguments beyond the model list, `stargazer2` automatically extracts
the dependent variable names from each formula (`log(Euros)` for the OLS model,
`Euros` for the count models), detects the estimator type for each column (OLS,
Poisson, Neg. Binomial), and reads the SE method from each model object:

```{r gravity-table-default, eval = requireNamespace("fixest", quietly = TRUE)}
stargazer(gravity_ols, gravity_pois, gravity_negbin,
          gravity_pois1, gravity_pois2,
          type = "text")
```

The same call in LaTeX produces submission-ready output:

```{r gravity-table, eval = requireNamespace("fixest", quietly = TRUE)}
stargazer(gravity_ols, gravity_pois, gravity_negbin,
          gravity_pois1, gravity_pois2,
          type  = "latex",
          title = "Gravity Equation for Trade Flows",
          label = "tab:gravity")
```

The SE note shows per-column types: OLS standard errors for column (1), MLE
standard errors for (2)–(3) (auto-detected from the fixest objects), standard
errors clustered by Origin-Destination for column (4), and two-way clustered
by Origin and Destination for column (5).

## Non-linear models with alpaca

The `alpaca` package offers an alternative implementation of fixed-effects GLMs
(logit, probit, Poisson). `stargazer2` supports `alpaca::feglm` objects through
a companion pair of vcov helpers:

- `alpaca_vcovSandwich()` — heteroskedasticity-robust (sandwich) SEs
- `alpaca_vcovCL()` — clustered SEs, with a formula interface matching
  `sandwich::vcovCL`

```{r alpaca-table, eval = requireNamespace("wooldridge", quietly = TRUE) && requireNamespace("alpaca", quietly = TRUE)}
library(alpaca)

# Logit model: P(married) as a function of wages and human capital,
# with occupation and industry fixed effects.
# industry must be in the FE specification for clustering by industry.
m_alp <- feglm(married ~ lwage + educ + exper | occupation + industry,
               wage1, binomial("logit"))

V_robust    <- alpaca_vcovSandwich(m_alp)
V_clustered <- alpaca_vcovCL(m_alp, cluster = ~industry)

stargazer(m_alp, m_alp,
          type             = "text",
          dep.var.labels   = "Married (0/1)",
          covariate.labels = c("log(Wage)", "Education", "Experience"),
          column.labels    = c("Sandwich-robust", "Industry-clustered"),
          vcov             = list(V_robust, V_clustered))
```

The SE note names the type for each column exactly as it does for fixest and
sandwich models, confirming that the reporting machinery is consistent across
all supported packages.
