Performance 如何用每个data.frame行作用数千次的矢量化替换for循环?
关于R和更重要的矢量化,我还没有完全弄清楚如何加速下面的代码 for循环通过对每个种子应用随机概率,计算具有不同种子生成植物密度的多个路段落在道路上的种子数。 由于我的实际数据帧有大约200k行,种子数高达300k/段,在我当前的机器上使用下面的示例需要几个小时Performance 如何用每个data.frame行作用数千次的矢量化替换for循环?,performance,r,for-loop,vectorization,Performance,R,For Loop,Vectorization,关于R和更重要的矢量化,我还没有完全弄清楚如何加速下面的代码 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 &
#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)
})
#示例data.frame
df这应该做相同的模拟:
df$SeedsOnRoad2 <- sapply(df$Seeds,function(x){
rbinom(1,x,0.6)
})
# Density Seeds SeedsOnRoad SeedsOnRoad2
#1 0 0 0 0
#2 0 0 0 0
#3 0 0 0 0
#4 3 1500 892 877
#5 0 0 0 0
#6 120 60000 36048 36158
#7 300 150000 90031 89875
#8 120 60000 35985 35773
#9 0 0 0 0
#10 0 0 0 0
df$SeedsOnRoad2一个选项是为每行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
注意,在任何数学函数中,R都将逻辑视为数字0
s或1
s。因此
sum(ifelse(sample(SeedRainDists, 100, replace=TRUE)>40,1,0))
及
如果使用相同的set.seed()
运行,将给出相同的结果
可能有一种更奇特的采样方法,它需要更少的调用sample()
(还有,sample(SeedRainDists,sum(Seeds),replace=TRUE)>40
,但是您需要注意为df
的每一行选择该向量的正确元素-不难,只是有点麻烦),但是我展示的可能足够有效了?(+1)rbinom(…,0.6)
非常好的捕获谢谢你的快速回复,不得不稍微更新一下问题;我从您的回答中得出的结论是,最好根据概率(当前存储为向量)定义一个函数,并像您使用rbinom
@sir_husefugg那样应用该函数,这是完全正确的。在任何模拟中,您都希望生成输入数据,而不是从数据池中采样。生成数据(并使输出与观察到的数据一致)表明,您有一个强大的理论框架来描述您的系统,并为外推场景增加了有效性。rbinom
已经矢量化了wrtsize
:sapply
是unnecessary@hadley看了看医生,试了试。我认为你错了。一个小插曲:在中,如果(x>0)
,x
是一个向量,那么这可能不是你想要的。另外,如果您的所有数据都是数字,那么在处理性能问题时,坚持使用矩阵而不是数据帧通常是一个好主意。@joranx
不会是一个向量,因为整个输入是一列矩阵,apply()
在行上运行。@GavinSimpson啊,谢谢。我读得太快了。太棒了,也有点丢脸,非常感谢。我从来没有想过要把样本的数量与种子的总数相匹配。一旦我的头脑清醒了一点,我会尝试研究你的“麻烦”解决方案,因为现在你提供的解释肯定很有帮助。干得好
SeedsOut <- numeric(length = x)
for(i in seq_len(x)) {
SeedsOut[i] <- ifelse(sample(SeedRainDists,1,replace=TRUE)>40,1,0)
}
sum(SeedOut)
sum(ifelse(sample(SeedRainDists, 100, replace=TRUE)>40,1,0))
sum(sample(SeedRainDists, 100, replace=TRUE)>40)