【问题标题】:Rbind list of vectors with differing lengthsRbind 不同长度的向量列表
【发布时间】:2018-12-31 08:23:45
【问题描述】:

我是 R 新手,我正在尝试构建频率/严重性模拟。除了为 700 个位置中的每一个进行 10000 次模拟需要大约 10 分钟之外,一切都运行良好。 为了模拟一个单独的位置,我得到了一个长度不同的向量列表,我想有效地 rbind 这些向量,为所有不存在的值填充 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

【问题讨论】:

    标签: r performance dplyr data.table rbind


    【解决方案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 函数,所以如果你以不同的顺序加载包,这样的代码会在没有警告的情况下给出不同的结果。

    【讨论】:

    • 非常感谢您的帮助!现在,一切都运行良好,运行整个模拟只需要大约 40 秒。我使用了单一看涨期权。但是,我更改了代码,这样我就不必在最后转置矩阵。这使频率/严重性建模再次变得更快。因为现在填写的严重性有点不同,所以结果略有不同,但这只是蒙特卡罗错误。还是有什么特别的原因让你一开始就以“错误的方式”创建了矩阵?
    • @Leon231000 很酷,很高兴听到它有帮助!是的,由于 R 如何处理向量和矩阵,我只是以这种方式创建它:m[w] &lt;- vv 的值按“列主要”顺序放入m。 (我已经在上面编辑了那个解释。)
    【解决方案2】:

    我们可以在末尾附加NAs 以使每个list 元素的length 相同,然后执行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)
    

    【讨论】:

    • 非常感谢您的帮助!以后我一定会记住这两个选项!
    猜你喜欢
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    • 2013-08-13
    • 2012-10-18
    • 1970-01-01
    相关资源
    最近更新 更多