2016-08-11 4 views
2

他の3つの変数に応じてカウンタ変数を作成する必要があります。複数の条件でユーザーごとの累積カウンタ変数を作成する

これはこの問題の拡張質問です。 extension question 複数の消費者がAmazonで注文した状況を考えてみましょう。私は各ユーザーの成功した注文時間を数えたいと思う。注文が正常に行われた場合、カウンタ変数には自己プラス1が、失敗した注文の場合はカウンタが変わりません。明らかに、カウンター変数は時間、注文状況、ユーザーに依存します。

tが同じであるが注文ステータスが異なる場合のシナリオを考えてください。これは、行が重複していることを意味するものではなく、異なる他の列もあります。

DT <- data.table(time=c(1,2,2,2,1,1,2,3,1,1),user=c(1,1,1,1,2,3,3,3,4,4), order_status=c('f','f','t','t','f','f','t','t','t','t')) 
DT 

希望のカウンタ出力は次のとおりです。 '出力'はカウンタ変数です。

time user order_status output 
1: 1 1   f  0 
2: 2 1   f  0 
3: 2 1   t  1 
4: 2 1   t  1 
5: 1 2   f  0 
6: 1 3   f  0 
7: 2 3   t  1 
8: 3 3   t  2 
9: 1 4   t  1 
10: 1 4   t  1 

答えて

4

ここでの主な課題は、その後、それがuserでグループ化されたシンプルな累積和だtime, user, order_status=='t' 1へのあらゆる組み合わせの最初に出現を設定することです。

方法1:

DT[, id := 0L 
    ][order_status == "t", id := c(1L, rep(0L, .N-1L)), by=names(DT) 
    ][, id := cumsum(id), by=user] 

2行目はここ1のみorder_status == "t"によって最初の発生をマーク

ここdata.tableを用いてこれを達成する二つの方法です。

DT[, id := 0L      # set entire id col to 0 
    ][order_status == "t",   # then, where order status is true 
     id := c(1L, rep(0L, .N-1L)), # set (or update) first value to 1 
     by = names(DT)    # for every time,user,order_status 
    ][, id := cumsum(id),   # then, get cumulative sum of id 
     by = user]     # for every user 

方法2:

鉱山の多くのコメント生産コードは次のようになりますdata.tableのを使用してを更新+参加:

DT[, id := 0L 
    ][DT, id := as.integer(order_status == "t"), mult="first", on=names(DT) 
    ][, id := cumsum(id), by=user] 

2番目のステップは方法1と同じですが、最初のオカレンスを直接識別し、それをに更新しますの場合order_status == "t"は、結合ベースのサブセットで更新を実行します。内部のDTunique(DT)に置き換えて、冗長性を取り除くことができます。

私がしなければならないのは、それぞれのグループのためにrep()を作成すると、結合+更新とは対照的に、かなり高速でなければならないので、しかし、私は2番目の方法をより理解して、の実際の操作が何であるかを特定することができます。数週間後にコードを見た方がより重要だと思います。

+0

joinの代わりに、おそらく 'which'はより速く同等に読み込み可能ですか? '' DT '、 '' v' = 0L ''' DT'は '' TRUE ''、 '' TRUE ''、=私のために 'cumsum'よりも。 – Frank

+0

c(1L、rep(0L、.N-1L))の 'L'とは何ですか? – alphabetagamma

+1

@Frank、 'DT [order_status ==" t "、which = TRUE]'は単純に '(order_status =="これは 'order_status ==" t "'( '' 'は内部的に使われている)と等価ですが、' 'r''は' 't ''の後の' 'f'' – Arun

1

最も読みやすい方法はおそらくサブクエリです。

library(data.table) 
library(dplyr) 
DT <- data.table(time=c(1,2,2,2,1,1,2,3,1,1),user=c(1,1,1,1,2,3,3,3,4,4), order_status=c('f','f','t','t','f','f','t','t','t','t')) 
DT %>% left_join(
    DT %>% 
    filter(order_status == "t") %>% 
    group_by(user, time) %>% 
    summarise() %>% 
    arrange(time) %>% 
    mutate(output = row_number()), 
    by = c("user", "time")) %>% 
    mutate(output = ifelse(is.na(output), 0, output)) 

tidyrを使用してNBあなたはreplace_na(list(output = 0))で最後mutateを置き換えることができます。

2

data.tableを用いた簡単なアプローチがある:

DT[,output := cumsum(order_status=="t" & !duplicated(cbind(time,user,order_status))) 
    ,by=.(user)] 

    time user order_status output 
1: 1 1   f  0 
2: 2 1   f  0 
3: 2 1   t  1 
4: 2 1   t  1 
5: 1 2   f  0 
6: 1 3   f  0 
7: 2 3   t  1 
8: 3 3   t  2 
9: 1 4   t  1 
10: 1 4   t  1 

このアプローチは、基本的に任意の「F」の値の最後の「T」値に満たします。すべての "f"値を0にしたい場合は、それも十分に簡単です。をby=.(user,order_status)に変更してください。