for循环通过对每个种子应用随机可能性来计算落入具有不同密度的种子生成植物的若干路段的道路上的种子数量.
由于我的真实数据帧有大约200k行,种子数量高达300k /段,使用下面的示例在我当前的机器上需要几个小时.
#Example data.frame df <- data.frame(Density=c(0,0,0,3,0,120,300,120,0,0)) #Example SeedRain vector SeedRainDists <- c(7.72,-43.11,16.80,-9.04,1.22,0.70,16.48,75.06,42.64,-5.50) #Calculating the number of seeds from plant densities df$Seeds <- df$Density * 500 #Applying a probability of reaching the road for every seed df$SeedsOnRoad <- apply(as.matrix(df$Seeds),1,function(x){ SeedsOut <- 0 if(x>0){ #Summing up the number of seeds reaching a certain distance for(i in 1:x){ SeedsOut <- SeedsOut + ifelse(sample(SeedRainDists,1,replace=T)>40,1,0) } } return(SeedsOut) })
如果有人可能会给我一个关于如何用矢量化代替循环的提示 – 或者可能首先如何更好地组织数据以提高性能 – 我将非常感激!
编辑:罗兰的回答表明我可能过于简化了问题.在for-loop中,我从另一位作者记录的距离分布中提取一个随机值(这就是我不能在这里提供数据的原因).添加了具有SeedRain距离的可能值的示例性矢量.
一个选项是在单个go中为每行df生成所有种子的sample().在基于循环的代码之前使用set.seed(1)我得到:
> df Density Seeds SeedsOnRoad 1 0 0 0 2 0 0 0 3 0 0 0 4 3 1500 289 5 0 0 0 6 120 60000 12044 7 300 150000 29984 8 120 60000 12079 9 0 0 0 10 0 0 0
如果我这样做,我会在很短的时间内得到相同的答案:
set.seed(1) tmp <- sapply(df$Seeds, function(x) sum(sample(SeedRainDists, x, replace = TRUE) > 40))) > tmp [1] 0 0 0 289 0 12044 29984 12079 0 0
为了比较:
df <- transform(df, GavSeedsOnRoad = tmp) df > df Density Seeds SeedsOnRoad GavSeedsOnRoad 1 0 0 0 0 2 0 0 0 0 3 0 0 0 0 4 3 1500 289 289 5 0 0 0 0 6 120 60000 12044 12044 7 300 150000 29984 29984 8 120 60000 12079 12079 9 0 0 0 0 10 0 0 0 0
这里要注意的要点是:
>如果函数是向量化的,或者可以通过一次调用生成整个最终结果,请尽量避免在循环中重复调用函数.在这里,您为每行df调用sample()种子时间,每次调用都返回SeedRainDists中的单个样本.在这里我做一个sample()调用,询问样本大小种子,每行df – 因此我调用样本10次,你的代码称它为271500次.
>即使你必须在循环中重复调用一个函数,也要从循环中删除在循环完成后可以对整个结果进行矢量化的任何内容.这里的一个例子是你的SeedsOut的累积,它调用()很多次.
更好的方法是在向量中收集每个SeedsOut,然后在循环外汇总()向量.例如.
SeedsOut <- numeric(length = x) for(i in seq_len(x)) { SeedsOut[i] <- ifelse(sample(SeedRainDists,1,replace=TRUE)>40,1,0) } sum(SeedOut)
>请注意,R将逻辑视为数字0或1,就像在任何数学函数中使用的那样.于是
sum(ifelse(sample(SeedRainDists, 100, replace=TRUE)>40,1,0))
和
sum(sample(SeedRainDists, 100, replace=TRUE)>40)
如果使用相同的set.seed()运行,则会得到相同的结果.
可能有更好的方式进行抽样,需要较少的sample()调用(并且样本(SeedRainDists,sum(Seeds),replace = TRUE)> 40但是你需要注意选择正确的元素对于df的每一行的那个向量 – 不是很难,只是一个轻微的麻烦),但我展示的可能足够有效?