绑定具有不同长度的向量的列表

时间:2018-07-23 19:53:19

标签: r performance dplyr data.table rbind

我是R的新手,我正在尝试建立频率/严重性仿真。一切工作正常,除了需要花费大约10分钟的时间对700个位置进行10000次仿真。 为了模拟一个单独的位置,我得到了一个长度可变的向量列表,我想有效地绑定这些向量,并为所有不存在的值填写NA。我希望R将data.frame返回给我。 到目前为止,在将列表中的向量转换为1行矩阵之后,我使用了rbind.fill.matrix。但是,我希望可以使用诸如bind_rows(dplyr)或rbindfill之类的东西,但是我不知道如何将向量转换为可用于这些功能的东西。预先感谢您的帮助!

set.seed(1223)

library(data.table)

numsim = 10

rN.D <- function(numsim) rpois(numsim, 4) 
rX.D <- function(numsim) rnorm(numsim, mean = 5, sd = 4)

freqs <- rN.D(numsim)
obs <- lapply(freqs, function(x) rX.D(x))
#obs is the list that I would like to rbind (efficiently!) and have a data.frame returned to me

2 个答案:

答案 0 :(得分:1)

我们可以在末尾附加NA,使每个length元素的list相同,然后执行rbind

out <- do.call(rbind, lapply(obs, `length<-`, max(lengths(obs))))
as.data.frame(out) # if we need a data.frame as output

或使用tidyverse

library(tidyverse)
obs %>%
   set_names(seq_along(.)) %>% 
   stack %>% 
   group_by(ind) %>% 
   mutate(Col = paste0("Col", row_number())) %>% 
   spread(Col, values)

答案 1 :(得分:1)

  

所有工作都很好,只是需要[太长]来进行[numsim]模拟

如果您的真实应用程序使用rnorm或类似的名称,则可以对其进行一次调用:

set.seed(1223)
numsim = 3e5
freqs = rN.D(numsim)
maxlen = max(freqs)
m = matrix(, maxlen, numsim)
m[row(m) <= freqs[col(m)]] <- rX.D(sum(freqs))

res = as.data.table(t(m))

我正在“错误地”填充数据(每次模拟都在列而不是行上),然后进行转置,因为R使用"column-major" order填充矩阵值。


如果您需要使用lapply,这是最后一步的基准:

set.seed(1223)

library(dplyr); library(tidyr); library(purrr)
library(data.table)

numsim = 3e5

rN.D <- function(numsim) rpois(numsim, 4) 
rX.D <- function(numsim) rnorm(numsim, mean = 5, sd = 4)

freqs <- rN.D(numsim)
obs <- lapply(freqs, function(x) rX.D(x))

system.time({
tidyres = obs %>%
   set_names(seq_along(.)) %>% 
   stack %>% 
   group_by(ind) %>% 
   mutate(Col = paste0("Col", row_number())) %>% 
   spread(Col, values)
})
#    user  system elapsed 
#   16.56    0.31   16.88     

system.time({
    out <- do.call(rbind, lapply(obs, `length<-`, max(lengths(obs))))
    bres = as.data.frame(out)
})
#    user  system elapsed 
#    0.50    0.05    0.55 

system.time(
    dtres <- setDT(transpose(obs))
)
#    user  system elapsed 
#    0.03    0.01    0.05 

与其他两种方法相比(从@akrun的答案中得出),最后一种方法最快。

评论。我建议仅使用data.table或tidyverse。混合和匹配将很快变得混乱。在设置此示例时,我看到purrr拥有自己的transpose函数,因此,如果以不同的顺序加载软件包,则这样的代码可能会在没有警告的情况下给出不同的结果。