2017-06-12 12 views
0

をベクトルでない-apply関数にforループの変換:各一意のIDに対して入力がデータフレームである私は、およそ次のようになります3列のデータ持っ

uid <- c(1,1,1,1,1,1,2,2,2) 
sale <- c(0,1,1,0,0,0,0,1,0) 
e <- as.data.frame(cbind(uid, sale)) 
e$uid <- as.factor(e$uid) 
e$sincesale <- NA 

を、私は同じ手順を適用したいです - 前回の販売からの日数を計算します。

これを行うことができるfor-loopを簡単に思い付くことができます。問題は、何百万行もあることです。したがって、この手順を完了するのに時間がかかります。 e$uidtapplyを使用しました。しかし、tapplyはベクトルを入力としてのみ受け入れます。

どのような代替方法(for-loopより高速です)を使用できますか?

私のforループ:

for (i in 2:length(e$uid)){ 
    #working within the good with the same unique id (uid) 
    if (e$uid[i] == e$uid[i-1]){ 
    if (e$sale[i]==1){ 
     sincesale[i] <- sincesale[i-1]+1 
    } 
    if (e$sale[i]==0){ 
     #if sale just ended, number of days since sale is 1 
     if (e$sale[i-1]==1){ 
     e$sincesale[i] <- 1 
     } 
     #if sale ended a few periods ago add 1 to previous value of "sincesale" 
     if (e$sale[i-1] == 0){ 
     e$sincesale[i] <- e$sincesale[i-1] + 1 
     } 
    } 
    } 
} 

UPD:

さて、私は正直、最後の夜と朝のために自分に取り組んでみましたが、新たな問題の解決策を考え出すことができませんでした。私は提案された方法を使ってみましたが、小さな問題の1つは、最初の行から "sincesale"を計算し始めるということです。(売上は最初から始まっていませんが、最初の行はtrueです。次の例では、入力は、forループ(「sincesale」)を用いた結果を生成し、提案dplyr(「sincesale4」)を使用して:

uid <- c(1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4) 
sale <- c(0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0) 
e <- as.data.frame(cbind(uid, sale)) 
e$uid <- as.factor(e$uid) 

    uid sale first sincesale sincesale4 
1 1 0  1  NA   0 
2 1 0  1  NA   1 
3 1 1  0  NA   1 
4 1 0  0   1   2 
5 1 0  0   2   3 
6 1 0  0   3   4 
7 2 0  1  NA   0 
8 2 1  1  NA   0 
9 2 0  0   1   1 
10 2 1  0  NA   1 
11 3 0  1  NA   0 
12 3 0  1  NA   1 
13 3 0  0  NA   2 
14 3 0  0  NA   3 
15 3 0  0  NA   4 
16 3 0  0  NA   5 
17 3 1  0  NA   5 
18 3 1  0  NA   5 
19 3 0  0   1   6 
20 4 0  1  NA   0 
21 4 0  1  NA   1 
22 4 0  0  NA   2 
+0

ちょうど 'e < - data.frame(uid、sale); e $ uid < - as.factor(e $ uid); e $ sincesale < - NA'は私がそれをソートするべきだと信じています。 – thelatemail

答えて

3

使用aveuidグループ内で見て、cumsumの累積和を取得します非販売日:

library(data.table) 
setDT(e) 
e[, sincesale3 := cumsum(!sale)-1, by=uid] 

または012:

e$sincesale2 <- ave(!e$sale, e$uid, FUN=cumsum)-1 

# uid sale sincesale sincesale2 
#1 1 0  NA   0 
#2 1 1  NA   0 
#3 1 1  NA   0 
#4 1 0   1   1 
#5 1 0   2   2 
#6 1 0   3   3 
#7 2 0  NA   0 
#8 2 1  NA   0 
#9 2 0   1   1 

が、これは可能でしょうdata.tableに翻訳@RonakShahのハットチップ付き:

library(dplyr) 
e %>% 
    group_by(uid) %>% 
    mutate(sincesale4 = cumsum(!sale)-1) 
関連する問題