我有这些(杂乱的)数据,其中包含每个患者的药物治疗阶段(ip 或 cp)、药物名称(编码为数字)以及多种药物的剂量信息:
df_have
# id ip_drug1 ip_dose1 ip_drug2 ip_dose2 cp_drug1 cp_dose1 cp_drug2 cp_dose2
# 1 A1 1 300 3 100 6 500 7 100
# 2 A2 1 300 2 200 11 300 NA NA
# 3 A3 1 500 NA NA 9 100 5 1500
我想让这些数据整洁并采用长格式:
df_want
# id phase drug dose
# 1 A1 ip 1 300
# 2 A1 ip 3 100
# 3 A1 cp 6 500
# 4 A1 cp 7 100
# 5 A2 ip 1 300
# 6 A2 ip 2 200
# 7 A2 cp 11 300
# 8 A2 cp NA NA
# 9 A3 ip 1 500
# 10 A3 ip NA NA
# 11 A3 cp 9 100
# 12 A3 cp 5 1500
我能够通过 、 和 (和 ) 的组合获得所需tidyr::pivot_longer
的dplyr::mutate
数据tidyr::pivot_wider
框dplyr::select
:
library(tidyr)
library(dplyr)
df_have %>%
pivot_longer(cols = -id,
names_to = c("phase", "type"),
names_pattern = "(cp|ip)_(drug|dose)") %>%
mutate(temp = row_number(),
.by = c(id, phase, type)) %>%
pivot_wider(names_from = type,
values_from = value) %>%
select(-temp)
然而,上面的多步代码在我非常大的实际数据上非常慢。我希望在tidyr
/内更快地完成此转换dplyr
,最好是一步完成pivot_wider
。这可能吗?
可重复的df_have
数据df_want
框:
# have
df_have <- data.frame(id = paste0("A", 1:3),
ip_drug1 = 1,
ip_dose1 = c(300, 300, 500),
ip_drug2 = c(3, 2, NA),
ip_dose2 = c(100, 200, NA),
cp_drug1 = c(6, 11, 9),
cp_dose1 = c(500, 300, 100),
cp_drug2 = c(7, NA, 5),
cp_dose2 = c(100, NA, 1500))
# want
df_want <- data.frame(id = rep(paste0("A", 1:3), each = 4),
phase = rep(rep(c("ip", "cp"), each = 2), times = 3),
drug = c(1, 3, 6, 7, 1, 2, 11, NA, 1, NA, 9, 5),
dose = c(300, 100, 500, 100, 300, 200, 300, NA, 500, NA, 100, 1500))
一步就可以实现
pivot_longer
。这应该快 4.5 倍左右:.value
我认为关键在于论证中的使用names_to
。从?pivot_longer
:基准
@ThomasIsCoding 的解决方案甚至更快 (~ 12x)
输出
这是一个基本的 R 解决方案,其中包含一个
reshape
这使