如何用R中tidyr的pivot_longer实现嵌套变量的宽转长?
使用
pivot_longer()将宽格式数据转换为带嵌套变量的长格式 需求说明
需要将包含多次随访及对应测试结果的宽格式数据,转换为每行对应个体-随访-测试项的长格式数据,最终结构需包含:个体标识(IDno、Sex)、随访次数、随访天数、测试类型、测试结果。
原始宽格式数据
变量说明
IDno、Sex:个体基础标识变量fu1、fu2:两次随访的天数cpass1/cpass2、is1/is2:分别对应两次随访的两项测试结果
创建数据代码
IDno <- c(1,2) Sex <- c("M","F") fu1 <- c(13,15) fu2 <- c(20,18) cpass1 <- c(27, 85) cpass2 <- c(33, 90) is1 <- c(201, 400) is2 <- c(220, 430) mydata <- data.frame(IDno, Sex, fu1, cpass1, is1, fu2, cpass2, is2)
期望的长格式结构
最终数据每行对应一次随访中的一项测试,列名及示例数据如下:
| IDno | Sex | fu | day | test | value |
|---|---|---|---|---|---|
| 1 | M | 1 | 13 | cpass | 27 |
| 1 | M | 1 | 13 | is | 201 |
| 1 | M | 2 | 20 | cpass | 33 |
| 1 | M | 2 | 20 | is | 220 |
| 2 | F | 1 | 15 | cpass | 85 |
| 2 | F | 1 | 15 | is | 400 |
| 2 | F | 2 | 18 | cpass | 90 |
| 2 | F | 2 | 18 | is | 430 |
尝试的错误代码
以下代码未能得到预期结构,出现了冗余行及列结构混乱:
#重命名变量 mydata_wide <- mydata %>% rename(fu1_day = fu1, cp_one = cpass1, is_one = is1, fu2_day = fu2, cp_two = cpass2, is_two = is2) #转换格式 mydata_wide %>% pivot_longer( cols = c(fu1_day, fu2_day), names_to = c("fu", ".value"), values_to = "day", names_sep = "_") %>% pivot_longer( cols = c("cp_one", "is_one", "cp_two", "is_two"), names_to = c("test", ".value"), values_to = "value", names_sep = "_")
正确解决方案
方法1:直接使用正则匹配变量名
无需提前重命名,通过names_pattern提取变量中的随访次数和类型:
library(tidyverse) mydata_long <- mydata %>% # 第一步:将同随访次数的变量整合,提取随访次数 pivot_longer( cols = -c(IDno, Sex), names_to = c(".value", "fu"), names_pattern = "(.*)(\\d)" # 正则匹配:变量名前缀 + 随访数字 ) %>% # 第二步:将测试项转为长格式 pivot_longer( cols = c(cpass, is), names_to = "test", values_to = "value" ) %>% # 重命名随访天数列,避免与随访次数列混淆 rename(day = fu) %>% # 调整列顺序至预期结构 select(IDno, Sex, fu, day, test, value) print(mydata_long)
方法2:先标准化变量名再转换
先将变量名调整为「类型_随访次数」的格式,再通过names_sep拆分:
library(tidyverse) # 标准化变量名 mydata_wide <- mydata %>% rename( fu_day_1 = fu1, cpass_1 = cpass1, is_1 = is1, fu_day_2 = fu2, cpass_2 = cpass2, is_2 = is2 ) # 转换格式 mydata_long <- mydata_wide %>% pivot_longer( cols = -c(IDno, Sex), names_to = c("var_type", ".value"), names_sep = "_" ) %>% pivot_longer( cols = c(cpass, is), names_to = "test", values_to = "value" ) %>% rename(day = fu_day) %>% select(IDno, Sex, fu, day, test, value) print(mydata_long)
两种方法均可得到符合预期的长格式数据。
内容的提问来源于stack exchange,提问作者KIM
相关产品推荐
相关产品推荐

