在R中实现两级拆分:将数据框转为双层列表向量
基于component和hyperpar层级将data.frame转换为双层列表
问题背景
现有如下结构的data.frame h:
component hyperpar x 1 i sigma2 1.0 2 envN sigma2 1.0 3 envN sigma2_int 0.1 4 envN sigma2_int 0.1 ... 85 dt sigma2 1.0 86 dt ls 40000.0 87 dt ls 40000.0 88 year sigma2 1.0
需要将其转换为双层列表:外层列表以component值为名称,内层列表以hyperpar值为名称,对应的值是该分组下的x向量。
已通过unstack(h, x ~ component)实现单层拆分,但尝试unstack(h, x ~ component ~ hyperpar)或unstack(h, x ~ component + hyperpar)均未得到预期的双层结构,希望用tidyverse的简洁方案实现。
完整测试数据集:
h <- structure(list(component = c("i", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "envN", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "env", "spat", "spat", "spat", "dt", "dt", "dt", "year"), hyperpar = c("sigma2", "sigma2", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_int", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2_slope", "sigma2", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "ls", "sigma2", "ls", "ls", "sigma2", "ls", "ls", "sigma2"), x = c(1, 1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 40000, 1, 10, 10, 1, 40000, 40000, 1)), row.names = c(NA, -88L), class = "data.frame")
解决方案
方法一:分组嵌套 + 映射转换
利用dplyr的分组、嵌套功能,结合purrr的映射函数,逐步构建双层列表:
library(tidyverse) result <- h %>% # 先按component和hyperpar分组,将每组的x转为列表 group_by(component, hyperpar) %>% summarise(x_vec = list(x), .groups = "drop") %>% # 再按component分组,嵌套hyperpar和x_vec group_by(component) %>% nest() %>% # 将每个component内的嵌套数据框转为hyperpar为名称的列表 mutate(data = map(data, ~deframe(.x))) %>% # 最终转为以component为名称的双层列表 deframe()
查看结果示例:
# 查看envN对应的内层列表 result$envN # $sigma2 # [1] 1 # # $sigma2_int # [1] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 # # $sigma2_slope # [1] 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
方法二:分组内直接unstack
更简洁的写法,在每个component分组内用unstack实现内层拆分:
result2 <- h %>% group_by(component) %>% # 对每个component分组内的数据,用unstack拆分出hyperpar层级的列表 summarise(hyperpar_list = list(unstack(cur_data(), x ~ hyperpar))) %>% # 转为双层列表 deframe()
这个方法逻辑更直接,利用cur_data()获取当前分组内的数据,再通过unstack生成内层列表,最后用deframe整理为外层以component为名称的双层结构。
两种方法都能得到符合需求的双层列表,且完全遵循tidyverse的链式语法风格,避免了繁琐的lapply嵌套写法。
内容的提问来源于stack exchange,提问作者Tomas
相关产品推荐
相关产品推荐

