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

在R中转换含重复/缺失观测值的数据集

解决长格式生命体征数据转宽格式(保留重复观测)的问题

问题核心

需要将每行对应单个生命体征观测的长格式数据,转换为每行对应一位患者、每个重复观测单独成列的宽格式数据,且不做任何聚合操作。

解决步骤(使用dplyr + tidyr)

核心思路是先给每个患者的同一类型生命体征添加观测序号,再通过宽表转换生成带后缀的列名。

1. 加载所需工具包

library(dplyr)
library(tidyr)

2. 构建原始数据集

df <- data.frame(
  Pt_ID = c(1,1,1,1,1,1,2,2,3,3,3,3,3,3,3),
  vital_descr = c("HR","SBP","DBP","HR","SBP","DBP","SBP","DBP","HR","SBP","DBP","SBP","DBP","SBP","DBP"),
  vital_value = c(70,110,75,NA,105,60,150,90,55,150,70,160,70,160,80),
  stringsAsFactors = FALSE
)

3. 给观测添加分组序号

给每个患者的每种生命体征按出现顺序标记序号,确保重复观测能被区分:

df_with_seq <- df %>%
  group_by(Pt_ID, vital_descr) %>%
  mutate(obs_num = row_number()) %>%
  ungroup()

4. 转换为目标宽格式

使用pivot_wider将长表转宽表,自动生成vital_descr_obs_num格式的列名:

wide_df <- df_with_seq %>%
  pivot_wider(
    id_cols = Pt_ID,
    names_from = c(vital_descr, obs_num),
    values_from = vital_value
  )

5. 调整列顺序(可选)

如果需要让列按HR_1、SBP_1、DBP_1、HR_2...的逻辑顺序排列,可以执行:

# 提取列名中的序号和体征类型,按序号+体征排序
col_order <- names(wide_df)[-1] %>%
  str_split("_", simplify = TRUE) %>%
  as.data.frame() %>%
  mutate(V2 = as.integer(V2)) %>%
  arrange(V2, V1) %>%
  pull(V1, V2) %>%
  paste(names(.), ., sep = "_")

wide_df <- wide_df %>%
  select(Pt_ID, all_of(col_order))

最终输出结果

运行后得到的wide_df即为目标格式:

Pt_ID HR_1 SBP_1 DBP_1 HR_2 SBP_2 DBP_2 SBP_3 DBP_3
1     1   70   110    75   NA   105    60    NA    NA
2     2   NA   150    90   NA    NA    NA    NA    NA
3     3   55   150    70   NA   160    70   160    80

补充:用reshape2::dcast的实现方式

之前用dcast失败是因为默认会对同组数据做聚合,只要先添加分组序号,再将序号纳入公式即可:

library(reshape2)
df_with_seq <- df %>%
  group_by(Pt_ID, vital_descr) %>%
  mutate(obs_num = row_number()) %>%
  ungroup()

wide_df_dcast <- dcast(df_with_seq, Pt_ID ~ vital_descr + obs_num, value.var = "vital_value")

效果与tidyr方法一致,但tidyr的语法更直观易读。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 04:12:59