2016-10-25 8 views
2

私は2つのパラメータ(日付とステータス)を持つdata.tableを持っています。元のテーブルに基づいて新しい列を挿入したいのですが。基本計算のグループ関数

データ・ルール:

  1. Status列が含まれている唯一の "0" と "1"
  2. 日付の欄には、常に秒:)

新しい変数によって増加されます。

  1. グループ:ステータスの各グループまたはサイクルに番号を付ける場合、ステータスの順番は(0,1)です。ステータスが「0」で始まり、ステータスが「0」になったときに1サイクルが完了したことを意味します。
  2. CYCLE_TIME:グループ毎
  3. group_0のサイクルタイムを計算する:特定のグループ内の状態0の時間を計算する
  4. GROUP_1:特定のグループ内のステータス1の時間を計算する

例えば、簡単な入力:

enter image description here

データを生成するコード:

dd <- data.table(date = c("2015-07-01 00:00:12", "2015-07-01 00:00:13","2015-07-01 00:00:14","2015-07-01 00:00:15", "2015-07-01 00:00:16", "2015-07-01 00:00:17","2015-07-01 00:00:18", "2015-07-01 00:00:19", "2015-07-01 00:00:20","2015-07-01 00:00:21", "2015-07-01 00:00:22", "2015-07-01 00:00:23","2015-07-01 00:00:24", "2015-07-01 00:00:25"), status = c(0,0,0,0,1,1,1,0,0,1,1,1,1,0)) 

新しいパラメータを含む出力は次のとおりです。

enter image description here

実際に私はいくつかの基本的な方法で行っている、

  1. 主なアイデアは、次のとおりです。現在のステータスは0とされている場合次のステータスは1で、それを1つのグループとしてマークします。
  2. アイデアはうまくいくかもしれませんが、問題は計算時間が長すぎます。

私はこの場合の簡単溶液

+0

を(http://stackoverflow.com/questions/5963269)[偉大R再現性の例を作るには?] – zx8754

答えて

2

だから1から0への遷移がグループの境界をマークが存在し得ることを想定。 cumsumdiffを使用すると、この機能を利用できます。より現実的な大き例えば

data.frame(x, group_id = c(1, cumsum(diff(x) == -1) + 1)) 
    x group_id 
1 0  1 
2 0  1 
3 0  1 
4 1  1 
5 1  1 
6 0  2 
7 0  2 
8 1  2 
9 0  3 

:@のzx8754の答えでx例えば、10万のレコードのため

res = data.frame(status = sample(c(0,1), 10e7, replace = TRUE)) 
system.time(res$group_id <- c(1, cumsum(diff(res$status) == -1) + 1)) 
    user system elapsed 
    2.770 1.680 4.449 
>  head(res, 20) 
    status group_id 
1  0  1 
2  0  1 
3  1  1 
4  0  2 
5  0  2 
6  0  2 
7  1  2 
8  1  2 
9  0  3 
10  1  3 
11  1  3 
12  0  4 
13  1  4 
14  0  5 
15  0  5 
16  1  5 
17  0  6 
18  0  6 
19  1  6 
20  0  7 

5秒は非常に高速である(それは速いのあなたの定義にもよるが、 :))。


ベンチマーク

set.seed(1) 
res = data.frame(status = sample(c(0,1), 10e4, replace = TRUE)) 

microbenchmark::microbenchmark(
    rleid = { 
    gr <- data.table::rleid(res$status) 
    x1 <- as.numeric(as.factor(ifelse(gr %% 2 == 0, gr - 1, gr))) 
    # removing "as.numeric(as.factor" helps, but still not as fast as cumsum 
    #x1 <- ifelse(gr %% 2 == 0, gr - 1, gr) 
    }, 
    cumsum = { x2 <- c(1, cumsum(diff(res$status) == -1) + 1) } 
) 

# Unit: milliseconds 
# expr  min   lq  mean  median   uq  max neval cld 
# rleid 118.161287 120.149619 122.673747 121.736122 123.271881 168.88777 100 b 
# cumsum 1.511811 1.559563 2.221273 1.826404 2.475402 6.88169 100 a 

identical(x1, x2) 
# [1] TRUE 
+1

私はgroup_idが次の0まで同じであるべきだと思います。すなわち、 '0,0,1,0,1'は' 1,1,1,2,2'でなければなりません。 – zx8754

+0

こんにちは、このアイデアのおかげで、この場合のグループの定義を見てください。あなたのデータに基づいて、1から3までの行は同じグループ1にあり、4番目と5番目の行は2番目のグループです:) – ZAWD

+0

@ zx8754正確に:)感謝! – ZAWD

2

このお試しください:

#dummy data 
x <- c(0,0,0,1,1,0,0,1,0) 

#get group id using rleid from data.table 
gr <- data.table::rleid(x) 

#merge separated 0,1 groups 
gr <- ifelse(gr %% 2 == 0, gr - 1, gr) 

#result 
cbind(x, gr) 
#  x gr 
# [1,] 0 1 
# [2,] 0 1 
# [3,] 0 1 
# [4,] 1 1 
# [5,] 1 1 
# [6,] 0 3 
# [7,] 0 3 
# [8,] 1 3 
# [9,] 0 5 

#if we need to have group names sequential then 
cbind(x, gr = as.numeric(as.factor(gr))) 
#  x gr 
# [1,] 0 1 
# [2,] 0 1 
# [3,] 0 1 
# [4,] 1 1 
# [5,] 1 1 
# [6,] 0 2 
# [7,] 0 2 
# [8,] 1 2 
# [9,] 0 3 
+1

+1、上記のdiffと 'cumsum'を使った私の解はコードがずっと短く、おそらくもっと高速です(私はこれをテストしませんでしたが)? –

+0

こんにちは、ソリューションのおかげで、私のソリューションよりもまだ高速です:)ありがとう! – ZAWD

関連する問題