如何使用tidyverse将含新变量的长格式数据转为宽格式
问题:正确转换长格式数据为目标宽格式
问题重现
用户使用以下代码生成长格式数据并尝试转换为宽格式:
library(tidyverse) n=3 set.seed(1) d <- tibble(y=sample(0:1,n,T), x1=sample(1:5,n,T), x2=sample(1:5,n,T), x3=sample(1:5,n,T)) d_long <- d |> pivot_longer(!y) # 添加新变量x4的行 d_long_new <- d_long |> bind_rows(tibble(y=1,name="x4",value=10))
直接使用pivot_wider得到的是包含列表列的结果,不符合预期:
d_long_new |> pivot_wider(names_from = name, values_from = value)
结果:
# A tibble: 2 x 5 y x1 x2 x3 x4 <dbl> <list> <list> <list> <list> 1 0 <dbl [2]> <dbl [2]> <dbl [2]> <NULL> 2 1 <dbl [1]> <dbl [1]> <dbl [1]> <dbl [1]>
用户期望得到的宽格式是:
# A tibble: 4 x 5 y x1 x2 x3 x4 <dbl> <int> <int> <int> <dbl> 1 0 1 3 3 NA 2 1 2 2 1 NA 3 0 5 3 5 NA 4 1 NA NA NA 10
问题原因
pivot_wider默认将除names_from和values_from外的所有列作为分组依据,这里只有y列。相同y值的所有观测会被归为一组,当组内有多个对应值时,函数会自动将这些值打包成列表,导致结果不符合预期。
解决方案
需要添加一个分组标识符,用来区分同一y值下的不同原始观测行,让pivot_wider按照y+分组标识符来拆分数据,生成独立的宽行。
方法一:初始数据阶段保留原始行ID
在生成初始宽数据时就添加id列标记原始行,后续转换时基于id和y分组:
library(tidyverse) n=3 set.seed(1) # 新增id列标记原始宽行 d <- tibble( id = 1:n, y=sample(0:1,n,T), x1=sample(1:5,n,T), x2=sample(1:5,n,T), x3=sample(1:5,n,T) ) d_long <- d |> pivot_longer(!c(id, y)) # 添加新变量x4的行,指定新的id d_long_new <- d_long |> bind_rows(tibble(id=4, y=1, name="x4", value=10)) # 转换为宽格式,最后可选移除id列 d_wide <- d_long_new |> pivot_wider( id_cols = c(id, y), names_from = name, values_from = value ) |> select(-id) print(d_wide)
方法二:在现有长数据中添加分组标记
如果不想修改初始数据,直接在d_long_new中生成分组标识符:
# 给长数据添加分组号:前9行每3行一组(对应原始宽行),第10行单独一组 d_long_new <- d_long_new |> mutate( group_id = case_when( row_number() <= 9 ~ rep(1:3, each=3), TRUE ~ 4 ) ) # 转换为宽格式,最后可选移除group_id列 d_wide <- d_long_new |> pivot_wider( id_cols = c(group_id, y), names_from = name, values_from = value ) |> select(-group_id) print(d_wide)
两种方法都能得到预期的宽格式结果。
内容的提问来源于stack exchange,提问作者mr.T
相关产品推荐
相关产品推荐

