2017-09-27 8 views
8

私は非常に大きな時系列を持っており、最初の任意の値に基づいて異なる時系列を作成し、現在の期間を変更する必要があります。実際のデータセットでは、この変更は、データフレームの他の変数に依存するが、次のようにMWEの目的のために、私はそれを再作成:Rプログラミング:各行を更新するために以前に計算された行を使用する

initial_value <- 100 
set.seed(123) 
library(data.table) 
df <- as.data.table(data.frame(num = c(1:10),change = rnorm(10))) 

新しい変数valueは前期に独自の値として定義されるプラス現在の期間のchange第1の観察における値は、任意に選択されたinitial_valueによって決定される。 valueには制限がなかった場合は、これはdata.tableを使用して非常に高速であるだけで

df <- df[, value0 := initial_value + cumsum(change)] 

として作成することができます。しかし、残念ながら、changeは前の期間の実際のvalueに依存することがあります。具体的には、102に達する度に次の期間にシリーズがinitial_valueに到達し、3つの期間にわたってそこにとどまる必要があるとします。

df$value <- NA 
df$value[1] <- initial_value + df$change[1] 
for (i in 2:nrow(df)) { 
    if (is.na(df$value[i])) { 
    if (df$value[i-1] < 102) { 
     df$value[i] <- df$value[i-1] + df$change[i] 
    } else { 
     df$value[i:(i+2)] <- initial_value 
    } 
    } 
} 
:私はこの結果を生み出すことに成功し

num  change value0  value 
1: 1 -0.56047565 99.43952 99.43952 
2: 2 -0.23017749 99.20935 99.20935 
3: 3 1.55870831 100.76806 100.76806 
4: 4 0.07050839 100.83856 100.83856 
5: 5 0.12928774 100.96785 100.96785 
6: 6 1.71506499 102.68292 102.68292 
7: 7 0.46091621 103.14383 100.00000 
8: 8 -1.26506123 101.87877 100.00000 
9: 9 -0.68685285 101.19192 100.00000 
10: 10 -0.44566197 100.74626 99.55434 

これまでのところ唯一の方法は、ループを使用している:したがって、次のデータフレームでは、私は上記のコードはvalue0を生産しながら、変数valueを作成する必要があります

しかし、何十万もの観測をループするのは非常に遅いです。可能性としてベクトル化する方法や、単にプロセスをより効率的に実行する方法はありますか?

答えて

6

単純なループではRcppを使用することをお勧めします。要求されたロジックを簡単に複製できます。
あなたの関数:C++で

fun_r <- function(){ 
    df$value <- NA 
    df$value[1] <- initial_value + df$change[1] 
    for (i in 2:nrow(df)) { 
    if (is.na(df$value[i])) { 
     if (df$value[i-1] < 102) { 
     df$value[i] <- df$value[i-1] + df$change[i] 
     } else { 
     df$value[i:(i+2)] <- initial_value 
     } 
    } 
    } 
    df 
} 

同じ機能

library(Rcpp) 
cppFunction({' 
    NumericVector fun_c(NumericVector change, double init, double thr){ 
    int n = change.size(); 
    int end; 
    NumericVector out(n); 
    out[ 0 ] = init + change[ 0 ]; 

    for(int i = 1; i < n; i++){ 

    if(out[ i - 1 ] < thr){ 

     out[i] = out[ i - 1 ] + change[ i ]; 

    } else { 

     end = std::min(i + 2 , n - 1); 
     for(int j = i; j <= end; j++) { 
     out[ j ] = init; 
     i = j; 
     } 
    } 

    } 
    return out; 
} 
'}) 

UPDATE:最初に書かれた R機能(上記)に非常に非効率的な方法であるdata.frameサブセット、に基づくものですRのデータを処理する機能は、単にすべてのベンチマークで失うと予想される弱者です。ルーピング中は、常にベクトル化(ベクトルと行列)をベクトル化する必要があります。関数の下Rcpp例とより競合的である:

fun_r2 <- function(change, initial_value, thr){ 
    n <- length(change) 
    value <- numeric(n) 
    value[1] <- initial_value + change[1] 

    for (i in 2:n) { 
    if (value[i]==0) { 
     if (value[i-1] < thr) { 
     value[i] <- value[i-1] + change[i] 
     } else { 
     value[i:(i+2)] <- initial_value 
     } 
    } 
    } 
    value 
} 

3つの関数が同じ結果を生成し、fun_cは最速であるが、ベクトル化fun_r2関数が許容されると考えることができます。

df$value <- fun_r() 
df$value_r2 <- fun_r2(as.vector(df$change), init=100, thr=102) 
df$value_rcpp <- fun_c(df$change, init=100, thr=102) 

all.equal(df$value, df$value_rcpp) 
all.equal(df$value, df$value_r2) 
# TRUE 

mb <- microbenchmark::microbenchmark(
    fun_r(), 
    fun_r2(as.vector(df$change), init=100, thr=102), 
    fun_c(df$change, init=100, thr=102), 
    times=100L 
) 

# expr  mean 
# 1 fun_r() 6650.72481 
# 2 fun_r2() 42.28442 
# 3 fun_c() 18.24121 

お楽しみください!

+1

すごい!ありがとう!私は、Rcppループが非常に速く動作することは知らなかった。 –

関連する問題