使用R语言dplyr包将指定列对转换为行(宽表转长表)
解决宽格式DataFrame转长格式(按特定列对分组)的问题
嘿,我来帮你搞定这个宽转长的需求!首先先确认下你的示例数据和目标:
示例数据
你提供的原始数据框代码如下:
df <- data.frame(id=c(1,2,3,4,5), var=c("a","d","g","f","i"), a1=c(3,5,1,2,2), b1=c(2,4,1,2,3), a2=c(8,1,2,5,1), b2=c(1,6,4,7,2), a3=c(7,7,2,3,1), b3=c(1,1,4,9,6))
原始表格长这样:
| id | var | a1 | b1 | a2 | b2 | a3 | b3 |
|---|---|---|---|---|---|---|---|
| 1 | a | 3 | 2 | 8 | 1 | 7 | 1 |
| 2 | d | 5 | 4 | 1 | 6 | 7 | 1 |
| 3 | g | 1 | 1 | 2 | 4 | 2 | 4 |
| 4 | f | 2 | 2 | 5 | 7 | 3 | 9 |
| 5 | i | 2 | 3 | 1 | 2 | 1 | 6 |
你的目标是把a1&b1、a2&b2、a3&b3这样的列对转换成独立的行,最终得到包含id、var、a、b的长格式表格。
解决方案1:使用tidyverse的pivot_longer(推荐)
这是最直观且易读的方法,用到tidyr包的pivot_longer函数:
library(tidyr) # 执行宽转长 df_long <- df %>% pivot_longer( cols = starts_with(c("a", "b")), # 选中所有a/b开头的列 names_pattern = "(.)(\\d)", # 用正则捕获列名的前缀(a/b)和数字(1/2/3) names_to = c(".value", "group") # .value表示把前缀作为新列名,group存分组编号 ) %>% select(-group) # 可选:如果不需要分组编号列就删掉它
代码解释:
starts_with(c("a", "b")):精准选中所有需要转换的列(a1、b1到a3、b3)names_pattern = "(.)(\\d)":正则表达式把列名拆成两部分,第一部分是a或b,第二部分是数字编号names_to = c(".value", "group"):.value是特殊参数,它会把捕获的前缀作为新的列名(也就是最终的a和b),group用来存储原来的数字编号(比如1、2、3)
解决方案2:使用Base R的reshape函数
如果你不想加载额外的包,Base R的reshape函数也能实现:
df_long_base <- reshape( df, direction = "long", # 指定转换方向为长格式 varying = list(c("a1","a2","a3"), c("b1","b2","b3")), # 对应a和b的原始列组 v.names = c("a", "b"), # 新列的名字 idvar = c("id", "var"), # 保持不变的标识列 timevar = "group" # 分组变量名 ) %>% select(id, var, a, b) # 调整列顺序到你需要的格式
最终结果
两种方法都会得到你想要的长格式表格,前几行如下:
| id | var | a | b |
|---|---|---|---|
| 1 | a | 3 | 2 |
| 1 | a | 8 | 1 |
| 1 | a | 7 | 1 |
| 2 | d | 5 | 4 |
| 2 | d | 1 | 6 |
| 2 | d | 7 | 1 |
内容的提问来源于stack exchange,提问作者HSJ
相关产品推荐
相关产品推荐

