2017-06-12 136 views
0

我有一个三列id,dtstart,dtend的数据表。例如:按日期分组ID

id start  end 
1 01/01/2015 31/01/2015 
1 02/02/2015 28/02/2015 
1 01/07/2016 31/07/2016 
1 01/08/2016 31/08/2016 
2 01/03/2015 31/03/2015 
2 01/04/2015 30/04/2015 
2 01/02/2016 28/02/2016 
2 01/03/2016 31/03/2016 
... 

我需要创建的ID与同列,但新的开始日期是在原起始日期的最小日期和新的结束日期分组的另一个数据表是在原来的最大日期DTEND。

如果在结束日期和下一个开始日期之间有一天的休息时间超过一天,那么它应该单独分组。

例如,对于新表上面会:

id start  end 
1  01/01/2015 28/02/2015 
1  01/07/2016 31/08/2016 
2  01/03/2015 30/04/2016 
2  01/02/2016 31/03/2016 
... 

我需要一个for循环或者是有一个更有效的方法(数据表分组为例)?该表超过2000万行,包含100k +独特的ID。

干杯 安德鲁

+0

我想,这可能让你去:[收起行范围重叠(https://stackoverflow.com/questions/41747742/collapse-rows-with-overlapping-ranges) – Henrik

回答

0

这可以通过使用dplyr

dt.new <- dt %>% 
      arrange(id, start, end) %>% 
      mutate(gr = cumsum(lag(id, default = min(id)) != id | 
         as.numeric(difftime(start, lag(end, default = first(start)), units = 'days')) > 1)) %>% 
      group_by(id, gr) %>% 
      summarise(start = first(start), 
        end = last(end)) 

结果做的是:

Source: local data frame [6 x 4] 
Groups: id [?] 

    id gr  start  end 
    <int> <int>  <dttm>  <dttm> 
1  1  0 2015-01-01 2015-01-31 
2  1  1 2015-02-02 2015-02-28 
3  1  2 2016-07-01 2016-08-31 
4  2  3 2015-03-01 2015-04-30 
5  2  4 2016-02-01 2016-02-28 
6  2  5 2016-03-01 2016-03-31  

这工作,你的输出,因为你申请了为期一天的不匹配保证金(如果您需要两天的保证金,则从>1切换到>2),2016年是闰年,这是R的内部日历。因此2016年2月28日至2016年3月1日期间的保证金为2天。

+0

谢谢。 cumsum只是为我制作新手。我试过as.numeric(as.Date(start,format =“%Y-%m-%d”) - lag(as.Date(end,format =“%Y-%m-%d”)) > 1),但第一个grp条目是NA,因此在分组和汇总时包含。 –

+0

您必须在'lag()'中添加一个'default'参数。我更新了我的代码。 – akash87

+0

谢谢@ akash87。只有一个错误,当它应该是一个时,它分成两个。例如,如果第3行是:2007年5月2日05/02/2007。这一行是自行拆分的。 –

0

再次感谢@ akash87

例如下面一行是6个月内,所以它应该仍然2006年1月2日返回一行ID 1至30/09/2006,但它打破了一分为二,首先,从2006年1月2日至2006年12月6日,然后从2006年1月7日至30/09/2016

id dtstart  dtend 
1 01/02/2006 28/02/2006 
1 01/03/2006 31/03/2006 
1 01/04/2006 30/04/2006 
1 01/05/2006 31/05/2006 
1 01/06/2006 30/06/2006 
1 10/06/2006 12/06/2006 
1 01/07/2006 31/07/2006 
1 01/08/2006 31/08/2006 
1 01/09/2006 30/09/2006 
2 01/04/2006 30/04/2006 
2 01/05/2006 31/05/2006 
2 01/09/2006 30/09/2006 
2 01/10/2006 31/10/2006 

因此而不是返回

id start  end 
1 01/02/2006 30/09/2006 
2 01/04/2006 31/05/2006 
2 01/09/2006 31/10/2006 

我们有

id start  end 
1 01/02/2006 12/06/2006 
1 01/07/2006 30/09/2006 
2 01/04/2006 31/05/2006 
2 01/09/2006 31/10/2006 

安德鲁

+0

AHHH这提出了一个不同的问题:您正在同一列中查找其他日期范围内的日期范围。 – akash87

+0

一个不垂直的优雅,为什么我想是通过总结(...结束=最大(结束))替换总结(...结束=最后(结束))新表再运行它? –

+0

我用cumsum得到的日期范围条件(lag(id,default = min(id))!= id |!(dtstart%in%min(dtstart):max(dtend))),然后添加difftime子句覆盖它。 –