使用dplyr::pivot_longer转换宽表为长表的结构匹配问题
宽表转长表:调整pivot_longer的names_to和names_pattern参数
现有一个宽格式数据集,包含3个波次(wave)的好友提名(fid)数据,每个波次下有3个好友提名,且每个提名对应a、b、c三项跟进问题。需要将该宽表转换为指定结构的长表,但当前使用的pivot_longer代码输出不符合预期,需调整names_to和names_pattern参数。
示例数据
df <- read.table(text= "id w1fid1 w1fid2 w1fid3 w2fid1 w2fid2 w2fid3 w3fid1 w3fid2 w3fid3 w1a1 w1a2 w1a3 w1b1 w1b2 w1b3 w1c1 w1c2 w1c3 w2a1 w2a2 w2a3 w2b1 w2b2 w2b3 w2c1 w2c2 w2c3 w3a1 w3a2 w3a3 w3b1 w3b2 w3b3 w3c1 w3c2 w3c3 1 10 3 9 4 13 9 15 14 NA 1 1 0 0 1 0 1 0 0 1 1 1 1 0 0 1 1 1 1 1 NA 1 0 NA 0 0 NA 2 13 NA 20 4 18 19 12 10 8 1 NA 0 0 NA 0 0 NA 0 1 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 1 0 0 3 NA 9 19 19 8 20 17 9 2 NA 1 1 NA 0 0 NA 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 1 0 4 13 19 9 19 16 2 2 11 14 1 1 1 1 1 1 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 5 11 8 9 18 20 7 9 17 12 0 1 1 0 0 0 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 1 1 0 1 0", header = TRUE)
期望输出
df_target <- read.table(text= "id wave fid a b c 1 1 10 1 0 1 1 1 3 1 1 0 1 1 9 0 0 0 1 2 4 1 1 1 1 2 13 1 0 1 1 2 9 1 0 1 1 3 15 1 1 0 1 3 14 1 0 0 1 3 NA NA NA NA 2 1 13 1 0 0 2 1 NA NA NA NA 2 1 20 0 0 0 2 2 4 1 1 0 2 2 18 0 1 1 2 2 19 0 0 0 2 3 12 0 1 1 2 3 10 0 0 0 2 3 8 0 0 0 3 1 NA NA NA NA 3 1 9 1 0 0 3 1 19 1 0 0 3 2 19 0 0 1 3 2 8 0 0 0 3 2 20 1 0 0 3 3 17 0 0 1 3 3 9 0 0 1 3 3 2 1 0 0 4 1 13 1 1 1 4 1 19 1 1 1 4 1 9 1 1 0 4 2 19 0 0 0 4 2 16 0 0 0 4 2 2 1 1 0 4 3 2 0 0 0 4 3 11 0 0 1 4 3 14 0 1 0 5 1 11 0 0 1 5 1 8 1 0 1 5 1 9 1 0 1 5 2 18 1 1 1 5 2 20 0 1 0 5 2 7 1 0 1 5 3 9 1 1 0 5 3 17 0 1 1 5 3 12 0 1 0", header = TRUE)
当前代码及输出
当前代码
df %>% pivot_longer(-id, names_to = c('wave', 'type', '.value'), names_pattern = "(\\w+\\d+)(\\w+)(\\d+)")
当前输出
# A tibble: 60 × 5 id wave type `1` `2` `3` <int> <chr> <chr> <int> <int> <int> 1 1 w1 fid 10 3 9 2 1 w2 fid 4 13 9 3 1 w3 fid 15 14 NA 4 1 w1 a 1 1 0 5 1 w1 b 0 1 0 6 1 w1 c 1 0 0 7 1 w2 a 1 1 1 8 1 w2 b 1 0 0 9 1 w2 c 1 1 1 10 1 w3 a 1 1 NA # ℹ 50 more rows
解决方案
问题出在正则表达式的分组逻辑和names_to的映射顺序错误。正确的做法是将列名拆分为波次标识、数据类型、提名序号三个部分,让数据类型对应最终的列名(fid/a/b/c),提名序号用来对齐同一提名的各项数据。
正确代码
library(tidyr) library(dplyr) df %>% pivot_longer( cols = -id, names_to = c("wave", ".value", "nomination"), names_pattern = "(w\\d)(fid|a|b|c)(\\d)" ) %>% # 将wave从字符型转为整数(如w1→1) mutate(wave = as.integer(sub("w", "", wave))) %>% # 调整列顺序并移除临时的nomination列 select(id, wave, fid, a, b, c) %>% # 按id和wave排序,匹配期望输出 arrange(id, wave)
代码说明
names_pattern正则分组:(w\\d):匹配波次前缀(w1/w2/w3)(fid|a|b|c):匹配数据类型(好友提名或三个跟进问题)(\\d):匹配每个波次下的提名序号(1/2/3)
names_to映射:- 第一个分组对应
wave列,存储波次信息 - 第二个分组用
.value,表示这部分字符串将作为最终的列名(fid/a/b/c) - 第三个分组对应临时列
nomination,用于对齐同一序号的fid和a/b/c数据
- 第一个分组对应
- 后续通过
mutate转换波次格式,select调整列顺序,arrange排序,最终得到与期望一致的长表结构。
内容的提问来源于stack exchange,提问作者L. Tucker
相关产品推荐
相关产品推荐

