2015-10-07 43 views
3

我想知道是否有一個簡單的解決方案來解決以下問題:想象一下每個月的狀態信息是否有人工作(工作= 1)或不工作(工作= 0)。這說明了原始數據:將每月狀態數據彙總到序列數據

orig <- data.frame(id=c(rep(1:2, each=10)), 
       month.nr=c(rep(1:10,2)), 
       work.yn=c(0,1,1,0,0,0,1,1,1,0, 
         1,1,1,1,0,1,1,0,0,1)) 
id month.nr work.yn 
1  1  0 
1  2  1 
1  3  1 
1  4  0 
1  5  0 
1  6  0 
1  7  1 
1  8  1 
1  9  1 
1  10  0 
2  1  1 
2  2  1 
2  3  1 
2  4  1 
2  5  0 
2  6  1 
2  7  1 
2  8  0 
2  9  0 
2  10  1 

我在尋找一個簡單的函數或算法轉換數據只保留啓動和工作時段與由人(ID),該數字所得的序列數月之久。對於上面的示例所得到的數據是這樣的:

id month.start.work month.end.work sequence.nr 
1    2    3   1 
1    7    9   2 
2    1    4   1 
2    6    7   2 
2    10    10   3 

由於我的數據量不是那麼小的資源有效的解決方案是非常讚賞。

編輯:做一個循環(也許滯後函數)的任務將工作,但我正在尋找一個更加矢量化的解決方案。

回答

4

下面是一個使用rleid功能有點類似的解決方案在data.table V> = 1.9.6(最新的穩定版)

library(data.table) # v.1.9.6+ 
setDT(orig)[, indx := rleid(work.yn) 
      ][work.yn != 0, .(start = month.nr[1L], 
           end = month.nr[.N]), 
       by = .(id, indx) 
       ][, seq := 1:.N, 
       by = id][] 
# id indx start end seq 
# 1: 1 2  2 3 1 
# 2: 1 4  7 9 2 
# 3: 2 6  1 4 1 
# 4: 2 8  6 7 2 
# 5: 2 10 10 10 3 

上述的輕微變體無需首先創建index,從而避免了一個g rouping操作:

setDT(orig)[, if (work.yn[1L]) 
       .(start=month.nr[1L], end=month.nr[.N]), 
      by=.(id, rleid(work.yn)) 
      ][, seq := seq_len(.N), by=id][] 

或者,我們可以只使用range更短的代碼

setDT(orig)[, if (work.yn[1L]) as.list(range(month.nr)), 
      by = .(id, rleid(work.yn)) 
      ][, seq := seq_len(.N), by = id][] 
+2

非常感謝!非常直接,也是我需要的解決方案。新的rleid函數在這裏幫助很大。 – drosophilus

2

可以使用data.table包,這個小工具功能:

library(data.table) 

f = function(x, y) 
{ 
    r = rle(x) 

    end = y[cumsum(r$lengths)[!!r$values]] 
    start = end - r$lengths[!!r$values] + 1 

    list(month.start=start, month.end=end) 
} 

setDT(orig)[, f(work.yn,month.nr),id][, sequence.nr:=seq(.N),id][] 

# id month.start month.end sequence.nr 
#1: 1   2   3   1 
#2: 1   7   9   2 
#3: 2   1   4   1 
#4: 2   6   7   2 
#5: 2   10  10   3 
0

使用dplyr庫中的解決方案。

require("dplyr") 

orig %>% filter(work.yn == 1) %>% group_by(id) %>% 
    mutate(sequence.nr = cumsum(diff(c(-1, month.nr)) != 1)) %>% 
    group_by(id, sequence.nr) %>% mutate(start_mon = min(month.nr), 
             end_mon = max(month.nr)) %>% 
    select(-month.nr, -work.yn) %>% distinct 

# id sequence.nr start_mon end_mon 
# 1 1   1   2  3 
# 2 1   2   7  9 
# 3 2   1   1  4 
# 4 2   2   6  7 
# 5 2   3  10  10