使用gtsummary生成含唯一受试者计数的不良事件(AE)表格的技术问题
使用gtsummary生成含唯一受试者计数的不良事件(AE)表格的技术问题
看起来你在尝试用gtsummary构建非嵌套的AE汇总表,每个行对应独立的唯一受试者计数(比如任意TEAE、任意严重TEAE这类独立类别),但用tbl_hierarchical遇到了零计数行不显示、计数强制嵌套的问题——这很正常,毕竟tbl_hierarchical本来是为层级结构(比如系统器官分类下的具体AE)设计的,用来做独立类别汇总确实有点“错位”。下面我针对你的两个核心挑战给出具体的解决方案:
一、先解决「非嵌套独立计数」的问题
我们的核心目标是:每个AE类别都独立统计唯一受试者数,不受其他类别层级影响。这里更适合用tbl_summary配合数据预处理来实现,而非tbl_hierarchical:
步骤1:预处理数据,确保每个受试者只被计数一次
一个受试者可能有多个AE记录,但我们要的是唯一受试者数,所以先按受试者去重,保留每个受试者的各类AE标记:
# 按受试者+治疗组去重,生成每个受试者的各类AE状态(仅保留是否符合类别) adae_subj <- adae %>% group_by(USUBJID, TRT01A, TRT01AN) %>% summarize( ANY.TEAE = if_else(any(ANY.TEAE == "Any TEAE"), "Any TEAE", "N"), ANY.SERAE = if_else(any(ANY.SERAE == "Any TE Serious AE"), "Any TE Serious AE", "N"), ANY.DREL = if_else(any(ANY.DREL == "Any Drug Related AE"), "Any Drug Related AE", "N"), ANY.SERREL = if_else(any(ANY.SERREL == "Any Drug Related Serious AE"), "Any Drug Related Serious AE", "N"), .groups = "drop" )
步骤2:宽转长,适配tbl_summary的分类统计逻辑
把每个AE类别转成单独的行,方便tbl_summary批量处理:
adae_long <- adae_subj %>% pivot_longer( cols = starts_with("ANY."), names_to = "ae_category", values_to = "ae_status" ) %>% filter(ae_status != "N") # 先过滤掉无符合的标记,后续再处理零计数
步骤3:用tbl_summary生成独立计数表
关联adsl作为分母,指定按治疗组分组统计唯一受试者数:
# 先从adsl提取各组总受试者数,作为统计分母 trt_totals <- adsl %>% group_by(TRT01A) %>% summarize(total_n = n_distinct(USUBJID), .groups = "drop") # 生成基础表格 ae_table <- adae_long %>% tbl_summary( by = TRT01A, include = ae_category, type = all_categorical() ~ "categorical", statistic = all_categorical() ~ "{n} ({p}%)", # 手动指定各组分母,确保用adsl的总受试者数 denominator = list( "Xanomeline High Dose" = trt_totals$total_n[trt_totals$TRT01A == "Xanomeline High Dose"], "Xanomeline Low Dose" = trt_totals$total_n[trt_totals$TRT01A == "Xanomeline Low Dose"], "Placebo" = trt_totals$total_n[trt_totals$TRT01A == "Placebo"] ) ) %>% add_overall(last = TRUE) %>% # 适配你需要的表头格式 modify_header( all_stat_cols() ~ "**{level}**\n(N = {n})\nn(%)", stat_0 ~ "**Total**\n(N = {n})\nn(%)", label ~ "Number of Participants" ) %>% modify_column_hide(variable) # 隐藏原始变量列,只保留AE类别
二、解决「显示零计数行」的问题
默认情况下,tbl_summary不会显示某组中计数为0的AE类别,我们需要先生成所有AE类别+治疗组的全组合,再填充零计数:
步骤1:生成AE类别与治疗组的全组合
# 提取所有需要统计的AE类别 ae_categories <- c("Any TEAE", "Any TE Serious AE", "Any Drug Related AE", "Any Drug Related Serious AE") # 提取所有治疗组 trt_groups <- unique(adae_subj$TRT01A) # 生成所有可能的组合,确保没有遗漏 full_combinations <- expand.grid( ae_category = ae_categories, TRT01A = trt_groups, stringsAsFactors = FALSE )
步骤2:合并数据并填充零计数
adae_long_full <- adae_long %>% group_by(ae_category, TRT01A) %>% summarize(n = n_distinct(USUBJID), .groups = "drop") %>% # 关联全组合,补全零计数的行 right_join(full_combinations, by = c("ae_category", "TRT01A")) %>% mutate(n = replace_na(n, 0)) %>% # 关联分母计算百分比 left_join(trt_totals, by = "TRT01A") %>% mutate(ae_status = if_else(n > 0, ae_category, "N")) %>% ungroup()
步骤3:调整tbl_summary配置,显示零计数
ae_table_with_zero <- adae_long_full %>% tbl_summary( by = TRT01A, include = ae_category, type = all_categorical() ~ "categorical", statistic = all_categorical() ~ "{n} ({p}%)", denominator = list( "Xanomeline High Dose" = trt_totals$total_n[trt_totals$TRT01A == "Xanomeline High Dose"], "Xanomeline Low Dose" = trt_totals$total_n[trt_totals$TRT01A == "Xanomeline Low Dose"], "Placebo" = trt_totals$total_n[trt_totals$TRT01A == "Placebo"] ), missing = "always", missing_text = "0 (0.0%)" ) %>% add_overall(last = TRUE) %>% modify_header( all_stat_cols() ~ "**{level}**\n(N = {n})\nn(%)", stat_0 ~ "**Total**\n(N = {n})\nn(%)", label ~ "Number of Participants" ) %>% modify_column_hide(variable) %>% # 过滤掉自动生成的"Missing"行,只保留我们需要的AE类别行 modify_filter(label %in% ae_categories) %>% # 移除默认表头脚注 remove_footnote_header(columns = all_stat_cols())
三、额外格式对齐调整
如果你需要和原来用tbl_hierarchical生成的格式完全一致,还可以添加:
ae_table_with_zero <- ae_table_with_zero %>% # 调整缩进(非嵌套所以设为0) modify_indent( columns = label, rows = label %in% ae_categories, indent = 0 )
为什么不用tbl_hierarchical?
tbl_hierarchical的核心设计目标是处理层级嵌套的变量(比如“消化系统疾病”下的“恶心”“呕吐”),它会默认按变量的层级关系汇总计数,所以天然不适合你的独立类别统计需求。而tbl_summary配合数据预处理,能更灵活地实现非嵌套的唯一受试者计数场景。
内容来源于stack exchange
相关产品推荐
相关产品推荐

