Манипулировать значениями в разных столбцах при выполнении операции строка за строкой

#r #dataframe #dplyr

#r #фрейм данных #dplyr

Вопрос:

У меня есть некоторые данные, содержащие информацию о бронировании гостиничного номера, которая выглядит как приведенный ниже пример:

    user_id h_name h_capacity check_in_date check_out_date
         1     A1          2    2019-01-01     2019-01-05
         2     A1          2    2019-01-02     2019-01-05
         3     A1          2    2019-01-02     2019-01-03
         4     A2          3    2019-01-02     2019-01-04
         5     A2          3    2019-01-04     2019-01-05
  

user_id : идентификатор клиента

h_name : Название отеля

h_capacity : Максимальное количество номеров в отеле.

check_in_date amp; check_out_date : не требует пояснений.

Моя цель — выяснить, какие отели регистрируют людей сверх установленной вместимости.

Подход, который я попробовал, включает в себя создание новых столбцов для каждой даты, что приводит к приведенному выше образцу фрейма данных. Это будет выглядеть следующим образом:

     <...> 2019-01-01 2019-01-02 2019-01-03 2019-01-04 2019-01-05
    <...>          0          0          0          0          0
    <...>          0          0          0          0          0
    <...>          0          0          0          0          0
    <...>          0          0          0          0          0
    <...>          0          0          0          0          0
  

<...> представляет столбцы, которые присутствуют в первом фрейме данных, показанном вверху.

После выполнения вышеописанного я хочу вставить число 1 в столбцы дат, на которые пользователь проживает в отеле.Результат будет выглядеть следующим образом:

     <...> 2019-01-01 2019-01-02 2019-01-03 2019-01-04 2019-01-05
    <...>          1          1          1          1          1
    <...>          0          1          1          1          1
    <...>          0          1          1          0          0
    <...>          0          1          1          1          0
    <...>          0          0          0          1          1
  

<...> представляет столбцы, которые присутствуют в примере, показанном вверху.

И, в конце концов, я бы просто использовал summary для h_name, чтобы получить общее количество занятых кроватей за день.

Проблема в том, что я не могу заполнить фрейм данных, как показано выше, значением 1 в столбцах дат, в которых человек занимал комнату. Это включает в себя заполнение значения в разные столбцы для каждой строки, что означает, что для пользователя мне нужно было бы вставить 1 в столбцы, представляющие даты, когда они останавливались в отеле. Я не смог найти оптимального решения для этого и в итоге использовал for цикл, выполнение которого заняло 32 минуты.

Я смиренно прошу об оптимальном решении для этого. (Пожалуйста, обратите внимание: данные содержат 500 тысяч строк)

Ответ №1:

Я не следую вашему подходу, но это решит вашу проблему. Примечание: Я предполагаю, что вместимость отеля остается постоянной с течением времени, и дата выезда засчитывается как занятая кровать, как вы делали в своем примере.

 library(tidyverse)

hotel_data %>% 
  gather(check_in, date, check_in_date, check_out_date) %>% 
  group_by(h_name, h_capacity, user_id) %>% 
  complete(date = seq.Date(first(date), last(date), by = "day"), 
           fill = list(check_in = "stay")) %>% 
  group_by(h_name, date) %>% 
  mutate(people = n()) %>% 
  filter(people > h_capacity)

# A tibble: 6 x 6
# Groups:   h_name, date [2]
#   h_name h_capacity user_id date       check_in       people
#   <chr>       <dbl>   <dbl> <date>     <chr>           <int>
# 1 A1              2       1 2019-01-02 stay                3
# 2 A1              2       1 2019-01-03 stay                3
# 3 A1              2       2 2019-01-02 check_in_date       3
# 4 A1              2       2 2019-01-03 stay                3
# 5 A1              2       3 2019-01-02 check_in_date       3
# 6 A1              2       3 2019-01-03 check_out_date      3
  

Объяснение
Сначала я преобразую ваши данные в длинный формат с помощью gather , поскольку затем легко заполнить недостающие значения дат для каждой группы (отель и пользователь) с помощью complete . Затем, группируя только по отелю и дате, я подсчитываю количество людей и фильтрую по тем, которые превышают вместимость.

Данные

 hotel_data <- structure(list(user_id = c(1, 2, 3, 4, 5), 
                             h_name = c("A1", "A1", "A1", "A2", "A2"), 
                             h_capacity = c(2, 2, 2, 3, 3), 
                             check_in_date = structure(c(17897, 17898, 17898, 17898, 17900), class = "Date"), 
                             check_out_date = structure(c(17901, 17901, 17899, 17900, 17901), class = "Date")), 
                        class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -5L))
  

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

1. Привет, Кэт, спасибо за элегантное решение. Просто пришлось внести одно изменение, чтобы оно работало с моими данными: date = seq(min(date), max(date), by = 1