提问人:Ian Campbell 提问时间:5/5/2020 最后编辑:user438383Ian Campbell 更新时间:9/28/2022 访问量:1947
按行绑定未命名向量的未命名向量列表的 Tidyverse 方法 - do.call(rbind,x) 等效项
Tidyverse approach to binding unnamed list of unnamed vectors by row - do.call(rbind,x) equivalent
问:
我经常发现一些问题,人们以某种方式最终得到了一个未命名的字符向量列表,他们想将它们逐行绑定到 .下面是一个示例:data.frame
library(magrittr)
data <- cbind(LETTERS[1:3],1:3,4:6,7:9,c(12,15,18)) %>%
split(1:3) %>% unname
data
#[[1]]
#[1] "A" "1" "4" "7" "12"
#
#[[2]]
#[1] "B" "2" "5" "8" "15"
#
#[[3]]
#[1] "C" "3" "6" "9" "18"
一种典型的方法是使用基数 R。do.call
do.call(rbind, data) %>% as.data.frame
# V1 V2 V3 V4 V5
#1 A 1 4 7 12
#2 B 2 5 8 15
#3 C 3 6 9 18
也许一种效率较低的方法是使用基础 R。Reduce
Reduce(rbind,data, init = NULL) %>% as.data.frame
# V1 V2 V3 V4 V5
#1 A 1 4 7 12
#2 B 2 5 8 15
#3 C 3 6 9 18
但是,当我们考虑更现代的包(例如 或)时,可能会立即想到的一些方法不起作用,因为向量未命名或不是列表。dplyr
data.table
library(dplyr)
bind_rows(data)
#Error: Argument 1 must have names
library(data.table)
rbindlist(data)
#Error in rbindlist(data) :
# Item 1 of input is not a data.frame, data.table or list
一种方法可能是在向量上。set_names
library(purrr)
map_df(data, ~set_names(.x, seq_along(.x)))
# A tibble: 3 x 5
# `1` `2` `3` `4` `5`
# <chr> <chr> <chr> <chr> <chr>
#1 A 1 4 7 12
#2 B 2 5 8 15
#3 C 3 6 9 18
但是,这似乎比需要的步骤更多。
因此,我的问题是,将未命名字符向量的未命名列表绑定到逐行的有效方法是什么?tidyverse
data.table
data.frame
答:
不完全确定效率,但使用和可能的紧凑选项是:purrr
tibble
map_dfc(purrr::transpose(data), ~ unlist(tibble(.)))
V1 V2 V3 V4 V5
<chr> <chr> <chr> <chr> <chr>
1 A 1 4 7 12
2 B 2 5 8 15
3 C 3 6 9 18
评论
tidyverse
data.table
编辑
使用 @sindri_baldur 的方法:https://stackoverflow.com/a/61660119/8583393
一种与 类似的方式,类似于@tmfmnk显示的data.table
library(data.table)
as.data.table(transpose(data))
# V1 V2 V3 V4 V5
#1: A 1 4 7 12
#2: B 2 5 8 15
#3: C 3 6 9 18
一个选项unnest_wider
library(tibble)
library(tidyr)
library(stringr)
tibble(col = data) %>%
unnest_wider(c(col), names_repair = ~ str_c('value', seq_along(.)))
# A tibble: 3 x 5
# value1 value2 value3 value4 value5
# <chr> <chr> <chr> <chr> <chr>
#1 A 1 4 7 12
#2 B 2 5 8 15
#3 C 3 6 9 18
我的方法是将这些列表条目转换为预期类型
rbindlist(lapply(data, as.list))
# V1 V2 V3 V4 V5
# <char> <char> <char> <char> <char>
#1: A 1 4 7 12
#2: B 2 5 8 15
#3: C 3 6 9 18
如果您希望将数据类型从字符向量调整为适当的类型,那么也可以在这里提供帮助。每行调用 First,每列调用 second。lapply
lapply
lapply
rbindlist(lapply(data, as.list))[, lapply(.SD, type.convert)]
V1 V2 V3 V4 V5
<fctr> <int> <int> <int> <int>
1: A 1 4 7 12
2: B 2 5 8 15
3: C 3 6 9 18
以下是 tmfmnk 建议的方法的细微变化,该方法用于将向量转换为单行提示。还需要使用以下参数:as_tibble_row()
.name_repair
library(purrr)
library(tibble)
map_df(data, as_tibble_row, .name_repair = ~paste0("value", seq(.x)))
# A tibble: 3 x 5
value1 value2 value3 value4 value5
<chr> <chr> <chr> <chr> <chr>
1 A 1 4 7 12
2 B 2 5 8 15
3 C 3 6 9 18
这似乎相当紧凑。我相信这就是 和 因此在 中 的权力,所以应该相当有效。bind_rows()
dplyr
map_df()
purrr
library(vctrs)
vec_rbind(!!!data)
这给出了一个 data.frame。
...1 ...2 ...3 ...4 ...5
1 A 1 4 7 12
2 B 2 5 8 15
3 C 3 6 9 18
一些基准
似乎方法内部是一个严重的瓶颈。我采取了一些相当简单的选项,这些选项似乎也是其他帖子中运行最快的(感谢 H 1 和 sindri_baldur)。.name_repair
tidyverse
microbenchmark(vctrs = vec_rbind(!!!data),
dt = rbindlist(lapply(data, as.list)),
map = map_df(data, as_tibble_row, .name_repair = "unique"),
base = as.data.frame(do.call(rbind, data)))
但是,如果你先命名向量(但不一定是列表元素),你会得到一个不同的故事。
data2 <- modify(data, ~set_names(.x, seq(.x)))
microbenchmark(vctrs = vec_rbind(!!!data2),
dt = rbindlist(lapply(data2, as.list)),
map = map_df(data2, as_tibble_row),
base = as.data.frame(do.call(rbind, data2)))
事实上,您可以将时间命名为向量到解决方案中,而不是其他向量,并且仍然可以看到相当高的性能。vec_rbind()
microbenchmark(vctrs = vec_rbind(!!!modify(data, ~set_names(.x, seq(.x)))),
dt = setDF(transpose(data)),
map = map_df(data2, as_tibble_row),
base = as.data.frame(do.call(rbind, data)))
为了它的价值。
评论
paste
vctrs::vec_rbind(!!!lapply(data,function(x){attr(x,"names") <- 1:5; x}))
vctrs
library(data.table)
setDF(transpose(data))
V1 V2 V3 V4 V5
1 A 1 4 7 12
2 B 2 5 8 15
3 C 3 6 9 18
评论
base::rbind()
setDF()
as.data.table()
as.data.frame()
setDF()
setDT()
我认为这可以添加到已经完整的这个问题的非常好的答案中:
library(rlang) # Or purrr
data %>%
exec(rbind, !!!.) %>%
as_tibble() %>%
set_names(~ letters[seq_along(.)])
# A tibble: 3 x 5
a b c d e
<chr> <chr> <chr> <chr> <chr>
1 A 1 4 7 12
2 B 2 5 8 15
3 C 3 6 9 18
评论
Reduce(rbind,
do.call(rbind,
do.call
Reduce
rbind