2017-06-12 173 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子句覆蓋它。 –