将月度状态数据汇总到序列数据

时间:2015-10-07 07:37:23

标签: r aggregate sequence

我想知道是否存在针对以下问题的简单解决方案:想象一下,处理有关某人是否工作(工作= 1)或工作= 0(工作= 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

由于我的数据量不是很小,因此非常感谢资源有效的解决方案。

编辑:用循环(也许是滞后函数)来完成任务会有效,但我正在寻找更加向量化的解决方案。

3 个答案:

答案 0 :(得分:4)

使用rleid v&gt; = 1.9.6(最新的稳定版本)中的data.table函数,这里有点类似的解决方案

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,从而避免了一次分组操作:

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][]

答案 1 :(得分: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

答案 2 :(得分: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