Skip to content

Filter the response categories of ordinal models named in by (emmeans backend) - #692

Merged
strengejacke merged 6 commits into
easystats:mainfrom
jmgirard:ordinal-response-filters
Oct 18, 2026
Merged

strengejacke merged 6 commits into
easystats:mainfrom
jmgirard:ordinal-response-filters

Conversation

@jmgirard

Copy link
Copy Markdown
Contributor

For ordinal models with backend = "emmeans", a by term can name the response with values, such as "Sat='Low'". This filter had no effect on means and contrasts, and it made slopes fail. emmeans ignores at for the response categories with mode = "prob", so the filter never reached the output.

Example, with m <- MASS::polr(Sat ~ Infl + Type + Cont, weights = Freq, data = MASS::housing). m2 is polr(Sat ~ Infl * x) on the same data with a numeric x:

Call Before Now
estimate_means(m, c("Infl", "Sat='Low'"), backend = "emmeans") 9 rows (all categories) 3 rows (Low)
estimate_contrasts(m, "Infl", by = "Sat='Low'", backend = "emmeans") 9 rows 3 rows
estimate_slopes(m2, "x", by = c("Infl", "Sat='Low'"), backend = "emmeans") error: No variable named Sat in the reference grid 3 rows
estimate_means(m, c("Infl", "Sat='Low'"), predict = "cum.prob", backend = "emmeans") error: No variable named Sat in the reference grid error: the response Sat can only be used in by with predict = "prob"
estimate_means(m, c("Infl", "Sat='Lowest'"), backend = "emmeans") 9 rows error: the response Sat has no category Lowest

What changed:

  • .process_emmeans_arguments() now calls the new .extract_ordinal_response_filter(). For frequentist ordinal models (.is_emmeans_ordinal()), it removes by terms on the response before emmeans runs. The emmeans call is then the same as without the filter. It keeps the chosen categories as character, so unquoted numbers such as rating=1 also work. Several terms on the response are combined. The values are checked against the categories of the model (y.levels or lev).
  • The getters store the categories in a response_filter attribute. .clean_pseudo_factor_emmeans_ordinal() keeps the rows of these categories before it renames the column to Response, so a predictor named Response does not change the result. p-values are adjusted within each response category, as before, so filtering does not change them.
  • .check_ordinal_response_filter() stops with an error for other predict modes. For slopes it checks the mode the user asked for, not the internal "cum.prob" mode.
  • The by docs and NEWS describe the filter.

Tests in test-ordinal.R cover polr, clm, clmm, and clmm with letter categories. They check one and two categories for means and contrasts, with p_adjust = "holm" for polr and clm, and slopes and contrasts of slopes for polr. The filtered output must equal the matching rows of the unfiltered output at a tolerance of 1e-10. The tests also cover the errors, several terms on the response, and the response named without values.

Not fixed here, the same as on main:

  • A list, formula or data-frame by that names the response is not filtered.
  • "Sat [Low]" is not recognized as a term on the response.
  • estimate_contrasts(m, "Sat='Low'", by = "Infl") ignores the value.
  • plot() of emmeans contrasts for ordinal models stops with "Please provide the estimated means data".

devtools::test() gives 0 failures locally. devtools::check() gives 0 errors and 0 warnings. The version is bumped to 0.17.0.20.

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.

🟢 Approval recommended

Only a minor test-coverage nit remains; no blocking issues were identified.

No open issues 🎉

4 files not reviewed
  • man/estimate_contrasts.Rd: Generated file
  • man/estimate_means.Rd: Generated file
  • man/estimate_slopes.Rd: Generated file
  • man/get_emmeans.Rd: Generated file
What changed in this PR

Adds response-category filtering for frequentist ordinal models using the emmeans backend.

Changes:

  • Supports filtering means, contrasts, and slopes by ordinal response categories.
  • Validates response categories and preserves category-specific adjustments.
  • Adds tests, documentation, NEWS updates, and a version bump.
File Summary
tests/​testthat/​test-ordinal.R Adds ordinal response-filter regression tests.
R/​get_emtrends.R Applies filters to ordinal slopes.
R/​get_emmeans.R Extracts, validates, and applies response filters.
R/​get_emcontrasts.R Propagates filters through ordinal contrasts.
R/​estimate_means.R Documents the filtering behavior.
NEWS.md Records the bug fix.
man/​get_emmeans.Rd Updates generated documentation.
man/​estimate_slopes.Rd Updates generated documentation.
man/​estimate_means.Rd Updates generated documentation.
man/​estimate_contrasts.Rd Updates generated documentation.
DESCRIPTION Bumps the development version.

Review effort: Lite

@strengejacke
strengejacke merged commit 92aa4d5 into easystats:main Oct 18, 2026
8 of 18 checks passed
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