Repository navigation
Tukey and Sidak p-values that match emmeans in estimate_contrasts() and estimate_slopes() - #693
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
2 open issues
3 files not reviewed
man/estimate_contrasts.Rd: Generated fileman/estimate_slopes.Rd: Generated fileman/get_emmeans.Rd: Generated file
What changed in this PR
Aligns "tukey" and "sidak" p-value adjustments in estimate_contrasts() / estimate_slopes() with emmeans, including fixing the emmeans backend in estimate_slopes() to actually apply p_adjust.
Changes:
- Updates
.p_adjust()to apply Tukey only to pairwise comparisons and otherwise fall back to Sidak (matching emmeans), with correct family sizing logic. - Applies
p_adjustfor the emmeans backend ofestimate_slopes()(forstats::p.adjust()methods + Tukey/Sidak). - Adds extensive regression tests and updates documentation + NEWS, with a version bump.
| File | Description |
|---|---|
| tests/testthat/test-p_adjust.R | Adds regression tests comparing adjusted p-values against emmeans across multiple scenarios/backends. |
| R/p_adjust.R | Refines Tukey/Sidak rules, corrects family sizing, adds helpers for pairwise/group-term detection, and improves Sidak numeric stability. |
| R/estimate_slopes.R | Ensures backend="emmeans" respects p_adjust (incl. Tukey→Sidak behavior). |
| R/estimate_contrasts.R | Documents Tukey→Sidak replacement rules and Sidak family sizing behavior. |
| R/estimate_contrast_methods.R | Restores by attribute before adjustment so Sidak can compute families correctly. |
| NEWS.md | Documents behavior changes and rationale. |
| DESCRIPTION | Bumps package version. |
Review effort: Lite
Copilot stopped reviewing on behalf of
jmgirard due to an error
October 18, 2026 18:00
…and count the comparisons in each by group for Sidak
…lopes tested against a value or not compared pairwise
… a group term, and narrow the p_adjust docs
… of predictions, accept any case of method names with the emmeans backend, and skip the Sidak message when nothing is adjusted
… with ::: in tests
jmgirard
force-pushed
the
p-adjust-families
branch
from
October 18, 2026 18:14
fb028e8 to
d204fb2
Compare
strengejacke
approved these changes
Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


With
p_adjust = "tukey"or"sidak",estimate_contrasts()andestimate_slopes()gave p-values that differ from emmeans in several cases, and the emmeans backend ofestimate_slopes()ignoredp_adjust. The Tukey adjustment was applied to comparisons that are not pairwise, Tukey and Sidak counted the wrong number of groups or tests, and slopes tested against a value other than 0 kept Tukey.Examples.
m1 <- lm(y ~ g * h)andm2 <- lm(y ~ x * g)with a 4-levelgand a 2-levelh(simulated data, as intest-p_adjust.R).m5 <- lm(mpg ~ gear)andm4 <- lm(mpg ~ wt * cyl)on mtcars with factoredgearandcyl. First p-value of each call:estimate_contrasts(m5, "gear", comparison = "reference", p_adjust = "tukey")estimate_contrasts(m1, "g", p_adjust = "sidak")estimate_slopes(m2, "x", by = "g", comparison = ~pairwise, p_adjust = "tukey")estimate_slopes(m2, "x", by = "g", hypothesis = 1, p_adjust = "tukey")estimate_slopes(m4, "wt", by = "cyl", backend = "emmeans", p_adjust = "holm")What changed:
.p_adjust()keeps Tukey only for pairwise comparisons: acomparison(contrasts), orcomparison,hypothesisorpost_process(slopes), that is a formula withpairwiseorrevpairwiseon the right side, with or without a| groupterm. Other comparisons, and slopes tested against a value, get Sidak with a message, as emmeans does. No message is printed when each family has one test, because Sidak then changes nothing.byvariables that are output columns, or of the| groupterm. Sidak counts these rows. For pairwise comparisons, Tukey takes the number of levels or slopeskfrom thek * (k - 1) / 2rows of each family. Missingbyvalues form their own family. Sidak is computed as-expm1(n * log1p(-p)), which keeps small p-values precise.hypothesisargument (for examplehypo = ~pairwise), whichavg_slopes()accepts, is read ashypothesis. Whenpost_processis given, it decides the rule, because it defines the output rows.estimate_contrasts()onestimate_relation()output sets thebyattribute again before the adjustment, because formatting drops it.estimate_slopes(backend = "emmeans")setsadjuston the emtrends object for thestats::p.adjust()methods,"tukey"and"sidak"(any case), andparametersthen adjusts the p-values."tukey"becomes"sidak"with a message, as inemmeans::test(). Bayesian and ordinal models are not changed. Other methods are still ignored there.p_adjustdocs ofestimate_contrasts()andestimate_slopes(), and NEWS, describe the rules. The sentence that multiple comparisons only occur for Johnson-Neyman intervals is gone.Tests in the new
test-p_adjust.R(noskip_on_os()) compare the p-values with emmeans at a tolerance of 1e-5: Tukey and Sidak for pairwise contrasts with and withoutby, the reference, sequential and meandev comparisons, compared slopes withestimate_slopes()andestimate_contrasts(), grouped slope comparisons, slopes tested against 1,hypo, contrasts ofestimate_relation()output, and the emmeans slope backend. They also check the messages, thep_adjustattribute and missingbyvalues.Not changed here, the same as on main:
stats::p.adjust()methods (holm, bonferroni and others) adjust over all rows with the marginaleffects backend, where emmeans adjusts within eachbygroup.post_processfunction drops thebycolumns, so Sidak then counts all rows as one family.post_process = ~pairwiseon slopes gives unadjusted p-values from z, wherecomparison = ~pairwiseuses t.devtools::test()gives 0 failures locally.devtools::check()gives 0 errors and 0 warnings. The version is bumped to 0.17.0.21.