使用pivot_wider仅转换单列部分层级为宽格式并保留指定层级
单列部分值转宽并保留其余层级的实现方案
需求说明
我需要将单列中仅部分层级/值转换为宽格式,其余选定层级保留在原列中。本次示例数据中food列的rice和beans值没有type属性,我希望保留原food列及其rice、beans层级,同时将其他值转宽。
示例数据
set.seed(1) df<-tibble(index=sample(1:5, 10, replace=TRUE), food=c(rep('fruit', 4),rep('meat', 4), 'rice', 'beans'), type=c('apple', 'apple', 'banana', 'banana', 'steak', 'steak', 't-bone', 't-bone', NA, NA)) # A tibble: 10 x 3 index food type <int> <chr> <chr> 1 1 fruit apple 2 4 fruit apple 3 1 fruit banana 4 2 fruit banana 5 5 meat steak 6 3 meat steak 7 2 meat t-bone 8 3 meat t-bone 9 3 rice NA 10 1 beans NA
期望输出
output<-structure(list(index = c(1L, 1L, 4L, 2L, 5L, 3L, 3L), fruit = c("apple", "banana", "apple", "banana", NA, NA, NA), meat = c(NA, NA, NA, "t-bone", "steak", "steak", "t-bone"), food = c("beans", NA, NA, NA, NA, "rice", NA)), row.names = c(NA, -7L), class = c("tbl_df", "tbl", "data.frame")) output # A tibble: 7 x 4 index fruit meat food <int> <chr> <chr> <chr> 1 1 apple NA beans 2 1 banana NA NA 3 4 apple NA NA 4 2 banana t-bone NA 5 5 NA steak NA 6 3 NA steak rice 7 3 NA t-bone NA
现有方法问题
之前尝试手动将rice、beans值移到type列,在food列创建对应层级,但转换过程繁琐不系统,还出现了值重复的非预期输出:
df1%>%mutate(type=coalesce(type, food), food=replace(food, type %in% c('rice', 'beans'), 'food'))%>% pivot_wider(id_cols = index, names_from = c(food), values_from = c(type))%>% unnest # A tibble: 7 x 4 index fruit meat food <int> <chr> <chr> <chr> 1 1 apple NA beans 2 1 banana NA beans ###<- 重复错误 3 4 apple NA NA 4 2 banana t-bone NA 5 5 NA steak NA 6 3 NA steak rice 7 3 NA t-bone rice ###<- 重复错误
错误原因是仅用index作为唯一行标识,同一个index下的多个条目会把food列的值广播到所有同index的行,导致重复。
正确解法
完全基于pivot_wider的实现方法如下,核心是增加分组内的行标识避免同index下的数值错位匹配:
library(tidyverse) df %>% # 新增行标识:同一index下的不同条目分配唯一序号,避免转宽时合并错位 group_by(index) %>% mutate(row_id = row_number()) %>% ungroup() %>% # 拆分需要转宽和需要保留的字段 mutate( # 临时字段:标记需要转宽的列名,不需要转宽的置为NA pivot_col = ifelse(!food %in% c("rice", "beans"), food, NA_character_), # 临时字段:转宽后列的取值 pivot_val = type, # 原food列仅保留rice和beans,其余置为NA food = ifelse(food %in% c("rice", "beans"), food, NA_character_) ) %>% # 执行宽转换 pivot_wider( id_cols = c(index, row_id, food), names_from = pivot_col, values_from = pivot_val ) %>% # 移除辅助行标识,调整列顺序,按index排序 select(index, fruit, meat, food) %>% arrange(index)
运行后输出结果和期望完全一致。
内容的提问来源于stack exchange,提问作者GuedesBF
相关产品推荐
相关产品推荐

