объединить векторные «даты» в фрейм данных в R

#r #date #join #merge

#r #Дата #Присоединиться #объединить

Вопрос:

Я создал вектор дат с 2015-11-29 по 2020-09-05 согласно:

 newdates_startweek <- seq(as.Date('2015-11-29'),as.Date('2020-09-05'),by = 7)
  

Теперь я хочу объединить этот ‘newdates_startweek’ с моим исходным набором данных:

 region   id      name        date       appointment
   A      1    clinic1    2015-11-29        1      
   A      1    clinic1    2015-12-08        1      
   A      1    clinic1    2020-08-17        1      
   A      1    clinic1    2020-08-19        1      
   A      1    clinic1    2020-09-03        1      
  

код исходного набора данных:

 region <- c("A","A","A","A", "A")
id <- c(1,1,1,1,1)
name <- c("clinic1","clinic1","clinic1","clinic1","clinic1")
date <- c(as.Date('2015-11-29'), as.Date("2015-12-08"), as.Date("2020-08-17"), as.Date('2020-08-19'), as.Date('2020-09-03'))
appointment <- c(1,1,1,1,1)

df <- data.frame(region, id, name, date, appointment)
  

Чтобы получить это:

 region   id      name        date       appointment   newdates_startweek
   A      1    clinic1    2015-11-29        1         2015-11-29
   A      1    clinic1    2015-12-08        1         2015-12-06
   A      1    clinic1        NA            0         2015-12-13
   A      1    clinic1        NA            0         2015-12-20
   A      1    clinic1        NA            0         2015-12-27
   A      1    clinic1        NA            0         2016-01-03
                                ....
   A      1    clinic1    2020-08-17        1         2020-08-16
   A      1    clinic1    2020-08-19        1         2020-08-16
   A      1    clinic1        NA            0         2020-08-23
   A      1    clinic1    2020-09-03        1         2020-08-30
  

Знаете ли вы быстрый способ сделать это?

Ответ №1:

Итак, мое решение немного затянуто. Я переработал некоторый код, который у меня был, и попытался подумать о том, как это будет работать, если вы захотите сделать это для нескольких наборов (регион, идентификатор, имя). Это data.table решение, но я преобразовал выходные данные обратно в df.

 library(data.table)
library(optiRum)
library(lubridate)
library(dplyr)

# a function in my tool kit :)
getWeek <- function(dates, weekday_start="Sunday"){
  lookup <- data.table(id=1:7, day=as.character(lubridate::wday(1:7, label = TRUE, abbr = FALSE)))
  # its -1 for some reason in floor date
  weeks <- floor_date(dates, "week", 
                      week_start = lookup[day==weekday_start, id]-1)
  return(weeks)
}

# test it, should go back to sunday
week_start <- weekdays(newdates_startweek[1])
getWeek(as.Date("2015-11-30"), week_start)

# so find the starting week for each date, using data.tables
dt <- as.data.table(df)
dt[, week := getWeek(date, week_start)]

# I've used optiRum::CJ.dt for making all combinations of the id data in id_sets[]
# and the string of weeks. It's like expand.grid but quicker and more versatile
id_sets <- as.data.table(unique(df[,  c("region", "id", "name")]))
expand_dt <- optiRum::CJ.dt(data.table(week = newdates_startweek), id_sets)

# dplyr::anti_join removes data already existing in dt
expand_dt <- setDT(anti_join(expand_dt, dt, by=c("region", "id", "name", "week")))

# now they can be bound together. rbindlist has a fill option to add NA's for me
out <- rbindlist(list(dt, expand_dt), fill=TRUE) 

# order by week and date
setorder(out, week, date)
out_df <- as.data.frame(out)
out_df
  

Ответ №2:

Отвечает ли это:

 > df %>% mutate(date = as.character(date)) %>% right_join(as.data.frame(as.character(newdates_startweek)) %>% setNames('newdates_startweek'), by = c('date' ='newdates_startweek' ), keep = 1) 
    region id    name       date appointment newdates_startweek
1        A  1 clinic1 2015-11-29           1         2015-11-29
2     <NA> NA    <NA>       <NA>          NA         2015-12-06
3     <NA> NA    <NA>       <NA>          NA         2015-12-13
4     <NA> NA    <NA>       <NA>          NA         2015-12-20
5     <NA> NA    <NA>       <NA>          NA         2015-12-27
6     <NA> NA    <NA>       <NA>          NA         2016-01-03
..
..
  

Комментарии:

1. Спасибо, что предложили этот подход — он похож на первый ответ.

Ответ №3:

Возможно, попробуйте

 library(dplyr)
library(tidyr)
library(lubridate)

df %>% 
  mutate(newdates_startweek = floor_date(date, "week", 7)) %>% 
  complete(region, id, name, newdates_startweek = full_seq(newdates_startweek, 7), fill = list(appointment = 0))
  

Вывод

 # A tibble: 250 x 6
   region    id name    newdates_startweek date       appointment
   <chr>  <dbl> <chr>   <date>             <date>           <dbl>
 1 A          1 clinic1 2015-11-29         2015-11-29           1
 2 A          1 clinic1 2015-12-06         2015-12-08           1
 3 A          1 clinic1 2015-12-13         NA                   0
 4 A          1 clinic1 2015-12-20         NA                   0
 5 A          1 clinic1 2015-12-27         NA                   0
 6 A          1 clinic1 2016-01-03         NA                   0
 7 A          1 clinic1 2016-01-10         NA                   0
 8 A          1 clinic1 2016-01-17         NA                   0
 9 A          1 clinic1 2016-01-24         NA                   0
10 A          1 clinic1 2016-01-31         NA                   0
# ... with 240 more rows
  

Комментарии:

1. Спасибо за предложенный вами подход. Однако я получаю следующее сообщение об ошибке: Ошибка: векторная память исчерпана (достигнут предел?)

2. Это странно. Я не могу воспроизвести эту ошибку на своем ноутбуке. Может быть, вы ограничили доступную для R память? Проверьте Sys.getenv("R_MAX_VSIZE") , чтобы увидеть некоторые диагностические данные. @DanielaRodrigues

3. Спасибо за это @ekoam, проверит