在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
相关产品推荐
相关产品推荐

