You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带自定义MAFC logit链接函数的二项式GLMM中emmeans::regrid()反变换失效的解决方法咨询

Great question! The issue here is that emmeans doesn't recognize your custom mafc.logit(2) link function out of the box, so it can't automatically apply the inverse transformation to get back to the [0.5, 1] probability scale. Let's walk through two reliable solutions to fix this:

Solution 1: Manual Inverse Transformation (Most Straightforward)

Since you know the exact form of your inverse link function (P = 0.5 + 0.5 * plogis(eta)), you can compute the response-scale probabilities, standard errors, and confidence intervals manually using the linear-scale results from emmeans. Here's how:

# 1. Get linear-scale (eta) emmeans and contrasts
mod.emm <- emmeans(mod, ~group|stim_strength, at=list(stim_strength=c(.25,.75)))
contrasts_lin <- contrast(mod.emm, 'pairwise', simple = 'group', combine = TRUE, adjust = 'holm')

# 2. Define inverse link function and its derivative (for SE calculation via delta method)
inv_mafc_logit <- function(eta, m=2) {
  1/m + (1 - 1/m) * plogis(eta)
}

mu_eta <- function(eta, m=2) {
  # Derivative of inverse link w.r.t. eta (needed for SE scaling)
  (1 - 1/m) * plogis(eta) * (1 - plogis(eta))
}

# 3. Convert emmeans to probability scale
emm_df <- as.data.frame(mod.emm)
emm_df$prob <- inv_mafc_logit(emm_df$emmean, m=2)
emm_df$prob_se <- emm_df$SE * mu_eta(emm_df$emmean, m=2)
# Calculate confidence intervals using normal approximation
emm_df$prob_lower <- inv_mafc_logit(emm_df$emmean - qnorm(0.975)*emm_df$SE, m=2)
emm_df$prob_upper <- inv_mafc_logit(emm_df$emmean + qnorm(0.975)*emm_df$SE, m=2)

# View cleaned probability-scale results
cat("Probability-scale emmeans with CIs:\n")
print(emm_df[, c("stim_strength", "group", "prob", "prob_se", "prob_lower", "prob_upper")], digits=3)

# 4. Convert contrasts to response-scale differences
contrast_df <- as.data.frame(contrasts_lin)
# Contrast is (group1 eta) - (group2 eta), so response-scale difference is inv_mafc(eta1) - inv_mafc(eta2)
eta1 <- emm_df$emmean[emm_df$group == 1]
eta2 <- emm_df$emmean[emm_df$group == 2]
contrast_df$prob_diff <- inv_mafc_logit(eta1, m=2) - inv_mafc_logit(eta2, m=2)
# Use delta method for SE of the difference: SE = sqrt( (mu_eta(eta1)*SE1)^2 + (mu_eta(eta2)*SE2)^2 )
se1 <- emm_df$SE[emm_df$group == 1]
se2 <- emm_df$SE[emm_df$group == 2]
contrast_df$prob_diff_se <- sqrt( (mu_eta(eta1)*se1)^2 + (mu_eta(eta2)*se2)^2 )
# Calculate CIs for the difference
contrast_df$prob_diff_lower <- contrast_df$prob_diff - qnorm(0.975)*contrast_df$prob_diff_se
contrast_df$prob_diff_upper <- contrast_df$prob_diff + qnorm(0.975)*contrast_df$prob_diff_se

# View cleaned contrast results
cat("\nResponse-scale group differences with CIs:\n")
print(contrast_df[, c("stim_strength", "prob_diff", "prob_diff_se", "prob_diff_lower", "prob_diff_upper", "p.value")], digits=3)

If you want emmeans to handle the transformation automatically (e.g., using type='response' or regrid()), you need to ensure your mafc.logit link function is properly defined with a linkinv (inverse link) attribute, and register it with emmeans.

First, double-check your mafc.logit function is defined correctly (this is the standard structure for custom GLM links):

mafc.logit <- function(m) {
  # Link function: maps probability to linear predictor eta
  linkfun <- function(mu) {
    if (any(mu <= 1/m | mu >= 1)) stop("mu must be between 1/m and 1")
    log( (mu - 1/m) / (1 - mu) )
  }
  
  # Inverse link function: maps eta back to probability
  linkinv <- function(eta) {
    1/m + (1 - 1/m) * plogis(eta)
  }
  
  # Derivative of inverse link (needed for model fitting and SE calculations)
  mu.eta <- function(eta) {
    (1 - 1/m) * plogis(eta) * (1 - plogis(eta))
  }
  
  # Validate eta values
  valideta <- function(eta) TRUE
  
  # Structure as a link-glm object with a clear name
  structure(
    list(linkfun = linkfun, linkinv = linkinv, mu.eta = mu.eta, valideta = valideta, 
         name = paste0("mafc.logit(", m, ")")),
    class = "link-glm"
  )
}

Once your link is properly defined, you can tell emmeans how to handle it by adding a transformation method. Run this once in your session:

# Register the inverse transformation for emmeans
emm_options(
  tran = list(
    "mafc.logit(2)" = list(
      linkfun = function(mu) log( (mu - 0.5)/(1 - mu) ),
      linkinv = function(eta) 0.5 + 0.5*plogis(eta),
      mu.eta = function(eta) 0.5*plogis(eta)*(1 - plogis(eta))
    )
  )
)

Now emmeans should recognize the link, and type='response' or regrid() should work as expected:

mod.emm <- emmeans(mod, ~group|stim_strength, at=list(stim_strength=c(.25,.75)))
# Get response-scale CIs
confint(mod.emm, type='response')
# Get response-scale contrasts
contrast(regrid(mod.emm), 'pairwise', simple='group', combine=TRUE, adjust='holm')

This should give you probabilities in the [0.5,1] range directly, without manual calculation.

Key Notes:

  • The delta method used in Solution 1 assumes normality of the linear predictor estimates, which is standard for GLMMs with large sample sizes.
  • For Solution 2, if you use m values other than 2, you'll need to register a separate transformation for each (e.g., mafc.logit(3)).

内容的提问来源于stack exchange,提问作者noe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:27:36