Создание инструкции для проверки нескольких дат между датой начала и датой окончания

#r #dataframe #date #if-statement

Вопрос:

У меня есть такой фрейм данных в R:

Дата начала Дата окончания Дата 1 Дата 2 Дата 3 Дата 4
11/12/2018 29/11/2019 08/03/2021 NA NA NA
07/03/2018 24/04/2019 08/03/2021 12/09/2016 NA NA
04/06/2018 23/04/2019 08/03/2021 02/10/2017 05/10/2018 NA
26/07/2018 29/08/2019 08/03/2021 03/08/2015 02/10/2017 23/01/2017

Я хочу создать новый столбец в R, в котором говорится: Если дата 1, Дата 2, Дата 3 или Дата 4 находится между датой начала и датой окончания, в противном случае она должна возвращать 1, 0, как показано в таблице ниже:

Дата начала Дата окончания Дата 1 Дата 2 Дата 3 Дата 4 Изменить
11/12/2018 29/11/2019 08/03/2021 NA NA NA 0
07/03/2018 24/04/2019 08/03/2021 12/09/2016 NA NA 0
04/06/2018 23/04/2019 08/03/2021 02/10/2017 05/10/2018 NA 1
26/07/2018 29/08/2019 08/03/2021 03/08/2015 02/10/2017 23/01/2017 0

У кого-нибудь есть предложения о том, как это решить? Спасибо вам 🙂

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

1. Пожалуйста, опубликуйте свои данные в выходных данных команды dput(your_dataframe) , чтобы нам было легче получить доступ к вашим данным. Также включите любой код, который вы пробовали, и/или ошибки, которые вы получили.

Ответ №1:

Людям будет намного проще помогать вам, если вы сможете публиковать код / данные, которые мы можем запускать напрямую. Самый простой способ сделать это-использовать удобную функцию R dput , которая генерирует инструкции для точного воссоздания любого объекта R. Таким образом , вы можете запустить dput(MY_DATA) или, если ваши данные намного больше, чем необходимо для демонстрации вашего вопроса, dput(head(MY_DATA)) получить первые шесть строк и вставить их в свой вопрос. </PSA>


Вот код для создания ваших примерных данных:

 my_data <- data.frame(
  stringsAsFactors = FALSE,
        Start.date = c("11/12/2018", "07/03/2018", "04/06/2018", "26/07/2018"),
          End.date = c("29/11/2019", "24/04/2019", "23/04/2019", "29/08/2019"),
            Date.1 = c("08/03/2021", "08/03/2021", "08/03/2021", "08/03/2021"),
            Date.2 = c(NA, "12/09/2016", "02/10/2017", "03/08/2015"),
            Date.3 = c(NA, NA, "05/10/2018", "02/10/2017"),
            Date.4 = c(NA, NA, NA, "23/01/2017")
)
 

Вот простой подход , чтобы сначала преобразовать даты вашего дня/месяца/года в данные типа даты R, используя lubridate::dmy , затем сравнить каждую из дат с 1 по 4 с вашими датами начала, а затем, наконец, показать, есть ли какие-либо 1 (в пределах диапазона).

 library(dplyr); library(lubridate)
my_data %>%
  mutate(across(.fns = ~dmy(.x))) %>%
  mutate(across(.cols = starts_with("Date"),
                .fns = ~coalesce(.x >= Start.date amp; .x <= End.date, FALSE)*1)) %>%
  mutate(Change = pmax(Date.1, Date.2, Date.3, Date.4))
 

coalesce(..., FALSE) используется здесь для того, чтобы относиться к NA как к ЛОЖНОМУ.

(...)*1 чтобы преобразовать значение TRUE/FALSE в 1/0.

pmax(...) чтобы захватить самую большую из 1/0, то есть «есть ли какие-нибудь 1?»


Изменить: альтернатива оставить столбцы дат нетронутыми:

 my_data %>%
  mutate(across(.fns = ~dmy(.x))) %>%
  mutate(across(.cols = starts_with("Date"), 
                .names = "Check_{.col}",
                .fns = ~coalesce(.x >= Start.date amp; .x <= End.date, FALSE)*1)) %>%
  rowwise() %>%
  mutate(Change = max(c_across(starts_with("Check")))) %>%
  select(-starts_with("Check"))

  Start.date End.date   Date.1     Date.2     Date.3     Date.4     Change
  <date>     <date>     <date>     <date>     <date>     <date>      <dbl>
1 2018-12-11 2019-11-29 2021-03-08 NA         NA         NA              0
2 2018-03-07 2019-04-24 2021-03-08 2016-09-12 NA         NA              0
3 2018-06-04 2019-04-23 2021-03-08 2017-10-02 2018-10-05 NA              1
4 2018-07-26 2019-08-29 2021-03-08 2015-08-03 2017-10-02 2017-01-23      0
 

Ответ №2:

 library(tidyverse)
library(lubridate)

df <- read.table(textConnection("start_date;end_date;date_1;date_2;date_3;date_4
11/12/2018;29/11/2019;08/03/2021;NA;NA;NA
07/03/2018;24/04/2019;08/03/2021;12/09/2016;NA;NA
04/06/2018;23/04/2019;08/03/2021;02/10/2017;05/10/2018;NA
26/07/2018;29/08/2019;08/03/2021;03/08/2015;02/10/2017;23/01/2017"),
                 sep=";",
                 header = TRUE)
df %>%
  mutate(
    across(everything(), lubridate::dmy),
    change = ((date_1 > start_date amp; date_1 < end_date) |
                (date_2 > start_date amp; date_2 < end_date) |
                (date_3 > start_date amp; date_3 < end_date)
    ) %>%
      coalesce(FALSE) %>%
      as.integer()
  )
#>   start_date   end_date     date_1     date_2     date_3     date_4 change
#> 1 2018-12-11 2019-11-29 2021-03-08       <NA>       <NA>       <NA>      0
#> 2 2018-03-07 2019-04-24 2021-03-08 2016-09-12       <NA>       <NA>      0
#> 3 2018-06-04 2019-04-23 2021-03-08 2017-10-02 2018-10-05       <NA>      1
#> 4 2018-07-26 2019-08-29 2021-03-08 2015-08-03 2017-10-02 2017-01-23      0