Ian*_*ell 34 r dplyr data.table purrr tidyverse
我经常发现一些问题,人们不知何故以未命名字符向量的未命名列表结束,他们想将它们逐行绑定到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"
Run Code Online (Sandbox Code Playgroud)
一种典型的方法是使用do.call基础 R。
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
Run Code Online (Sandbox Code Playgroud)
也许一种效率较低的方法是Reduce从基础 R 开始。
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
Run Code Online (Sandbox Code Playgroud)
但是,当我们考虑更现代的包(例如dplyr或 )时data.table,可能会立即想到的一些方法不起作用,因为向量未命名或不是列表。
library(dplyr)
bind_rows(data)
#Error: Argument 1 must have names
Run Code Online (Sandbox Code Playgroud)
library(data.table)
rbindlist(data)
#Error in rbindlist(data) :
# Item 1 of input is not a data.frame, data.table or list
Run Code Online (Sandbox Code Playgroud)
一种方法可能是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
Run Code Online (Sandbox Code Playgroud)
但是,这似乎比实际需要的步骤多。
因此,我的问题是什么是有效tidyverse或data.table方法结合的未命名的列表无名特征向量成data.frame逐行?
tmf*_*mnk 15
不完全确定效率,但使用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
Run Code Online (Sandbox Code Playgroud)
mar*_*kus 11
编辑
使用@sindri_baldur的方法:https : //stackoverflow.com/a/61660119/8583393
一种方式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
Run Code Online (Sandbox Code Playgroud)
sin*_*dur 10
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
Run Code Online (Sandbox Code Playgroud)
这看起来相当紧凑。我相信这就是力量bind_rows()fromdplyr和map_df()in purrr,所以应该相当有效。
library(vctrs)
vec_rbind(!!!data)
Run Code Online (Sandbox Code Playgroud)
这给出了一个 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
Run Code Online (Sandbox Code Playgroud)
这似乎是.name_repair在内部tidyverse的方法是一个严重的瓶颈。我采用了一些相当简单的选项,这些选项似乎也从其他帖子中运行得最快(感谢 H 1 和 sindri_baldur)。
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)))
Run Code Online (Sandbox Code Playgroud)
但是如果你首先命名向量(但不一定是列表元素),你会得到一个不同的故事。
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)))
Run Code Online (Sandbox Code Playgroud)
事实上,您可以将命名向量的时间包含在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)))
Run Code Online (Sandbox Code Playgroud)
物有所值。
一个选项 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
Run Code Online (Sandbox Code Playgroud)
我的方法是将这些列表条目转换为预期类型
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
Run Code Online (Sandbox Code Playgroud)
如果您希望将数据类型从字符向量调整为适当的类型,那么lapply在这里也可以提供帮助。Firstlapply为每一行调用,secondlapply为每一列调用。
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
Run Code Online (Sandbox Code Playgroud)