4

R と、さらに重要なベクトル化については、まだ耳の後ろにかなり濡れているため、以下のコードを高速化する方法について頭を悩ませることはできません。

for ループは、種子ごとにランダムな確率を適用することにより、種子を生成する植物の密度が異なるいくつかの道路セグメントの道路に落ちる種子の数を計算します。私の実際のデータ フレームには最大 20 万行あり、シード数はセグメントあたり最大 30 万なので、現在のマシンで以下の例を使用すると数時間かかります。

#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 ループでは、別の作成者によって記録された距離の分布からランダムな値を抽出します (そのため、ここでデータを提供できません)。SeedRain 距離の可能性が高い値を持つ例示的なベクトルを追加しました。

4

2 に答える 2

5

これは、ほぼ同じシミュレーションを実行する必要があります。

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
于 2013-03-08T17:41:18.310 に答える
4

1 つのオプションはsample()、すべてSeedsの行ごとに一度に生成するdfことです。

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

ここでの注意点は次のとおりです。

  1. 関数がベクトル化されている場合、または 1 回の呼び出しで最終結果全体を生成できる場合は、関数をループ内で繰り返し呼び出さないようにしてください。ここでは、sample() Seedsの各行の時間をdf呼び出しており、各呼び出しは から 1 つのサンプルを返していSeedRainDistsます。ここでは、各行のsample()サンプル サイズを要求する 1 回の呼び出しを行います。したがって、10 回呼び出します。コードは 271500 回呼び出します。Seedsdfsample
  2. ループ内で関数を繰り返し呼び出す必要がある場合でも、ループの完了後に結果全体に対して実行できるベクトル化されたものはすべてループから削除してください。ここでの例は、多数回SeedsOut呼び出している の蓄積です。+()

    それぞれSeedsOutをベクトルに集めてから、sum()そのベクトルをループの外側に集めたほうがよかったでしょう。例えば

    SeedsOut <- numeric(length = x)
    for(i in seq_len(x)) {
      SeedsOut[i] <- ifelse(sample(SeedRainDists,1,replace=TRUE)>40,1,0)
    }
    sum(SeedOut)
    
  3. 0R は、logical を数値s または1数学関数で使用される s であるかのように扱うことに注意してください。したがって

    sum(ifelse(sample(SeedRainDists, 100, replace=TRUE)>40,1,0))
    

    sum(sample(SeedRainDists, 100, replace=TRUE)>40)
    

    同じで実行すると、同じ結果が得られset.seed()ます。

より少ない呼び出しを必要とするサンプリングを行うためのより洗練された方法があるかもしれませんsample()(そしてsample(SeedRainDists, sum(Seeds), replace = TRUE) > 40、そのベクトルの行ごとにそのベクトルの正しい要素を選択するように注意する必要がありますdf- 難しいことではなく、少し面倒です)。私が示すのは十分に効率的でしょうか?

于 2013-03-08T18:22:40.480 に答える