【问题标题】:Populate a given time period/time sequence by IDs按 ID 填充给定的时间段/时间序列
【发布时间】:2019-07-31 15:37:29
【问题描述】:

假设一个像这样的df:

df <- data.frame(end_date = as.Date("2007-10-10", format = "%Y-%m-%d"))

    end_date
1 2007-10-10

我想生成从给定日期开始并返回 N 个月(在本例中为 11 个月)的每月日期序列。之后,我想为每 N 个月分配一个组 ID(在本例中为 2 个月)。我可以这样做:

library(tidyverse)
library(lubridate)

df %>%
 mutate(start_date = end_date %m-% months(11),
        date = list(seq.Date(start_date, end_date, by = "month"))) %>%
 unnest() %>%
 arrange(desc(date)) %>%
 mutate(ID = gl(ceiling(n()/3), 3, length = n()))

     end_date start_date       date ID
1  2007-10-10 2006-11-10 2007-10-10  1
2  2007-10-10 2006-11-10 2007-09-10  1
3  2007-10-10 2006-11-10 2007-08-10  1
4  2007-10-10 2006-11-10 2007-07-10  2
5  2007-10-10 2006-11-10 2007-06-10  2
6  2007-10-10 2006-11-10 2007-05-10  2
7  2007-10-10 2006-11-10 2007-04-10  3
8  2007-10-10 2006-11-10 2007-03-10  3
9  2007-10-10 2006-11-10 2007-02-10  3
10 2007-10-10 2006-11-10 2007-01-10  4
11 2007-10-10 2006-11-10 2006-12-10  4
12 2007-10-10 2006-11-10 2006-11-10  4

这是棘手的部分。我想让后续组的“end_date”成为前一组的“start_date”,并根据这一举动调整“start_date”,这意味着ID仍然具有相同的月数。我能够创建一个半功能解决方案:

df %>%
 mutate(start_date = end_date %m-% months(11),
        date = list(seq.Date(start_date, end_date, by = "month"))) %>%
 unnest() %>%
 arrange(desc(date)) %>%
 mutate(ID = gl(ceiling(n()/3), 3, length = n())) %>%
 group_by(ID) %>%
 summarise(start_date = min(date),
           end_date = max(date)) %>%
 mutate(start_date = if_else(ID != 1, start_date %m+% months(row_number() - 1), start_date),
        end_date = if_else(ID != 1, end_date %m+% months(row_number() - 1), end_date))

  ID    start_date end_date  
  <fct> <date>     <date>    
1 1     2007-08-10 2007-10-10
2 2     2007-06-10 2007-08-10
3 3     2007-04-10 2007-06-10
4 4     2007-02-10 2007-04-10

但是,问题在于它没有按组填充整个初始阶段(12 个月)。在到达原始“start_date”之前,可能会有更多 ID,即2006-11-10。

对于这个给定的示例,有第五个 ID (这也是所需的输出):

  ID start_date   end_date
1  1 2007-08-10 2007-10-10
2  2 2007-06-10 2007-08-10
3  3 2007-04-10 2007-06-10
4  4 2007-02-10 2007-04-10
5  5 2006-12-10 2007-02-10

因此,我的问题是,是否有可能通过上述逻辑填充给定时间段?

【问题讨论】:

    标签: r datetime dplyr lubridate


    【解决方案1】:

    如果这对应于您所追求的输出,更直接的方法可能如下:

    • 根据month_start 定义的月份序列定义date
    • 通过使用cut 将date 列切割成month_step-月间隔来获取start_date
    • 通过重新标记因子start_date(向上移动级别)来定义end_date
    • 通过将start_date 中的级别重新标记为整数来定义id
    library(dplyr)
    library(lubridate)
    
    month_data <- function(end_date = "2007-10-10", month_start = -12, month_step = 2) {
    
      tibble(date = as.Date(end_date) + months(0:month_start)) %>%
          mutate(
              start_date = cut(date, 
                  breaks = as.Date(end_date) + months(0:(month_start %/% month_step) * month_step), 
                  include.lowest = TRUE),
              end_date = as.Date(factor(start_date, labels = c(tail(levels(start_date), -1), end_date))), 
              ID = factor(start_date, labels = seq_len(nlevels(start_date))),
              start_date = as.Date(start_date)
          )
    }
    
    
    ## 12-month sequence + 2-month intervals
    month_data()
    #> # A tibble: 13 x 4
    #>    date       start_date end_date   ID   
    #>    <date>     <date>     <date>     <fct>
    #>  1 2007-10-10 2007-08-10 2007-10-10 6    
    #>  2 2007-09-10 2007-08-10 2007-10-10 6    
    #>  3 2007-08-10 2007-08-10 2007-10-10 6    
    #>  4 2007-07-10 2007-06-10 2007-08-10 5    
    #>  5 2007-06-10 2007-06-10 2007-08-10 5    
    #>  6 2007-05-10 2007-04-10 2007-06-10 4    
    #>  7 2007-04-10 2007-04-10 2007-06-10 4    
    #>  8 2007-03-10 2007-02-10 2007-04-10 3    
    #>  9 2007-02-10 2007-02-10 2007-04-10 3    
    #> 10 2007-01-10 2006-12-10 2007-02-10 2    
    #> 11 2006-12-10 2006-12-10 2007-02-10 2    
    #> 12 2006-11-10 2006-10-10 2006-12-10 1    
    #> 13 2006-10-10 2006-10-10 2006-12-10 1
    
    ## 12-month sequence + 3-month intervals
    month_data(month_step = 3)
    #> # A tibble: 13 x 4
    #>    date       start_date end_date   ID   
    #>    <date>     <date>     <date>     <fct>
    #>  1 2007-10-10 2007-07-10 2007-10-10 4    
    #>  2 2007-09-10 2007-07-10 2007-10-10 4    
    #>  3 2007-08-10 2007-07-10 2007-10-10 4    
    #>  4 2007-07-10 2007-07-10 2007-10-10 4    
    #>  5 2007-06-10 2007-04-10 2007-07-10 3    
    #>  6 2007-05-10 2007-04-10 2007-07-10 3    
    #>  7 2007-04-10 2007-04-10 2007-07-10 3    
    #>  8 2007-03-10 2007-01-10 2007-04-10 2    
    #>  9 2007-02-10 2007-01-10 2007-04-10 2    
    #> 10 2007-01-10 2007-01-10 2007-04-10 2    
    #> 11 2006-12-10 2006-10-10 2007-01-10 1    
    #> 12 2006-11-10 2006-10-10 2007-01-10 1    
    #> 13 2006-10-10 2006-10-10 2007-01-10 1
    
    ## 10 month sequence + 5-month intervals
    month_data(month_start = -10, month_step = 5)
    #> # A tibble: 11 x 4
    #>    date       start_date end_date   ID   
    #>    <date>     <date>     <date>     <fct>
    #>  1 2007-10-10 2007-05-10 2007-10-10 2    
    #>  2 2007-09-10 2007-05-10 2007-10-10 2    
    #>  3 2007-08-10 2007-05-10 2007-10-10 2    
    #>  4 2007-07-10 2007-05-10 2007-10-10 2    
    #>  5 2007-06-10 2007-05-10 2007-10-10 2    
    #>  6 2007-05-10 2007-05-10 2007-10-10 2    
    #>  7 2007-04-10 2006-12-10 2007-05-10 1    
    #>  8 2007-03-10 2006-12-10 2007-05-10 1    
    #>  9 2007-02-10 2006-12-10 2007-05-10 1    
    #> 10 2007-01-10 2006-12-10 2007-05-10 1    
    #> 11 2006-12-10 2006-12-10 2007-05-10 1
    

    【讨论】:

    • 你的努力。您的解决方案有两个问题。首先,我不认为它可以很容易地扩展到每 N 个月(这里是 2,但它可以是 3、4 等)。其次,它会生成超出原始日期的 ID(2006-11-10 与 2006-10-10)。
    • @tmfmnk:我编辑了回复,添加了一个额外的过滤器,并以 3 个月的间隔而不是 2 个月的间隔进行切割。也许这更接近您想要实现的目标,从如何定义 ID 的问题中有点不清楚,因为 start_date-end_date ID 似乎与 date ID 不对应
    • “日期”列是“开始日期”和“结束日期”之间的 N 个月(此处为 12 个月)序列。 “start_month”列是根据给定的“end_date”列计算的。根据“日期”列,生成一组初始 ID,以便每 N 个月(此处为每两个月)分配一个唯一 ID。
    • @tmfmk:我想“start_month”指的是“start_date”,而这两个 N 无关?我在上一条评论中的意思是,例如2007-05-10 在初始df 中获得ID 2,但不在最终df 中ID 2(2007-06-10 到2007-08-10)的区间内。
    • 你是对的,“start_month”我的意思是“start_date” :) 由于它所采取的移动,最终的 ID 并不完全在其原始间隔内。这意味着“end_date”列变成了前一个ID的“start_date”。然后相应地调整“start_date”,使其仍有 N 个月(在本例中为 2)。因此,最终的“start_date”是“end_date” - 2 个月,而“end_date”是之前 ID 的“start_date”。
    猜你喜欢
    • 2018-07-24
    • 2015-02-13
    • 2018-09-06
    • 2021-04-21
    • 1970-01-01
    • 2022-11-22
    • 1970-01-01
    • 2020-04-04
    • 1970-01-01
    相关资源
    最近更新 更多