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

如何用gtsummary的tbl_summary()处理数值列中的占位值?

问题

使用gtsummary包的tbl_summary()汇总调查数据时,数值列中的占位值会干扰统计结果。例如变量weight中,998代表“不知道/无答案”,999代表“拒绝回答”,直接调用tbl_summary()会将这些值纳入均值、标准差计算,导致结果失真。期望实现:

  • 计算均值、标准差时排除998/999
  • 单独显示这两个占位值的计数和占比
  • 为统计项添加友好的描述性标签

初始代码及问题:

library(tidyverse)
library(gtsummary)

df <- tibble(
  weight = c(
    72, 83, 65, 998, 79,
    56, 999, 92, 67, 84,
    998, 75, 68, 999, NA,
    998, 73, 80, 91, NA
  )
)

tbl_summary(
  data = df,
  statistic = all_continuous() ~ "{mean} ({sd})"
)
解决方案

方法1:预处理数据拆分变量(最直观)

将原始变量拆分为“有效数值”和“应答情况”两个变量,让gtsummary自动处理统计逻辑:

library(tidyverse)
library(gtsummary)

df_processed <- df %>%
  mutate(
    # 提取有效体重值,占位值转为NA
    weight_valid = ifelse(weight %in% c(998, 999), NA, weight),
    # 将占位值转为分类标签,有效数值转为NA
    weight_response = case_when(
      weight == 998 ~ "不知道/无答案",
      weight == 999 ~ "拒绝回答",
      TRUE ~ NA_character_
    )
  )

# 汇总并合并行(可选)
tbl_summary(
  data = df_processed,
  include = c(weight_valid, weight_response),
  statistic = list(
    weight_valid ~ "{mean} ({sd})",
    weight_response ~ "{n} ({p}%)"
  ),
  label = list(
    weight_valid ~ "体重(有效数值)",
    weight_response ~ "体重应答情况"
  )
) %>%
  # 可选:将两个变量的行合并到同一组
  modify_table_body(
    mutate,
    variable = case_when(
      variable == "weight_response" ~ "weight_valid",
      TRUE ~ variable
    )
  ) %>%
  modify_header(label ~ "**体重**")

方法2:自定义统计+标签修改(无需改原始数据)

直接在tbl_summary()中定义自定义统计函数,再通过modify_table_body修改标签:

library(tidyverse)
library(gtsummary)

df <- tibble(
  weight = c(
    72, 83, 65, 998, 79,
    56, 999, 92, 67, 84,
    998, 75, 68, 999, NA,
    998, 73, 80, 91, NA
  )
)

# 定义简洁的自定义统计函数
stat_funs <- list(
  mean_sd = function(x) {
    x_clean <- x[!x %in% c(998, 999) & !is.na(x)]
    paste0(round(mean(x_clean), 1), " (", round(sd(x_clean), 1), ")")
  },
  donknow = function(x) {
    n <- sum(x == 998, na.rm = TRUE)
    p <- round(n/length(x)*100, 1)
    paste0(n, " (", p, "%)")
  },
  refused = function(x) {
    n <- sum(x == 999, na.rm = TRUE)
    p <- round(n/length(x)*100, 1)
    paste0(n, " (", p, "%)")
  }
)

tbl_summary(df,
  type = weight ~ "continuous2",
  statistic = weight ~ c("{mean_sd}", "{donknow}", "{refused}")
) %>%
  # 修改统计项的显示标签
  modify_table_body(
    mutate,
    label = case_when(
      row_type == "statistic" & label == "{mean_sd}" ~ "均值(标准差)",
      row_type == "statistic" & label == "{donknow}" ~ "不知道/无答案",
      row_type == "statistic" & label == "{refused}" ~ "拒绝回答",
      TRUE ~ label
    )
  ) %>%
  modify_header(label ~ "**体重**")

方法3:用add_stat()追加占位值统计(贴合gtsummary工作流)

先计算有效数值的统计,再通过add_stat()添加占位值的统计行:

library(tidyverse)
library(gtsummary)

df <- tibble(
  weight = c(
    72, 83, 65, 998, 79,
    56, 999, 92, 67, 84,
    998, 75, 68, 999, NA,
    998, 73, 80, 91, NA
  )
)

# 先汇总有效数值(排除占位值)
tbl <- df %>%
  mutate(weight = ifelse(weight %in% c(998, 999), NA, weight)) %>%
  tbl_summary(
    statistic = weight ~ "{mean} ({sd})",
    label = weight ~ "体重"
  )

# 添加占位值的统计行
tbl %>%
  add_stat(
    fns = list(
      ~ paste0(sum(.x == 998, na.rm=T), " (", round(sum(.x == 998, na.rm=T)/length(.x)*100,1), "%)"),
      ~ paste0(sum(.x == 999, na.rm=T), " (", round(sum(.x == 999, na.rm=T)/length(.x)*100,1), "%)")
    ),
    location = "after",
    label = c("不知道/无答案", "拒绝回答")
  )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:13:03