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

如何用tbl_cross展示配对表并添加自定义McNemar mid-p检验?

问题与解决方案

问题背景

已知可通过tbl_summary执行McNemar检验,但更倾向于用tbl_cross展示交叉表结果。需要采用mid-p检验计算McNemar检验的p值,不过tbl_cross的add_p()函数存在自定义方法限制:指定自定义函数mcnemar_stats后,实际仍调用Fisher精确检验,希望找到既能使用自定义检验方法,又保留tbl_cross输出格式的方案。

解决方案

tbl_cross的add_p()默认适配独立样本的检验方法,直接传递自定义配对样本检验函数会被覆盖。可以通过先生成交叉表,再手动插入自定义检验结果的方式解决,具体步骤如下:

  • 用tbl_cross生成符合需求的交叉表格式
  • 编写正确的mid-p版McNemar检验函数(修复原函数中未定义的变量问题)
  • 计算mid-p值后,通过modify_table_body()将结果插入表格,保持原有格式

修正后的完整代码

# 加载所需包
library(gtsummary)
library(dplyr)
library(tidyr)

# 生成模拟数据集:100例受试者的Week16和Week32应答数据
set.seed(123)
subject_ids <- paste0("SUBJ", sprintf("%03d", 1:100))
dummy_data <- data.frame(
  USUBJID = rep(subject_ids, each = 2),
  AVISIT = rep(c("Week 16", "Week 32"), times = 100)
)

# 生成有相关性的应答数据
set.seed(456)
responses <- vector("character", length = 200)
for (i in seq(1, 199, by = 2)) {
  responses[i] <- sample(c("Yes", "No"), 1, prob = c(0.3, 0.7))
  responses[i+1] <- ifelse(responses[i] == "Yes",
                           sample(c("Yes", "No"), 1, prob = c(0.7, 0.3)),
                           sample(c("Yes", "No"), 1, prob = c(0.2, 0.8)))
}
dummy_data$CRIT1FL <- responses

# 转换为宽格式,用于配对检验
dummy_wide <- dummy_data %>%
  pivot_wider(id_cols = USUBJID, names_from = AVISIT, values_from = CRIT1FL) %>%
  mutate(across(c(`Week 16`, `Week 32`), ~factor(.x, levels = c("Yes", "No"))))

# 定义mid-p版McNemar检验函数(修复变量未定义问题)
mcnemar_midp <- function(data, row_var, col_var) {
  tbl <- table(data[[row_var]], data[[col_var]])
  discordant_n <- tbl[1,2] + tbl[2,1]  # 不一致对数总和
  x <- min(tbl[1,2], tbl[2,1])         # 较小的不一致数
  # 计算mid-p值
  midp_p <- 2 * pbinom(x, discordant_n, 0.5, lower.tail = TRUE) - dbinom(x, discordant_n, 0.5)
  tibble(p.value = midp_p, method = "McNemar检验(mid-p法)")
}

# 1. 生成交叉表
cross_table <- dummy_wide %>%
  tbl_cross(row = `Week 16`, col = `Week 32`, percent = "cell") %>%
  modify_header(p.value = "**McNemar P值**") %>%
  modify_footnote(p.value ~ "采用mid-p法计算") %>%
  modify_fmt_fun(p.value = label_style_sigfig(digits = 3))

# 2. 计算mid-p值
midp_result <- mcnemar_midp(dummy_wide, "Week 16", "Week 32")

# 3. 将p值插入交叉表,替换默认的Fisher检验结果
final_table <- cross_table %>%
  modify_table_body(
    ~.x %>%
      mutate(p.value = ifelse(row_type == "label", midp_result$p.value, p.value))
  )

# 展示结果
final_table

关键说明

  • 原函数中未定义的n变量修正为discordant_n(不一致对数总和),这是mid-p检验的正确计算基础
  • 通过modify_table_body()精准替换表格中的p值,完全保留tbl_cross的原有格式
  • 自定义函数专注于计算mid-p值,与表格生成逻辑分离,避免add_p()的默认行为干扰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 17:29:52