Skip to content

Translating PROC GLIMMIX Models#586

Open
Franosei wants to merge 4 commits into
PSIAIMS:mainfrom
Franosei:r-sas-glimmix
Open

Translating PROC GLIMMIX Models#586
Franosei wants to merge 4 commits into
PSIAIMS:mainfrom
Franosei:r-sas-glimmix

Conversation

@Franosei

Copy link
Copy Markdown
Contributor

Summary

The article compares SAS and R implementations across common GLIMMIX use cases, including binary GLMMs, repeated-measures models, structured covariance models, count models, ordinal outcomes, and post-fit inference.

Main changes

  • Added a structured article: R vs SAS: Translating PROC GLIMMIX Models.
  • Included scenario-level comparisons between SAS PROC GLIMMIX and R packages.
  • Documented package recommendations for lme4, glmmTMB, nlme, mmrm, geepack, emmeans, and ordinal.
  • Added guidance on data preparation checks, equivalence criteria, direct versus approximate translation, and common sources of differences.
  • Included references and session information for reproducibility.

The article focuses on whether the same statistical model, estimand, covariance structure, and post-fit interpretation can be reproduced or closely approximated in R.

Some scenarios are direct translations, while others are described as approximate or sensitivity-based where the R implementation does not exactly match the SAS PROC GLIMMIX specification.

@yannickvandendijck

Copy link
Copy Markdown
Collaborator

Thank you for this nice work @Franosei ! This is so very useful for our users.
Apologies for the delay.
I will look into how we can nicely combine this with the current information we have around GLMM: https://psiaims.github.io/CAMIS/Comp/r-sas_glmm.html

Comment thread Comp/r-sas_glimmix.qmd

This scenario translates a SAS `PROC GLIMMIX` binary random-intercept and random-slope model into R. The outcome is asthma control, coded as `AVAL`, with repeated observations clustered within `USUBJID`.

The model estimates the effect of treatment arm, scaled time, scaled age, sex, race, baseline severity, and scaled adherence on the odds of asthma control. The SAS model uses `PROC GLIMMIX` with independent subject-level random intercept and random slope terms. The R implementation uses `lme4::glmer()` with the matching `||` random-effects structure.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

could be: "with independent (uncorrelated) subject-level random intercept and random slope terms."

Comment thread Comp/r-sas_glimmix.qmd

This scenario translates a SAS `PROC GLIMMIX` binary random-intercept model for timepoint-specific responder rates into R. The outcome is `AVAL`, where `1` indicates responder status. Repeated visits are clustered within `USUBJID`.

The SAS model uses `PROC GLIMMIX` with a binary logit model, treatment-by-visit interaction, and subject-level random intercept. The R implementation uses `glmmTMB` with the same conditional GLMM structure. `geepack` is used as a marginal sensitivity analysis.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why did you want the geepack model here? The SAS and glmmTMB models seem pretty similar.

Comment thread Comp/r-sas_glimmix.qmd
Comment on lines +1553 to +1557
emm_r <- emmeans::emmeans(
fit_r,
~ ARMCD,
type = "response"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

For this one, to mimic SAS (and get the rate), you could do:
emmeans::emmeans(fit_r, ~ ARMCD, type = "response", offset = 0)

Comment thread Comp/r-sas_glimmix.qmd
Comment on lines +1561 to +1566
pairs(
emm_r,
reverse = TRUE,
adjust = "none",
type = "response"
)

@danielpwilliams137 danielpwilliams137 Jun 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This one SAS (I think) reports the ratio as the difference on the log scale, so:

emm_r <- emmeans::emmeans(
    fit_r, ~ ARMCD)

pairs(
    emm_r,
    reverse = TRUE,
    adjust = "none")

Comment thread Comp/r-sas_glimmix.qmd
Comment on lines +1730 to +1733
scenario_09$AVAL_ORD <- ordered(
scenario_09$AVAL,
levels = c(1, 2, 3, 4, 5)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is the source of the sign discrepancy with SAS. Presumably, it orders the categories from last to first (like it does with CLASS variables as predictors). So maybe reverse the order of the levels:

levels = c(5,4,3,2,1)

Comment thread Comp/r-sas_glimmix.qmd
Odds_Ratio = exp(fixed_r_sas_scale[, "Estimate"]),
Lower_95_CI = exp(
fixed_r_sas_scale[, "Estimate"] -
1.96 * fixed_r_sas_scale[, "Std. Error"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A bit picky. Instead of 1.96 you could be more precise:
qnorm(p = 1-0.05/2)

Comment thread Comp/r-sas_glimmix.qmd
| 4 | 4 | 0.135 | 0.247 | 0.112 |
| 4 | 5 | 0.079 | 0.212 | 0.132 |

The likelihood, AIC, random-intercept variance, and fixed-effect estimates were aligned after reversing the R coefficient signs to match the SAS cumulative-logit direction. The active arm shifted probability away from lower `AVAL` categories and towards higher `AVAL` categories. This is consistent with the SAS cumulative-logit odds ratios and the R category probability estimates.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See comment above about reversing the response levels before fitting the model.

Comment thread Comp/r-sas_glimmix.qmd

- R uses GEE as a marginal sensitivity analysis for a conditional GLIMMIX model.

- Ordinal cumulative-logit models use different sign conventions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See other comments. This is probably just SAS's default. Presumably, if you don't specify an ordering in R it would be first to last, whereas SAS uses last to first.

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