Repository navigation
Feature request: logit_notation option for GLM equations #247
Description
Summary
For logistic regression models, extract_eq() currently renders the LHS as an
explicit log-odds fraction:
\log\left[ \frac{P(\hat{Y} = 1)}{1 - P(\hat{Y} = 1)} \right] = \alpha + \beta_1 X_1 + \cdotsIt would be useful to have an option to render this in the more compact and widely-used logit notation:
\operatorname{logit}\left[ P(\hat{Y}) \right] = \alpha + \beta_1 X_1 + \cdotsBoth forms are mathematically equivalent, but the logit form is standard in many textbooks and journals, and is considerably more compact — especially when equations are long or displayed in presentations.
Motivating context: nested logit models
This came up while adding an extract_eq() S3 method to the nestedLogit package (CRAN). A
nested logit model is a collection of binary logit sub-models, one per dichotomy. For a model with several dichotomies and several predictors, the
equations become long, and the log-fraction LHS adds visual noise on top of an already busy RHS.
See: the latex equations vignette illustrating the current implementation.
In this context:
- Each sub-model is a standard
glm(..., family = binomial)object. extract_eq()is called on each sub-model in turn; the results are assembled into a named list and printed separately in an R Markdown document.- Because there are multiple equations (one per dichotomy), compactness of the LHS matters more than in the single-equation case.
The nestedLogit method currently works around the limitation by post-processing the raw LaTeX string returned by extract_eq(), but it would be cleaner to handle this directly in equatiomatic.
Proposed interface
A new logical argument on extract_eq.glm() (and propagated through
extract_eq.default() for any binomial-family GLM):
extract_eq(model, logit_notation = FALSE, ...)When logit_notation = TRUE:
- Replace
\log\left[\frac{P(Y)}{1 - P(Y)}\right]with
\operatorname{logit}\left[P(Y)\right]on the LHS. - Drop the redundant
= 1fromP(Y = 1)when collapsing toP(Y).
Default FALSE preserves current behaviour.
Implementation sketch
The substitution could be applied as a post-processing step on the assembled
LaTeX string, using two passes:
# 1. Strip "= 1" from P(Y = 1) → P(Y)
eq_str <- gsub(
"(P\\(\\s*\\\\operatorname\\{[^}]+\\})\\s*=\\s*\\\\operatorname\\{1\\}",
"\\1", eq_str, perl = TRUE
)
# 2. Replace log-fraction with logit[...]
eq_str <- gsub(
"\\\\log\\\\left\\[\\s*\\\\frac\\s*\\{(P\\([^)]+\\))\\}\\{\\s*1\\s*-\\s*\\1\\s*\\}\\s*\\\\right\\]",
"\\\\operatorname{logit}\\\\left[\\1\\\\right]",
eq_str, perl = TRUE
)(Note: back-references in the pattern require perl = TRUE; R's default TRE
engine only supports back-references in replacement strings.)
Related
- The same simplification would apply to
probitand other binary-response GLMs, where the LHS could similarly be written as
\operatorname{probit}[P(Y)]rather than\Phi^{-1}[P(Y)], though that is a separate decision. - For
polr(ordered logit/probit), the threshold form of the LHS is different and would need separate treatment. - GLMs in general allow a wide variety of link functions
g(Y)so this idea could be more generally useful.
Activity
phgrosjean commentedon Mar 26, 2026
This seems feasible. I'll look at this...
friendly commentedon Mar 26, 2026
Thanks! (And also for making such a handy tool available)
phgrosjean commentedon Mar 27, 2026
@friendly Could you test branch
logic_notationwith the feature implemented ?friendly commentedon Mar 27, 2026
Thank you so much for implementing this feature request!! Here are my results from testing this
Testing the
logit-notationbranchInstalled via
remotes::install_github("datalorax/equatiomatic@logit_notation")andtested with the
nestedLogitpackage.What works
logit_notation = TRUEproduces the correct LHS on a single GLM sub-model:Output (as expected):
The argument also passes through correctly to the
nestedLogitS3 method, whichcalls
extract_eq()on each sub-model in turn — so the full nested model works too.Bug??: misleading message when
logit_notation = TRUEis actually appliedEvery call to
extract_eq(model, logit_notation = TRUE)emits:Maybe I just don't understand
show_distribution?But the output IS correct — the logit notation is applied. The message is
contradicted by the result. It appears that the warning fires unconditionally
(perhaps whenever
show_distributiontakes its defaultTRUEvalue) rather thanonly when the substitution is genuinely being skipped. This should either be
suppressed when the substitution succeeds, or the condition guarding it should be
inverted.
Downstream issue:
preview_eq()fails on already-rendered equation stringspreview_eq()works fine on a single sub-model:But piping the full
nestedLogitoutput topreview_eq()errors:The
nestedLogitmethod returns a named list of"equation"objects that arealready-rendered character strings (class
c("equation", "character")).preview_eq()then tries to callextract_lhs()on each one as if it were stilla model object, which fails. This is most likely a
nestedLogit-side issue —the package needs a
preview_eqmethod for its"nestedLogit_equations"listclass — rather than a bug in equatiomatic. Noting it here for completeness.
Summary
extract_eq(glm_submodel, logit_notation = TRUE)extract_eq(nestedLogit_model, logit_notation = TRUE)extract_eq(...) |> preview_eq()on single sub-modelextract_eq(nestedLogit_model, ...) |> preview_eq()phgrosjean commentedon Mar 30, 2026
@friendly, my bad. I misplaced to message relative to
show_distributionandlegit_notation. This should be solved in the current iteration.For your
preview_eq()problem, if you object is of classc("equation", "character"), it should not callextract_eq()at all. Inpreview_eq(), there is :if (!inherits(x, "equation")) x <- extract_eq(x). But as I understand it, you provide alist()ofc("equation", "character"), so, you would have to do something like(l|s)apply(x, preview_eq)probably. Could you give a reproducible example, so that I could further inspect this?phgrosjean commentedon Apr 4, 2026
I have made a change in
preview_eq()to allow previewing lists of equations directly.friendly commentedon Apr 4, 2026
Great! Thanks so much for responding to this.
equatiomaticis really handy.BTW: My
matlibpkg has some very general tools for constructing LaTeX for matrix expressions, https://friendly.github.io/matlib/articles/latex-equations.htmlphgrosjean commentedon Apr 13, 2026
This feature is now implemented in {equatiomatic} 0.4.8 that is now on CRAN (pull request #248).