Skip to content

Add G-computation correction for emmeans, contrasts argument#567

Open
apanagos-lilly wants to merge 32 commits into
openpharma:mainfrom
apanagos-lilly:main
Open

Add G-computation correction for emmeans, contrasts argument#567
apanagos-lilly wants to merge 32 commits into
openpharma:mainfrom
apanagos-lilly:main

Conversation

@apanagos-lilly
Copy link
Copy Markdown

@apanagos-lilly apanagos-lilly commented May 21, 2026

Adds emmeans_gcomp_vars to mmrm_control(). When set, emmeans() returns the average treatment effect with standard errors that account for covariate variability across subjects. Default is NULL, when NULL, mmrm runs identically as before. Related to #560, though the counterfactual path does not support variance correction and has 50x runtime overhead only addressable through emmeans changes.

mmrm() and fit_mmrm() now accept a contrasts argument for specifying contrast matrices or functions, matching lm(). When a contrast matrix includes levels not present in the fitting data, those levels are preserved and marked as aliased, enabling prediction on new data

Copy link
Copy Markdown
Collaborator

@danielinteractive danielinteractive left a comment

Choose a reason for hiding this comment

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

Nice start, thanks @apanagos-lilly ! Please see comments below.

For next time, let's please try to have separate PRs for the separate enhancements as much as possible (here it would be 3 as far as I can see). That usually speeds up reviews, resolution and merges.

One additional high level comment:
I wonder if there is a nice way we can print to the user when they use emmeans on an mmrm with g-computation arguments that average treatment effect estimates are calculated. To basically highlight this as message, or hook into the show/print of emmeans results or similar, to make it very visible for the user.

Comment thread NEWS.md Outdated
Comment thread DESCRIPTION
person("Daniel D.", "Sjoberg", , "sjobergd@gene.com", role = "aut",
comment = c(ORCID = "0000-0003-0862-2018")),
person("Nikolas Ivan", "Krieger", , "nikolas.krieger@experis.com", role = "aut",
comment = c(ORCID = "0000-0002-4581-3545")),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please add yourself as author or contributor here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

added.

Comment thread tests/testthat/test-utils.R Outdated
visit_var = visit_var,
subject_var = subject_var,
subject_groups = subject_groups,
cov_type = "us"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should we still check that additional arguments are ignored?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reverted with emp_start removal

Comment thread tests/testthat/test-utils.R Outdated
subject_groups = fit$tmb_data$subject_groups,
cov_type = fit$formula_parts$cov_type
)
expect_length(result, 2L)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we please keep comparing to the numbers as before? I want to see that this stays stable

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reverted with emp_start removal

Comment thread tests/testthat/test-utils.R Outdated
)
})

# emp_start per covariance type ----
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this should be moved upwards such that all emp_start tests are together in one section of this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reverted with emp_start removal

Comment thread R/interop-emmeans.R
# Start with the model-based covariance.
V <- component(object, "beta_vcov")

if (!is.null(object$emmeans_gcomp_vars)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we move this into helper function and apply here? otherwise this thing gets too long

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, placed in h_emm_basis_gcomp()

Comment thread R/interop-emmeans.R Outdated
aliased_pos <- which(is.na(beta_hat))

# Apply G-computation correction if specified.
visit_var <- object$formula_parts$visit_var
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

will come back for review once algorithm vignette is there

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

vignette has been added, thanks

Comment thread R/tmb.R

contrasts_preserve_vars <- NULL
if (!is.null(contrasts)) {
formula_factors <- intersect(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ok we need some explanations here. either in comments or in the function documentation. please also refer to how lm() is doing this, is it the same or different and why

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added comments above here which explains how lm() is doing this and how that's insufficient and how contrasts fixes it

Comment thread R/utils.R Outdated
#' `emp_start` supports all non-spatial covariance types.
#' It uses linear regression to first obtain the coefficients
#' and uses the residuals to obtain the empirical
#' variance-covariance matrix. This is then decomposed into
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can you please include more algorithm details here? as this is the only place where this is documented

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reverted with emp_start removal

Comment thread tests/testthat/test-fit.R
# mmrm contrasts argument ----

test_that("mmrm accepts contrasts argument with contrast function", {
fit <- mmrm(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we also check that the result is as expected? I mean the coefficients for ARMCD

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

check added using the coefficient for ARMCD

@apanagos-lilly
Copy link
Copy Markdown
Author

@danielinteractive, thanks for all your great feedback. First, yes I agree, keeping these separate would have been better. We reverted out the pr which added the emp_start additions so now it is only g-computation and the contrast. The vignette for g-compute has been added and a message() too when it is called.

@danielinteractive
Copy link
Copy Markdown
Collaborator

Thanks @apanagos-lilly can you please check the open comments above, if they are no longer applicable please resolve them. Please let me know once all comments resolved and ready for my second review

@apanagos-lilly apanagos-lilly changed the title Add G-computation correction for emmeans, contrasts argument, and expanded emp_start Add G-computation correction for emmeans, contrasts argument Jun 2, 2026
@apanagos-lilly
Copy link
Copy Markdown
Author

apanagos-lilly commented Jun 2, 2026

One additional high level comment: I wonder if there is a nice way we can print to the user when they use emmeans on an mmrm with g-computation arguments that average treatment effect estimates are calculated. To basically highlight this as message, or hook into the show/print of emmeans results or similar, to make it very visible for the user.

Added a message() in emm_basis.mmrm()

@apanagos-lilly
Copy link
Copy Markdown
Author

@danielinteractive, I believe all comments have been tended to. Please let me know if I missed any. Thanks!

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