这是一个可重现的示例,其中我使用辅助列(temp)生成嵌套的data
列。
如何在不使用辅助列的情况下获得相同的结果?我尝试使用group_by_all
,但它没有用。 (所以,我也不确定我是否理解了group_by_all函数的用途)
library(tidyverse)
df <- structure(list(Var1 = c(0L, 1L, 2L, 3L, 0L, 1L, 2L, 3L, 0L, 1L,
2L, 3L, 0L, 1L, 2L, 3L, 0L, 1L, 2L, 3L, 0L, 1L, 2L, 3L), Var2 = c(0L,
0L, 0L, 0L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 0L, 0L, 0L, 0L, 1L,
1L, 1L, 1L, 2L, 2L, 2L, 2L), Var3 = c(0L, 0L, 0L, 0L, 0L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L)), .Names = c("Var1", "Var2", "Var3"), out.attrs = structure(list(
dim = c(4L, 3L, 2L), dimnames = structure(list(Var1 = c("Var1=0",
"Var1=1", "Var1=2", "Var1=3"), Var2 = c("Var2=0", "Var2=1",
"Var2=2"), Var3 = c("Var3=0", "Var3=1")), .Names = c("Var1",
"Var2", "Var3"))), .Names = c("dim", "dimnames")), class = "data.frame", row.names = c(NA,
-24L))
df$temp <- 1:nrow(df)
df %>% group_by(temp) %>% nest %>% select(-temp)
答案 0 :(得分:7)
这是4个解决方案
我设计了一个名为tags的软件包(目前仅适用于github),该软件包的功能grouping_by
包裹在dplyr::group_by
周围,允许与副词/修饰符/函数运算符的行为进行分组,取消分组也一样。按未命名的表达式分组时,不会保留temp列,并且语法更紧凑,希望与您要查找的内容足够接近:
# devtools::install_github("moodymudskipper/tags")
library(tidyverse)
library(tags)
df %>% grouping_by(vars(row_number()))$nest()
#> # A tibble: 24 x 1
#> data
#> <list>
#> 1 <tibble [1 x 3]>
#> 2 <tibble [1 x 3]>
#> 3 <tibble [1 x 3]>
#> 4 <tibble [1 x 3]>
#> 5 <tibble [1 x 3]>
#> 6 <tibble [1 x 3]>
#> 7 <tibble [1 x 3]>
#> 8 <tibble [1 x 3]>
#> 9 <tibble [1 x 3]>
#> 10 <tibble [1 x 3]>
#> # ... with 14 more rows
如果我们命名temp变量,则将其保留:
df %>% grouping_by(vars(X =row_number()))$nest()
#> # A tibble: 24 x 2
#> X data
#> <int> <list>
#> 1 1 <tibble [1 x 3]>
#> 2 2 <tibble [1 x 3]>
#> 3 3 <tibble [1 x 3]>
#> 4 4 <tibble [1 x 3]>
#> 5 5 <tibble [1 x 3]>
#> 6 6 <tibble [1 x 3]>
#> 7 7 <tibble [1 x 3]>
#> 8 8 <tibble [1 x 3]>
#> 9 9 <tibble [1 x 3]>
#> 10 10 <tibble [1 x 3]>
#> # ... with 14 more rows
这是另一种解决方法,完全避免了nest
:
df %>% as_tibble() %>% split(.,1:nrow(.)) %>% tibble(data =.)
#> # A tibble: 24 x 1
#> data
#> <list>
#> 1 <tibble [1 x 3]>
#> 2 <tibble [1 x 3]>
#> 3 <tibble [1 x 3]>
#> 4 <tibble [1 x 3]>
#> 5 <tibble [1 x 3]>
#> 6 <tibble [1 x 3]>
#> 7 <tibble [1 x 3]>
#> 8 <tibble [1 x 3]>
#> 9 <tibble [1 x 3]>
#> 10 <tibble [1 x 3]>
#> # ... with 14 more rows
您可能不需要as_tibble()
步骤,我用它来获得完全相同的输出,没有它,您将在小标题列表列中获得常规的data.frames。
100%的基本方式:
df2 <- data.frame(data = 1:nrow(df)) # initiate with proper number of rows
df2$data <- split(df, 1:nrow(df)) # assign list column
由于答案集中在效率上,因此效率会更高:
structure(list(data = split(df, rn <- seq_len(nrow(df)))),
row.names = rn, class = "data.frame")
创建nest.rowwise_df
可以将nest()
与rowwise()
一起使用,并使@ cj-yetman的想法成为可能:
nest.rowwise_df <- function(data, ..., .key = "data") {
df %>% group_by(`*temp*` = row_number()) %>% nest() %>% select(-`*temp*`)
}
df %>% rowwise() %>% nest()
#> # A tibble: 24 x 1
#> data
#> <list>
#> 1 <tibble [1 x 3]>
#> 2 <tibble [1 x 3]>
#> 3 <tibble [1 x 3]>
#> 4 <tibble [1 x 3]>
#> 5 <tibble [1 x 3]>
#> 6 <tibble [1 x 3]>
#> 7 <tibble [1 x 3]>
#> 8 <tibble [1 x 3]>
#> 9 <tibble [1 x 3]>
#> 10 <tibble [1 x 3]>
#> # ... with 14 more rows
或者使用第一个答案的软件包 tags 获得相同的结果:
using_rowwise$nest(df)
答案 1 :(得分:7)
我们可以使用group_split
在每一行进行拆分,并在每一行使用nest
。
library(tidyverse)
df %>%
group_split(row_number(), keep = FALSE) %>%
map_df(nest)
# A tibble: 24 x 1
# data
# <list>
# 1 <tibble [1 × 3]>
# 2 <tibble [1 × 3]>
# 3 <tibble [1 × 3]>
# 4 <tibble [1 × 3]>
# 5 <tibble [1 × 3]>
# 6 <tibble [1 × 3]>
# 7 <tibble [1 × 3]>
# 8 <tibble [1 × 3]>
# 9 <tibble [1 × 3]>
#10 <tibble [1 × 3]>
# … with 14 more rows
在keep = FALSE
中,我们不包括row_number()
的分组列。
现在代替row_number
,我们可以使用不同的变体将其按行拆分。
#Option 2
df %>% group_split(1:nrow(df), keep = FALSE) %>% map_df(nest)
#Option 3
df %>% group_split(seq_len(n()), keep = FALSE) %>% map_df(nest)
#Option 4
df %>% group_split(seq_len(nrow(df))) %>% map_df(nest)
答案 2 :(得分:5)
您可以使用purrr::transpose
,
这有点直观,
但是当我尝试了解其背后的逻辑时,却使我头昏脑胀:
tibble(data = lapply(transpose(df), as_tibble))
答案 3 :(得分:2)
假设您的真实数据与示例数据相似,嵌套可能不是您数据的正确策略。如果您坚持嵌套并且您的数据集很大,那么使用split
进行操作比使用transpose
进行处理更为有效。
nest
用例我真的不认为nest
是这里的正确选择。具有单行数据框的一个列表列的数据框与具有正常行的数据框基本相同,只是行被遮盖了。
link页面上描述的用例几乎可以肯定需要包含一个分组变量(类似于您的temp
变量)。例如:对于“钻石”数据集,嵌套color
。然后使用mutate
+ map
为每个子数据帧计算模型。然后取消嵌套model
:
library(tidyverse)
library(broom)
theme_set(theme_minimal())
dia_mods <- diamonds %>%
nest(-color) %>%
mutate(model = map(data, ~ lm(price ~ carat + clarity, .) %>% augment)) %>%
unnest(model)
优点在于,取消嵌套后,模型数据仍与分组变量相关联,这使得该数据更易于在ggplot中使用,等等。如果删除分组变量,则无法区分未嵌套的数据,这将使这样的事情变得不可能:
dia_mods %>%
ggplot(aes(x = carat, y = .fitted, color = clarity)) +
geom_line() +
facet_wrap(~ color) # facet by same grouping variable used to nest
如果您仍然打算将nest
与数据框一起使用,请考虑以下基准测试,我是使用原始数据框运行的:
以下是基准测试方法的说明:
split(as_tibble(df), 1:nrow(df))
,创建数据帧列表。我添加此内容是因为,对于您而言,我认为仅创建列表更有意义。as_tibble
。as_tibble
如您所见,最有效的解决方案包括分割小节或数据帧,最流行的答案也是效率最低的解决方案之一。这是因为转置数据在计算上是昂贵的。对于您的数据框来说,它并不太重要,但是在处理较大的数据集时,我会避免使用它。
当我们使用较大的数据集比较不同的策略时,情况会更加清晰。我使用比您的行多1x,25x,50x,75x,100x和125x的数据集对每种方法进行基准测试:
很明显,使用split
的方法效率更高,而使用transpose
的计算成本很高。就是说,transpose
方法似乎比其他方法损失的速度更大,这是不正确的。实际上,transpose
方法始终比最快的方法慢7倍,而与数据集的大小无关。当我们转换x任意y轴时,这一点变得更加清晰:
值得注意的是,尽管使用较小的数据集时,解决方案(使用temp
的性能相当慢,但随着数据集大小的增加,其性能将接近最有效解决方案的性能。
答案 4 :(得分:2)
在dplyr
内,人们可以像这样使用rowwise()
和do()
:
df %>% rowwise %>% do( nest(data.frame(.)) ) %>% ungroup
# or with less parentheses
df %>% rowwise %>% do( data.frame(.) %>% nest ) %>% ungroup
# test identical with `purrr::transpose`
identical(
df %>% rowwise %>% do(nest(data.frame(.))) %>% ungroup
,
tibble(data = lapply(transpose(df), as_tibble))
)
# [1] TRUE
其中rowwise()
将按行对data.frame
进行分组,并将每个组(行)显示为一个命名列表。
通常,这些“行组”仅对do()
和mutate()
之类的一些函数内部的语句“可见”,例如:
df %>% rowwise %>% nest # this nest will apply on the entire tibble
# # A tibble: 1 x 1
# data
# <list>
# 1 <tibble [24 × 3]>
查看通过管道%>%
传递的内容的技巧是将其str()
传递给do()
,尽管这会引起错误消息,因为data.frame
期望其中的函数返回df %>% rowwise %>% str(.)
# Classes ‘rowwise_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 24 obs. of 3 variables:
# $ Var1: int 0 1 2 3 0 1 2 3 0 1 ...
# $ Var2: int 0 0 0 0 1 1 1 1 2 2 ...
# $ Var3: int 0 0 0 0 0 0 0 0 0 0 ...
# - attr(*, "out.attrs")=List of 2
# ..$ dim : int 4 3 2
# ..$ dimnames:List of 3
# .. ..$ Var1: chr "Var1=0" "Var1=1" "Var1=2" "Var1=3"
# .. ..$ Var2: chr "Var2=0" "Var2=1" "Var2=2"
# .. ..$ Var3: chr "Var3=0" "Var3=1"
df %>% rowwise %>% do(str(.))
# List of 3
# $ Var1: int 0
# $ Var2: int 0
# $ Var3: int 0
# List of 3
# $ Var1: int 1
# $ Var2: int 0
# $ Var3: int 0
# ...
# Error: Results 1, 2, 3, 4, 5, ... must be data frames, not NULL
# Call `rlang::last_error()` to see a backtrace
tidyr::nest()
由于data.frame
接受了rowwise
,而list
传递了一个命名列表,我们需要强制data.frame
到data.frame()
使用,例如{{ 1}},得出上面的答案。
如果目的是将dplyr::mutate()
应用于新创建的列表列,则可以完全避免使用nest()
,只需在变量dplyr::mutate()
之后使用rowwise
,并使用变量名,例如:
df.raw %>% rowwise %>% mutate(data = tibble(Var1, Var2, Var3) %>% list)
# Source: local data frame [24 x 4]
# Groups: <by row>
#
# # A tibble: 24 x 4
# Var1 Var2 Var3 data
# <int> <int> <int> <list>
# 1 0 0 0 <tibble [1 × 3]>
# 2 1 0 0 <tibble [1 × 3]>
# 3 2 0 0 <tibble [1 × 3]>
# 4 3 0 0 <tibble [1 × 3]>
# 5 0 1 0 <tibble [1 × 3]>
# 6 1 1 0 <tibble [1 × 3]>
# 7 2 1 0 <tibble [1 × 3]>
# 8 3 1 0 <tibble [1 × 3]>
# 9 0 2 0 <tibble [1 × 3]>
# 10 1 2 0 <tibble [1 × 3]>
# # … with 14 more rows
# compare the newly generated column `data` with `nest` generated
identical(
(
df.raw %>% rowwise %>% mutate(data = tibble(Var1, Var2, Var3) %>% list)
%>% select(data) %>% ungroup
)
,
tibble(data = lapply(transpose(df), as_tibble))
)
# [1] TRUE
例如,结合使用函数式编程样式和dplyr::unnest()
时,我们可以生成一个表来演示Legendre's three-square theorem。
给一个data.frame
,将Var1
,Var2
,Var3
列分别作为x
,y
,z
行,并添加列x^2
,y^2
,z^2
,n = x^2 + y^2 + z^2
。
three.square = function(x, y, z) {
tibble(
x^2,
y^2,
z^2,
n = x^2 + y^2 + z^2
)
}
df %>% rowwise %>%
mutate(three.square = three.square(Var1, Var2, Var3) %>% list)
# Source: local data frame [24 x 4]
# Groups: <by row>
#
# # A tibble: 24 x 4
# Var1 Var2 Var3 three.square
# <int> <int> <int> <list>
# 1 0 0 0 <tibble [1 × 4]>
# 2 1 0 0 <tibble [1 × 4]>
# 3 2 0 0 <tibble [1 × 4]>
# 4 3 0 0 <tibble [1 × 4]>
# 5 0 1 0 <tibble [1 × 4]>
# 6 1 1 0 <tibble [1 × 4]>
# 7 2 1 0 <tibble [1 × 4]>
# 8 3 1 0 <tibble [1 × 4]>
# 9 0 2 0 <tibble [1 × 4]>
# 10 1 2 0 <tibble [1 × 4]>
# # … with 14 more rows
# to "expand" the list-column, use `dplyr::unnest()`
df %>% rowwise %>%
mutate(three.square = three.square(Var1, Var2, Var3) %>% list) %>%
unnest(three.square)
# # A tibble: 24 x 7
# Var1 Var2 Var3 `x^2` `y^2` `z^2` n
# <int> <int> <int> <dbl> <dbl> <dbl> <dbl>
# 1 0 0 0 0 0 0 0
# 2 1 0 0 1 0 0 1
# 3 2 0 0 4 0 0 4
# 4 3 0 0 9 0 0 9
# 5 0 1 0 0 1 0 1
# 6 1 1 0 1 1 0 2
# 7 2 1 0 4 1 0 5
# 8 3 1 0 9 1 0 10
# 9 0 2 0 0 4 0 4
# 10 1 2 0 1 4 0 5
# # … with 14 more rows
答案 5 :(得分:2)
我们可以按行的顺序split
library(tidyverse)
df %>%
split(seq_len(nrow(.))) %>%
map_dfr(nest)
# A tibble: 24 x 1
# data
# <list>
# 1 <tibble [1 × 3]>
# 2 <tibble [1 × 3]>
# 3 <tibble [1 × 3]>
# 4 <tibble [1 × 3]>
# 5 <tibble [1 × 3]>
# 6 <tibble [1 × 3]>
# 7 <tibble [1 × 3]>
# 8 <tibble [1 × 3]>
# 9 <tibble [1 × 3]>
#10 <tibble [1 × 3]>
# … with 14 more rows
或者另一个选择是pmap
df %>%
pmap_dfr(., ~ tibble(...) %>%
nest)
# A tibble: 24 x 1
# data
# <list>
# 1 <tibble [1 × 3]>
# 2 <tibble [1 × 3]>
# 3 <tibble [1 × 3]>
# 4 <tibble [1 × 3]>
# 5 <tibble [1 × 3]>
# 6 <tibble [1 × 3]>
# 7 <tibble [1 × 3]>
# 8 <tibble [1 × 3]>
# 9 <tibble [1 × 3]>
#10 <tibble [1 × 3]>
# … with 14 more rows
答案 6 :(得分:1)
这应该做到。得到相同的结果
library(purrr)
tibble(data = map(split(df,1:nrow(df)),tibble) )
或者使用plurrrlyr更加优雅
library("purrrlyr")
df %>% by_row(tibble,.to="data") %>% select(data)