Skip to content

Reinstate the fixest fixed effects, and settle what a bar in the formula means #8

Description

@jhollway

Parked on feature/fixest-fixed-effects while the architecture settles. Reinstate with git revert 96fed9e.

Why it was parked

A bar in the formula meant two different things:

  • getRHSNames() read y ~ a + b | c as an {lme4} random slope and rewrote it as (b | c).
  • parse_qap_formula() read a bar without parentheses as a {fixest} fixed effect.

Because the front end always adds the parentheses, fixed effects through the formula were unreachable, and {fixest} was only ever entered through the fixest_se_cluster control. The path itself worked and was tested; the interface around it did not.

With {fixest} parked, a bar means an {lme4} random-effect term and nothing else, and parse_qap_formula() drops from three branches to one.

What it needs before coming back

  • Decide the formula grammar first. See the discussion below on what other packages do.
  • Reinstate the fixest::feglm() branch against whatever grammar is chosen.
  • Keep the fix already on the branch: feglm() reports an intercept where no fixed effect is absorbed, so the placeholder must only be added when one is.

The grammar question

Three conventions exist, and they conflict:

Convention Used by Fixed effects Random effects
Bar means random lme4, glmmTMB, brms not supported y ~ x + (1 | g)
Bar means fixed fixest, felm (lfe), plm y ~ x | g not supported
Both, by position fixest with {lme4}-style terms after the first bar in parentheses

{fixest} itself takes y ~ x | fe1 + fe2 | iv — bars separate parts of the formula, and the parts are positional. {lme4} takes bars only inside parentheses. The two can coexist without ambiguity, since a bar inside parentheses is always random and a bare bar is always a part separator, which is roughly what the engine already assumed. What broke it was the front end normalising every bar into parentheses before the parser saw it.

Options:

  1. Follow {lme4} only, and put fixed effects in a control. y ~ x + (1 | sv) for random, fixed = "sv" for fixed. Unambiguous, verbose, and the two kinds of effect stop looking alike.
  2. Follow {fixest}'s positional parts. y ~ x | sv for fixed, y ~ x + (1 | sv) for random. Matches the two most likely reference packages, and needs getRHSNames() to stop rewriting bare bars.
  3. Neither: name the effect. y ~ x + fixed(sv) and y ~ x + random(sv), alongside the existing ego(), alter(), same() terms. Fits the house convention that a formula term is a function call naming what it constructs, and reads without knowing either package's habits.

Option 3 is the one that fits this package's own grammar, but it is the least familiar. Worth deciding before the branch comes back.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions