如何从R中的数据帧生成月度日期序列?

时间:2019-04-28 15:14:10

标签: r date runtime-error sequence lubridate

请考虑以下数据帧(df):

"id"    "date_start"    "date_end"
 a       2012-03-11     2012-03-27
 a       2012-05-17     2012-07-21
 a       2012-06-09     2012-08-18
 b       2015-06-21     2015-07-12
 b       2015-06-27     2015-08-04
 b       2015-07-02     2015-08-01
 c       2017-10-11     2017-11-08
 c       2017-11-27     2017-12-15
 c       2017-01-02     2018-02-03

我正在尝试创建一个新的数据框,该数据框具有按月日期顺序,从“ id”中每个组的“ date_start”最小值之前一个月开始。该序列还仅包括从一个月的第一天开始的日期,并以“ id”中每个组的“ date-end”的最大值结尾。

这是我的数据框的可复制示例:

library(lubridate)

id <- c("a","a","a","b","b","b","c","c","c")
df <- data.frame(id)
df$date_start <- as.Date(c("2012-03-11", "2012-05-17","2012-06-09", "2015-06-21", "2015-06-27","2015-07-02", "2017-10-11", "2017-11-27","2018-01-02"))
df$date_end <- as.Date(c("2012-03-27", "2012-07-21","2012-08-18", "2015-07-12", "2015-08-04","2015-08-012", "2017-11-08", "2017-12-15","2018-02-03"))

我试图做的事情:

library(dplyr)
library(Desctools)
library(timeDate)

df2 <- df %>%
   group_by(id) %>%
   summarize(start= floor_date(AddMonths(min(date_start),-1), "month"),end=max(date_end)) %>%
   do(data.frame(id=.$id, date=seq(.$start,.$end,by="1 month")))

对于未分组的数据帧,该代码可以很好地工作。以某种方式,通过“ id”分组会引发错误消息:

Error in seq.default(.$date_start, .$date_end, by = "1 month") : 
'from' must be of length 1

这是上面给出的数据帧所需输出的样子:

"id"       "date"    
 a       2012-02-01     
 a       2012-03-01     
 a       2012-04-01     
 a       2012-05-01    
 a       2012-06-01     
 a       2012-07-01     
 a       2012-08-01         
 b       2015-05-01 
 b       2015-06-01 
 b       2015-07-01
 b       2015-08-01  
 c       2017-09-01 
 c       2017-10-01 
 c       2017-11-01
 c       2017-12-01
 c       2018-01-01
 c       2018-02-01

是否有一种方法可以更改代码以使其与分组数据帧一起工作?此操作是否有完全不同的方法?

3 个答案:

答案 0 :(得分:1)

使用dplyrlubridate的另一种选择是,首先为每个summarise list的{​​{1}}个Date对象,然后id将它们展开为不同的行。

unnest

答案 1 :(得分:0)

使用as.yearmon转换为年/月。请注意,yearmon对象在内部用Year + Fraction表示,其中分数对于1月为0,对于2月为1/12,对于3月为2/12,依此类推。然后使用as.Date将其转换为Date类。 do允许组更改大小。

library(dplyr)
library(zoo)

df %>%
  group_by(id) %>%
  do( data.frame(month = as.Date(seq(as.yearmon(min(.$date_start)) - 1/12,
                                     as.yearmon(max(.$date_end)), 
                                     1/12) ))) %>%
  ungroup

给予:

# A tibble: 17 x 2
   id    month     
   <fct> <date>    
 1 a     2012-02-01
 2 a     2012-03-01
 3 a     2012-04-01
 4 a     2012-05-01
 5 a     2012-06-01
 6 a     2012-07-01
 7 a     2012-08-01
 8 b     2015-05-01
 9 b     2015-06-01
10 b     2015-07-01
11 b     2015-08-01
12 c     2017-09-01
13 c     2017-10-01
14 c     2017-11-01
15 c     2017-12-01
16 c     2018-01-01
17 c     2018-02-01

这也可以使用与上面相同的library语句来编写:

Seq <- function(st, en) as.Date(seq(as.yearmon(st) - 1/12, as.yearmon(en), 1/12))
df %>%
  group_by(id) %>%
  do( data.frame(month = Seq(min(.$date_start), max(.$date_end))) ) %>%
  ungroup

答案 2 :(得分:0)

在您的代码中,由于id中存在重复项,因此您可以按row_number分组并获得与以下相同的结果:

df %>%
  group_by(id) %>%
  summarize(start= floor_date(AddMonths(min(date_start),-1), "month"),end=max(date_end)) %>%
  group_by(rn=row_number()) %>%
  do(data.frame(id=.$id, date=seq(.$start, .$end, by="1 month"))) %>%
  ungroup() %>%
  select(-rn)
# A tibble: 17 x 2
   id    date      
   <fct> <date>    
 1 a     2012-02-01
 2 a     2012-03-01
 3 a     2012-04-01
 4 a     2012-05-01
 5 a     2012-06-01
 6 a     2012-07-01
 7 a     2012-08-01
 8 b     2015-05-01
 9 b     2015-06-01
10 b     2015-07-01
11 b     2015-08-01
12 c     2017-09-01
13 c     2017-10-01
14 c     2017-11-01
15 c     2017-12-01
16 c     2018-01-01
17 c     2018-02-01