根据条件组合列表元素的快速方法
Fast method for combining list elements based on criteria
我正在 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
的函数,其中每个列表元素都是大小为(B1
到 B20
)的向量,对于给定的 SpeciesID
在给定的 SiteID
中乘以每个尺寸的计数 class。由此,该函数创建了一个列表 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 行,我发现函数 运行s 在 ~ 0.7 秒内完成,最耗时的步骤是创建 lsize2
(~ 0.5 秒)。我需要 运行 这个函数很多很多次,使用不同的排列和数据子集,所以我希望有一种方法可以显着缩短这个处理时间。
这里应该不需要任何循环。这是一种尝试:
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)
),然后 aggregate
将总和和计数乘以 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
我正在 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
的函数,其中每个列表元素都是大小为(B1
到 B20
)的向量,对于给定的 SpeciesID
在给定的 SiteID
中乘以每个尺寸的计数 class。由此,该函数创建了一个列表 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 行,我发现函数 运行s 在 ~ 0.7 秒内完成,最耗时的步骤是创建 lsize2
(~ 0.5 秒)。我需要 运行 这个函数很多很多次,使用不同的排列和数据子集,所以我希望有一种方法可以显着缩短这个处理时间。
这里应该不需要任何循环。这是一种尝试:
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)
),然后 aggregate
将总和和计数乘以 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