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

如何用gtsummary构建多时间点生命体征分层汇总表?

问题描述

我有长格式的多时间点(0H、4H、8H等)生命体征数据,包含HR、TEMP、SYSBP等测量指标,数据结构如下:

data <- structure(list(SUBJID = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), VSTESTCD = c("HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP", "HR", "TEMP", "SYSBP", "DIABP", "RESP"), APERIODC = c("Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 1", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2", "Period 2"), VSSTRESN = c(66, 36.5, 119, 78, 16, 67, 120, 77, 17, 69, 121, 79, 16, 70, 36.5, 122, 82, 17, 69, 36.5, 123, 83, 17, 69, 36.5, 121, 79, 17, 68, 36.6, 120, 77, 17, 68, 121, 77, 17, 70, 123, 77, 17, 72, 36.5, 122, 79, 17, 71, 36.5, 124, 81, 16, 69, 36.6, 122, 80, 18, 69, 36.6, 121, 80, 18, 67, 120, 79, 18, 69, 119, 78, 17, 68, 36.5, 121, 80, 18, 65, 36.6, 121, 80, 18, 70, 36.6, 121, 79, 18, 72, 36.6, 121, 80, 16, 69, 121, 80, 17, 66, 119, 78, 18, 68, 36.6, 120, 79, 18, 68, 36.6, 120, 79, 18, 67, 36.5, 119, 77, 17), VSTPT = c("0H", "0H", "0H", "0H", "0H", "4H", "4H", "4H", "4H", "8H", "8H", "8H", "8H", "12H", "12H", "12H", "12H", "12H", "24H", "24H", "24H", "24H", "24H", "36H", "36H", "36H", "36H", "36H", "0H", "0H", "0H", "0H", "0H", "4H", "4H", "4H", "4H", "8H", "8H", "8H", "8H", "12H", "12H", "12H", "12H", "12H", "24H", "24H", "24H", "24H", "24H", "36H", "36H", "36H", "36H", "36H", "0H", "0H", "0H", "0H", "0H", "4H", "4H", "4H", "4H", "8H", "8H", "8H", "8H", "12H", "12H", "12H", "12H", "12H", "24H", "24H", "24H", "24H", "24H", "36H", "36H", "36H", "36H", "36H", "0H", "0H", "0H", "0H", "0H", "4H", "4H", "4H", "4H", "8H", "8H", "8H", "8H", "12H", "12H", "12H", "12H", "12H", "24H", "24H", "24H", "24H", "24H", "36H", "36H", "36H", "36H", "36H")), row.names = c(NA, -112L), class = c("tbl_df", "tbl", "data.frame"))

目标是用gtsummary构建一张按VSTESTCD(测量指标)和VSTPT(时间点)分类的汇总表,格式要求:

  • 按指标分组(如HR、TEMP等),每个指标作为分组标题
  • 每个指标下展示不同时间点(0H、4H等)的统计值
  • 分两列展示Period 1和Period 2的均值±标准差,同时标注每组的样本量

我尝试用nest函数编写代码,但未得到预期结果,代码如下:

data |>
  select(SUBJID, VSTESTCD, APERIODC, VSSTRESN, VSTPT) |>
  nest(data = -c(VSTPT, VSTESTCD)) |> 
  rowwise() |> 
  mutate(
    tbl = 
      data |>
      tbl_summary(
        by = APERIODC,
        digits = all_continuous() ~ 2,
        include = -SUBJID,
        type = VSSTRESN ~ "continuous",
        statistic = VSSTRESN ~ "{mean} ({sd})",
        label = list(VSSTRESN = VSTPT)
      )  |>
      modify_header(list(
        label ~ "**Test**",
        all_stat_cols() ~ "**{level}**, N = {n}"
      )) |> 
      list()
  ) |> 
  pull(tbl) |> 
  tbl_stack() |> 
  modify_spanning_header(all_stat_cols() ~ "**Stage**") |> 
  modify_table_body( ~.x |> dplyr::relocate(stat_1, .before = stat_2))
调整后的解决方案

关键调整点

  1. 先按VSTESTCD分组嵌套,确保每个指标作为独立的分组模块
  2. 在子表生成时,将时间点VSTPT设置为统计项的标签,同时为每个指标添加分组标题
  3. 统一表头格式,确保跨栏标题和列标题匹配目标样式
  4. 优化tbl_stack的分组标题显示,避免重复表头

完整代码

library(gtsummary)
library(dplyr)
library(glue)

# 按指标分组嵌套,生成每个指标的子表
result_tbl <- data |>
  select(SUBJID, VSTESTCD, APERIODC, VSSTRESN, VSTPT) |>
  group_by(VSTESTCD) |>
  nest() |>
  mutate(
    tbl = map(
      data,
      ~.x |>
        tbl_summary(
          by = APERIODC,
          include = VSSTRESN,
          type = VSSTRESN ~ "continuous",
          statistic = VSSTRESN ~ "{mean} ({sd})",
          digits = all_continuous() ~ 2,
          label = VSSTRESN ~ VSTPT,
          # 自动计算每个Period的样本量
          addn = TRUE
        ) |>
        # 替换表头为"Time Point"
        modify_header(label ~ "**Time Point**") |>
        # 调整统计列表头,显示Period和样本量
        modify_header(all_stat_cols() ~ "**{level}** (N = {n})") |>
        # 添加当前指标作为分组标题
        modify_table_styling(
          locations = heading,
          text = glue("**{first(.x$VSTESTCD)}**")
        )
    )
  ) |>
  pull(tbl) |>
  # 堆叠子表并保留分组标题
  tbl_stack(group_header = TRUE) |>
  # 添加顶部跨栏标题
  modify_spanning_header(all_stat_cols() ~ "**Stage**") |>
  # 调整列顺序,确保Period 1在前
  modify_table_body(~.x |> relocate(stat_1, .before = stat_2))

# 查看最终表格
result_tbl

代码解释

  • 分组嵌套:先按VSTESTCD分组,确保每个指标的子表独立生成,方便添加专属分组标题
  • 子表配置:
    • label = VSSTRESN ~ VSTPT:将时间点作为统计项的标签,替代默认的指标名称
    • addn = TRUE:自动计算每个Period的样本量,并显示在列标题中
    • modify_table_styling:为每个指标添加加粗的分组标题,增强表格可读性
  • 堆叠与格式调整:tbl_stack(group_header = TRUE)保留每个指标的分组标题,modify_spanning_header添加顶部跨栏标题,最后调整列顺序匹配需求

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

相关产品推荐
方舟 Agent Plan

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

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