Tidyverse方法通过row-do.call(rbind,x)等价物绑定未命名向量的未命名列表

Tidyverse方法通过row-do.call(rbind,x)等价物绑定未命名向量的未命名列表,r,dplyr,data.table,tidyverse,purrr,R,Dplyr,Data.table,Tidyverse,Purrr,我经常发现这样的问题:人们不知何故得到了一个未命名字符向量的未命名列表,他们希望将它们按行绑定到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"

我经常发现这样的问题:人们不知何故得到了一个未命名字符向量的未命名列表,他们希望将它们按行绑定到
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开始使用
Reduce

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
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 或<代码>数据>表< /代码>时,可能会立即想到的一些方法不起作用,因为向量是未命名的或不是列表。p>

library(dplyr)
bind_rows(data)
#Error: Argument 1 must have 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  
库(purrr)
映射(数据,~设置名称(.x,顺(.x)))
#一个tibble:3x5
#  `1`   `2`   `3`   `4`   `5`  
#      
#1A 14 7 12
#2B25815
#3C36918
然而,这似乎比需要的步骤更多


因此,我的问题是什么是有效的
tidyverse
data.table
方法来将未命名字符向量的未命名列表绑定到
data.frame
行?

不完全确定效率,但使用
purr
tible
的紧凑选项可以是:

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  
map\u-dfc(purrr::transpose(数据),~unlist(tible(.))
V1 V2 V3 V4 V5
1A 14 7 12
2B25815
3C36918
编辑

使用的方法:


使用
data.table
的方式,类似于@tmfmnk显示的方式

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\u加宽的选项

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    
库(TIBLE)
图书馆(tidyr)
图书馆(stringr)
TIBLE(列=数据)%>%
unnest_wide(c(col),name_repair=~str_c('value',seq_along(.))
#一个tibble:3x5
#值1值2值3值4值5
#           
#1A 14 7 12
#2B25815
#3C36918

我的方法是将这些列表条目转换为预期类型

rbindlist(lappy(数据,as.list))
#V1 V2 V3 V4 V5
#       
#1:A 14 7 12
#2:B25815
#3:C36918

如果您希望将数据类型从字符向量调整为适当的类型,那么
lappy
也可以在这里提供帮助。第一个
lappy
为每一行调用,第二个
lappy
为每一列调用

rbindlist(lappy(数据,as.list))[,lappy(.SD,type.convert)]
V1 V2 V3 V4 V5
1:A 14 7 12
2:B25815
3:C36918

这里是tmfmnk建议使用
as_tible_row()
将向量转换为单行tible的方法的一个细微变化。还需要使用
.name\u 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
库(purrr)
图书馆(tibble)
map_df(数据,作为_tible_行,.name_repair=~paste0(“值”,seq(.x)))
#一个tibble:3x5
值1值2值3值4值5
1A 14 7 12
2B25815
3C36918

这看起来相当紧凑。我相信这就是
bind_rows()
来自
dplyr
的力量,因此
map_df()
purr
中的力量,因此应该是相当有效的

库(vctrs)
vec_rbind(!!!数据)
这给出了一个数据帧

…1…2…3…4…5
1A 14 7 12
2B25815
3C36918

一些基准 似乎
tidyverse
方法中的
.name\u repair
是一个严重的瓶颈。我选择了一些相当简单的选项,这些选项似乎也是其他帖子中运行最快的(感谢H1和sindri_baldur)

microbenchmark(vctrs=vec\u rbind(!!!数据),
dt=rbindlist(lappy(数据,as.list)),
map=map_-df(数据,作为可存储行,.name_repair=“unique”),
base=as.data.frame(do.call(rbind,data)))

但是如果你先命名向量(但不一定是列表元素),你会得到一个不同的故事


data2我认为这可以添加到这个问题的一整套非常好的答案中:

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  
library(rlang)#或purrr
数据%>%
执行者(rbind,!!!)%>%
as_tible()%>%
设置名称(~字母[顺序(.))
#一个tibble:3x5
a、b、c、d、e
1A 14 7 12
2B25815
3C36918

作为旁注,
Reduce(rbind,
不能比
do.call(rbind,
更有效,因为
do.call
构造分配内存并复制数据一次,而
Reduce
构造重复分配新内存并重新复制所有以前的“
rbind
ed”元素。你说得很对。我没想到性能会像现在这样糟糕,在100000行上慢了6000倍。我编辑了这个问题,将其称为“效率较低的方法”。我只是用一些其他方法运行了一个基准测试。这在速度方面压倒了所有其他方法,并且是第一个真正超过
base::rbind()的方法
solution@dww是的,但是
setDF()
不同于
as.data.table()
/
as.data.frame()
@Adam,你认为你可以用新的解决方案更新你的基准吗?对于那些不知道如何
setDF()
/
setDT()
work then here is good post:您可以通过将名称设置为不需要粘贴的整数来进一步提高性能。可能类似于
vctrs::vec\u rbind(!!!lappy(数据,函数(x){attr(x,“名称”)是的,这比我刚才做的要快一点。但我同意。我很想在

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
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