【问题标题】:Fast method for combining list elements based on criteria基于条件组合列表元素的快速方法
【发布时间】:2017-11-27 21:43:51
【问题描述】:

我正在 R 中构建一个小函数,它从几个物种和几个地点测量大小,按地点组合所有数据(将许多物种集中在一起),然后计算这些组合数据的一些统计数据。

这里是一些简单的示例数据:

SiteID <- rep(c("D00002", "D00003", "D00004"), c(5, 2, 3))
SpeciesID <- c("CHIL", "CHIP", "GAM", "NZMS", "LUMB", "CHIL", "SIMA", "CHIP", "CHIL", "NZMS")
Counts <- data.frame(matrix(sample(0:99,200, replace = TRUE), nrow = 10, ncol = 20))
    colnames(Counts) <- paste0('B', 1:20)
spec <- cbind(SiteID, SpeciesID, Counts)
stat1 <- data.frame(unique(SiteID))
    colnames(stat1) <- 'SiteID'
    stat1$Mean <- NA

这是一个函数,它创建一个列表lsize1,其中每个列表元素是给定SpeciesID 在给定SiteID 中的大小(B1B20)的向量,相乘通过每个尺寸类别的计数。由此,该函数创建了一个列表lsize2,它结合了lsize1 中具有相同SiteID 的列表元素。最后,它获取lsize2 中每个元素的平均值(即每个SiteID 的个体的平均大小,与SpeciesID 无关),并将其作为结果输出。

fsize <- function(){
    specB <- spec[, 3:22]
    lsize1 <- apply(specB, 1, function(x) rep(1:20, x))
        names(lsize1) <- spec$SiteID
    lsize2 <- sapply(unique(names(lsize1)), function(x) unlist(lsize1[names(lsize1) == x], use.names = FALSE), simplify = FALSE)
        stat1[stat1$SiteID %in% names(lsize2), 'Mean'] <- round(sapply(lsize2, mean), 2)
    return(stat1)
}

在创建这个函数时,我遵循了这里的建议:combine list elements based on element names,这解决了我的问题的症结所在:根据一些共同的标准组合列表元素(在我的例子中,组合来自同一个 SiteID 的所有元素)。该功能按预期工作,但我的问题是是否有办法让它大大加快速度?

注意:对于我的实际数据集,长度约为 40,000 行,我发现该函数运行时间约为 0.7 秒,其中最耗时的步骤是创建 lsize2(约 0.5 秒)。我需要使用不同的数据排列和子集多次运行这个函数,所以我希望有一种方法可以显着缩短处理时间。

【问题讨论】:

    标签: r list apply sapply


    【解决方案1】:

    这里应该不需要循环。这是一次尝试:

    tmp <- data.frame(spec["SiteID"], sums = rowSums(specB * col(specB)), counts=rowSums(specB) )
    tmp <- aggregate(. ~ SiteID, tmp, sum)
    tmp$avg <- tmp$sums / tmp$counts
    tmp
    
    #  SiteID  sums counts      avg
    #1 D00002 46254   4549 10.16795
    #2 D00003 20327   1810 11.23039
    #3 D00004 29651   2889 10.26341
    

    比较:

    fsize()
    #  SiteID  Mean
    #1 D00002 10.17
    #2 D00003 11.23
    #3 D00004 10.26
    

    此代码实际上将每个值乘以它的索引 (col(specB)),然后将 aggregates 的总和和计数乘以 SiteID。该逻辑也应该相对可转移到其他方法(data.table/dplyr)。例如:在data.table:

    setDT(spec)
    spec[, .(avg = sum(.SD * col(.SD)) / sum(unlist(.SD))), by=SiteID, .SDcols=B1:B20]
    
    #   SiteID      avg
    #1: D00002 10.16795
    #2: D00003 11.23039
    #3: D00004 10.26341
    

    【讨论】:

    • 谢谢。这对平均值很有效。不过,我也希望对其他统计数据做类似的事情,例如中位数或标准差(事后看来,我应该在我的示例中包含这些)。对如何以类似方式计算这些有什么想法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    相关资源
    最近更新 更多