2015-10-07 44 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