如何用tidyverse生成向量类型列表列而非数据框列表列?
问题
tidyr::nest() 会生成数据框/tibble类型的列表列:
library(tidyverse) iris %>% nest(data = c(-Species)) #> # A tibble: 3 × 2 #> Species data #> <fct> <list> #> 1 setosa <tibble [50 × 4]> #> 2 versicolor <tibble [50 × 4]> #> 3 virginica <tibble [50 × 4]>
这种用法在很多场景中都很实用,但在部分场景下,我需要执行类似的隐式汇总操作,不过不是生成单个数据框类型的列表列,而是将每个选中列转换为向量类型的列表列。
目前可以通过nest()搭配unnest_wider()实现需求:
iris %>% nest(data = c(-Species)) %>% unnest_wider(data) #> # A tibble: 3 × 5 #> Species Sepal.Length Sepal.Width Petal.Length Petal.Width #> <fct> <list<dbl>> <list<dbl>> <list<dbl>> <list<dbl>> #> 1 setosa [50] [50] [50] [50] #> 2 versicolor [50] [50] [50] [50] #> 3 virginica [50] [50] [50] [50]
也可以通过更显式的汇总操作实现:
iris %>% group_by(Species) %>% summarize(across(everything(), vctrs::list_of)) #> # A tibble: 3 × 5 #> Species Sepal.Length Sepal.Width Petal.Length Petal.Width #> <fct> <list<dbl>> <list<dbl>> <list<dbl>> <list<dbl>> #> 1 setosa [50] [50] [50] [50] #> 2 versicolor [50] [50] [50] [50] #> 3 virginica [50] [50] [50] [50]
但这两种方法都略显繁琐,我想知道在tidyr或相关tidyverse包中,是否有更简洁、更标准的实现方式?
回答
在tidyverse生态里,目前最简洁且标准的实现方式就是用dplyr::summarize()配合across(),甚至可以直接用list()替代vctrs::list_of()(后者的优势是生成类型更严谨的列表列,避免混入不同类型的元素),写法非常紧凑:
iris %>% group_by(Species) %>% summarize(across(everything(), list))
输出结果和你之前的方法完全一致:
#> # A tibble: 3 × 5 #> Species Sepal.Length Sepal.Width Petal.Length Petal.Width #> <fct> <list<dbl>> <list<dbl>> <list<dbl>> <list<dbl>> #> 1 setosa [50] [50] [50] [50] #> 2 versicolor [50] [50] [50] [50] #> 3 virginica [50] [50] [50] [50]
另外,tidyr::nest_by()也能实现类似效果,但需要额外的步骤拆分列,写法反而不如summarize + across简洁,所以不推荐作为首选方案。
需要说明的是,tidyverse目前没有专门的单一函数直接实现「按分组将每列转为向量列表列」的操作,上面的summarize + across已经是最简洁的标准写法了。
内容的提问来源于stack exchange,提问作者lost
相关产品推荐
相关产品推荐

