Как создать последовательность ежемесячных дат из фрейма данных в R? - PullRequest
0 голосов
/ 28 апреля 2019

Рассмотрим следующий фрейм данных (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

Я пытаюсь создать новый фрейм данных с последовательностями ежемесячных дат, начинающихся за месяц до минимального значения "date_start" для каждогогруппа в "id".Последовательность также включает в себя только даты с первого дня месяца и заканчивается максимальным значением «дата-конец» для каждой группы в «id».

Это воспроизводимый пример для моего фрейма данных:

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 ]

1 голос
/ 28 апреля 2019

Другой вариант, использующий dplyr и lubridate, заключается в том, чтобы сначала summarise a list объектов Date для каждого id, а затем unnest их, чтобы развернуть их в разные строки.

library(dplyr)
library(lubridate)

df %>%
  group_by(id) %>%
  summarise(date = list(seq(floor_date(min(date_start),unit = "month") - months(1), 
                   floor_date(max(date_end), unit = "month"), by = "month"))) %>%
  tidyr::unnest()


#   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
0 голосов
/ 28 апреля 2019

В вашем коде, поскольку в 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
0 голосов
/ 28 апреля 2019

Используйте as.yearmon для преобразования в год / месяц.Обратите внимание, что объекты yearmon внутренне представлены как год + дробь, где дробь равна 0 для января, 1/12 для февраля, 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...