Skip to content

Latent-scale effect sizes for ordinal models, and effect sizes on the rows of their own contrasts (emmeans backend) - #698

Open
jmgirard wants to merge 7 commits into
easystats:mainfrom
jmgirard:ordinal-latent-effect-sizes
Open

jmgirard wants to merge 7 commits into
easystats:mainfrom
jmgirard:ordinal-latent-effect-sizes

Conversation

@jmgirard

Copy link
Copy Markdown
Contributor

With backend = "emmeans", effectsize = "emmeans" now works for polr and clm models with predict = "latent" and a logit or probit link. Before, every effectsize option stopped with "We strongly recommend not using the effectsize argument with non-Gaussian models" for these models. The contrasts of the latent variable are divided by the SD of its error distribution. This SD is pi / sqrt(3) for the logit link (the standard logistic distribution, ?stats::Logistic). It is 1 for the probit link (?stats::Normal). Sigma is taken as known (edf = Inf), so the effect sizes and their limits are the contrasts and their limits divided by sigma.

This also fixes effect sizes that were on the wrong rows, for Gaussian models too.

Example, with m <- MASS::polr(Sat ~ Infl + Type, weights = Freq, data = MASS::housing, Hess = TRUE), d <- iris with d$Species <- factor(d$Species, levels = c("virginica", "setosa", "versicolor")), g <- lm(Sepal.Length ~ Species, data = d), and mt <- lm(mpg ~ cyl * am, data = mtcars) with cyl and am as factors. All calls use backend = "emmeans" and effectsize = "emmeans":

Call Before Now
estimate_contrasts(m, "Infl", predict = "latent") error: non-Gaussian models partial_d 0.682 for High - Low (1.237 / 1.814, equal to coef(m)["InflHigh"] / (pi / sqrt(3)))
the same with method = "probit" in polr() error: non-Gaussian models partial_d equal to Difference
estimate_contrasts(m, "Infl") (default predict) error: non-Gaussian models error: use predict = "latent"
estimate_contrasts(g, "Species") partial_d -1.267 on the versicolor - setosa row 1.807 (0.930 / sigma(g))
estimate_contrasts(mt, "cyl", by = "am") partial_d held the am codes (0, 1) -1.245 for cyl6 - cyl4 at am 0 (-3.775 / sigma(mt))
estimate_contrasts(m, "Infl", by = "Type", comparison = "joint", predict = "latent") error: no applicable method for @ error: effect sizes are not possible for joint or omnibus tests

What changed:

  • .estimate_contrasts_effectsize() skips the Gaussian check for polr and clm with effectsize = "emmeans". It requires predict = "latent" and a logit or probit link, and passes the matching sigma and edf = Inf to emmeans::eff_size(). clm models with a scale formula (the latent SD differs between observations) or a nominal formula (the thresholds differ between groups) give an error. A rescale argument for emmeans multiplies sigma by abs(rescale[2]), so the size of the effect sizes does not change.
  • .format_emmeans_contrasts() sorts the contrasts with merge(), so it now keeps the row of each contrast in the emmeans grid. The effect sizes are reordered by it. This fixes factor levels that are not in alphabetical order, and comparison = "poly" with 4 or more levels.
  • The eff_size() columns are selected by name, because a by variable adds a column.
  • The effect-size limits are computed at the level of ci. Before, they were always 95% limits. They are not adjusted for multiple comparisons, as before.
  • Joint and omnibus tests with any effectsize give a clear error. Before, the emmeans effect sizes gave an internal error and effectsize = "marginal" returned NA.
  • effectsize = "marginal" and "boot" still give the non-Gaussian error for ordinal models.
  • The effectsize docs and Details, NEWS entries, and the version bump.

Tests in test-ordinal.R check the logit and probit effect sizes against coef(m) divided by the latent SD, and clm against polr. They also cover by, ci, p_adjust, rescale, and every error. A new file, test-estimate_contrasts_effectsize_order.R, checks the row order for Gaussian models (reordered levels, by, poly with chickwts, and ci). It has no skip_on_os(), so it also runs on macOS.

About the design: in #227, the discussion agreed on no standardized effect sizes by default and noted that the choice of sigma is not settled. In #501, effect sizes were kept to families with a variance parameter. The default here stays NULL. The link fixes the latent SD of a logit or probit ordinal model. So this SD plays the role of the residual SD in the Gaussian case, with the same caveat about covariates. To keep effect sizes Gaussian-only, the ordinal part can be dropped from this PR.

Not fixed here, the same as on main:

  • Bayesian Gaussian models stop in emmeans::eff_size() with "non-conformable arguments", and so do models with non-estimable cells.
  • effectsize = "boot" with backend = "emmeans" can put effect sizes on the wrong rows, as above, and "boot" and "marginal" ignore ci.
  • effectsize = "none" still gives the non-Gaussian error for non-Gaussian models.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Omnibus tests are not fully covered by the effect-size guard and require correction.

No open issues 🎉

1 file not reviewed
  • man/estimate_contrasts.Rd: Generated file
What changed in this PR

Adds emmeans latent-scale effect sizes for ordinal models and fixes effect-size row alignment, confidence limits, and grouping behavior.

Changes:

  • Supports logit/probit effect sizes for polr and clm.
  • Corrects contrast-to-effect-size ordering and CI handling.
  • Adds tests, documentation, NEWS, and a version bump.
File Summary
tests/​testthat/​test-ordinal.R Tests ordinal effect sizes and validation errors.
tests/​testthat/​test-estimate_contrasts_effectsize_order.R Tests ordering and confidence limits.
R/​get_emcontrasts.R Preserves contrast-row mappings.
R/​estimate_contrasts.R Forwards CI and rescaling options.
R/​estimate_contrasts_effectsize.R Implements ordinal effect sizes and validation.
NEWS.md Documents the changes.
man/​estimate_contrasts.Rd Updates reference documentation.
DESCRIPTION Bumps the development version.

Review effort: Lite

@jmgirard

Copy link
Copy Markdown
Contributor Author

#698 (review)

  • Omnibus tests and the effect-size guard: not changed. With backend = "marginaleffects", comparison = "omnibus" with effectsize = "marginal", "emmeans" or "boot" stops with "Effect sizes are not possible for joint or omnibus tests." With backend = "emmeans", comparison = "omnibus" already stops inside emmeans ("Contrast function 'omnibus.emmc' not found"), with or without effectsize, so no omnibus result reaches the effect sizes there (checked with lm(mpg ~ cyl * am + wt), by = "am").

@bwiernik

Copy link
Copy Markdown
Contributor

This should work for marginaleffects backend too

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants