#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