2017-03-23 14 views
3

IDごとに連続した#日間の非アクティブ(consecDaysInactive)をカウントしようとしています。条件付きでリセットされるIDでグループ化されたRでカウンタ変数を作成します。

私はすでに、idが非アクティブである日に1であり、アクティブであるときに0である指標変数inactiveを作成しました。私はまた、id変数と日付変数を持っています。私の分析データセットには数十万行のデータが含まれるため、効率が重要になります。

私が作成しようとしているロジックは次のとおりです。ユーザーがアクティブな場合、ユーザーがアクティブでない場合、、IDごと= 0 consecDaysInactive

  • IDごと

    • 、そして前日にアクティブでした、IDごとconsecDaysInactive = 1
    • 、ユーザーは、前の日にアクティブでない場合、consecDaysInactive = 1 +#前の連続した非アクティブな日
    • consecDaysInactiveは、IDの新しい値は0にリセットする必要があります。

    私は累積合計を作成することができましたが、> = 0の行=> 0の後にリセットすることができませんでした。

    私は(consecDaysInactive)の結果と、プログラムで達成できた結果(bad_consecDaysInactive)を下に示しました。

    library(dplyr) 
    d <- data.frame(id = c(1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2), date=as.Date(c('2017-01-01','2017-01-02','2017-01-03','2017-01-04','2017-01-05','2017-01-06','2017-01-07','2017-01-08','2017-01-01','2017-01-02','2017-01-03','2017-01-04','2017-01-05','2017-01-06','2017-01-07','2017-01-08')), inactive=c(0,0,0,1,1,1,0,1,0,1,1,1,1,0,0,1), consecDaysInactive=c(0,0,0,1,2,3,0,1,0,1,2,3,4,0,0,1)) 
    
    d <- d %>% 
        group_by(id) %>% 
        arrange(id, date) %>% 
        do(data.frame(., bad_consecDaysInactive = cumsum(ifelse(.$inactive==1, 1,0)) 
    ) 
    ) 
    d 
    

    各連続日の+1によってconsecDaysInactive反復非活動が、各日付、ユーザは、IDの新しい値について0からリセットアクティブである0にリセットします。出力が下に示すように、bad_consecDaysInactiveを0にリセットすることができません。行

      id  date inactive consecDaysInactive bad_consecDaysInactive 
         <dbl>  <date> <dbl>    <dbl>     <dbl> 
        1  1 2017-01-01  0     0      0 
        2  1 2017-01-02  0     0      0 
        3  1 2017-01-03  0     0      0 
        4  1 2017-01-04  1     1      1 
        5  1 2017-01-05  1     2      2 
        6  1 2017-01-06  1     3      3 
        7  1 2017-01-07  0     0      3 
        8  1 2017-01-08  1     1      4 
        9  2 2017-01-01  0     0      0 
        10  2 2017-01-02  1     1      1 
        11  2 2017-01-03  1     2      2 
        12  2 2017-01-04  1     3      3 
        13  2 2017-01-05  1     4      4 
        14  2 2017-01-06  0     0      4 
        15  2 2017-01-07  0     0      4 
        16  2 2017-01-08  1     1      5 
    

    私も考えられ(としようとした)group_by() & do()内の変数をインクリメントするが、do()が反復的ではないので、私が過去得るために、私のカウンターを取得することはできません2:

    d2 <- d %>% 
        group_by(id) %>% 
        do(data.frame(., bad_consecDaysInactive2 = ifelse(.$inactive == 0, 0, ifelse(.$inactive==1,.$inactive+lag(.$inactive), .$inactive)))) 
    d2 
    

    前述したように、得られたどの:

     id  date inactive consecDaysInactive bad_consecDaysInactive bad_consecDaysInactive2 
        <dbl>  <date> <dbl>    <dbl>     <dbl>     <dbl> 
    1  1 2017-01-01  0     0      0      0 
    2  1 2017-01-02  0     0      0      0 
    3  1 2017-01-03  0     0      0      0 
    4  1 2017-01-04  1     1      1      1 
    5  1 2017-01-05  1     2      2      2 
    6  1 2017-01-06  1     3      3      2 
    7  1 2017-01-07  0     0      3      0 
    8  1 2017-01-08  1     1      4      1 
    9  2 2017-01-01  0     0      0      0 
    10  2 2017-01-02  1     1      1      1 
    11  2 2017-01-03  1     2      2      2 
    12  2 2017-01-04  1     3      3      2 
    13  2 2017-01-05  1     4      4      2 
    14  2 2017-01-06  0     0      4      0 
    15  2 2017-01-07  0     0      4      0 
    16  2 2017-01-08  1     1      5      1 
    

    をあなたが見ることができるように、私のイテレータをbad_consecDaysInactive2リセット0になりますが、過去2には増えません! data.tableソリューションがあれば、私もそれを聞いてうれしいです。ここで

  • +0

    このような何かの

    d <- data.frame(inactive=a, id=id) t2 <- Sys.time() b <- setDT(d)[, v := if (inactive[1]) seq.int(.N) else 0L, by=rleid(inactive)] Sys.time()-t2 

    時間差? 'ライブラリ(data.table); ' – chinsoon12

    +0

    'ライブラリ(data.table); setDT(d)[、consecDaysInactive2:= cumsum(非アクティブ)、by =(id、cumsum(! – Frank

    +0

    ありがとう、chinsoon12とフランク - これらはどちらもうまくいきました。私は、これをdata.tableライブラリを探索する機会として利用します。 @@Frank、この投稿を重複としてマークすることに関して、私はあなたがマークした記事とは違うと思っていました。OPはdplyrでdata.table関数を使用する方法を求めています。値。私が試みていた操作は異なっていて、私はdplyrでdata.tableメソッドを要求していませんでした。 dplyrは私が試した方法でしたが、私の目標を達成できませんでした。再度、あなたの助けをありがとう。 – rsty

    答えて

    2

    は、forループでそれを行うにはキュートな方法です:

    a <- c(1,1,1,1,0,0,1,0,1,1,1,0,0) 
    b <- rep(NA, length(a)) 
    b[1] <- a[1] 
    for(i in 2:length(a)){ 
        b[i] <- a[i]*(a[i]+b[i-1]) 
    } 
    a 
    b 
    

    それは、これを行うための最も効率的な方法ではないかもしれないが、それはかなりくそ速くなります。私のコンピュータでは1千万の行に対して11.7秒。

    a <- round(runif(10000000,0,1)) 
    b <- rep(NA, length(a)) 
    b[1] <- a[1] 
    t <- Sys.time() 
    for(i in 2:length(a)){ 
        b[i] <- a[i]*(a[i]+b[i-1]) 
    } 
    b 
    Sys.time()-t 
    

    11.73612秒

    の時間差をしかし、これはIDごとに物事を行う必要を考慮していません。これは最小の効率のペナルティで簡単に修正できます。あなたのサンプルデータフレームはidでソートされます。実際のデータがまだソートされていない場合は、ソートします。その後:我々はそれがidをソートするために要した時間は含まれ

    a <- round(runif(10000000,0,1)) 
    id <- round(runif(10000000,1,1000)) 
    id <- id[order(id)] 
    b <- rep(NA, length(a)) 
    b[1] <- a[1] 
    t <- Sys.time() 
    for(i in 2:length(a)){ 
        b[i] <- a[i]*(a[i]+b[i-1]) 
        if(id[i] != id[i-1]){ 
        b[i] <- a[i] 
        } 
    } 
    b 
    Sys.time()-t 
    

    13.54373秒

    の時間差ならば、時間差は19秒に近いです。まだあまりにも悪くない!

    OPのコメントでフランクの答えを使ってどれくらい効率を上げることができますか? 2.233547秒

    +0

    ありがとう、Jacob!これは、データフレーム形式でそれを置くために軽微な微調整で働いた。これは、ループソリューションとしてはうってつけです。私のRはかなり錆びていますが、これは今後も役立ちます。 – rsty

    関連する問題