Performance 如何用每个data.frame行作用数千次的矢量化替换for循环?

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 &

关于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 <- 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
    已经矢量化了wrt
    size
    :sapply
    是unnecessary@hadley看了看医生,试了试。我认为你错了。一个小插曲:在
    中,如果(x>0)
    x
    是一个向量,那么这可能不是你想要的。另外,如果您的所有数据都是数字,那么在处理性能问题时,坚持使用矩阵而不是数据帧通常是一个好主意。@joran
    x
    不会是一个向量,因为整个输入是一列矩阵,
    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)