¡Hola!
Soy Valeria Moreira, publicista y analista de datos.
En R Markdown os presento algunas de mis habilidades y destrezas adquiridas en la especialización en Análisis de datos con programación en R, que forma parte del Certificado profesional de Análisis de Datos de Google.
El análisis de datos puede ayudarnos a comprender la dinámica de negocios en el sector hotelero, como por ejemplo qué tipo de habitación genera mayor cantidad de ingresos y tiene más o menos demanda. También permite tomar decisiones estratégicas de marketing relacionadas con el inventario de habitaciones. Además, el análisis de datos puede ayudarnos a realizar previsiones y a planificar la demanda futura. Al analizar regularmente los datos, podemos tomar decisiones informadas que mejoren la experiencia del huésped, los ingresos y los niveles de ocupación.
Este conjunto de datos 1 contiene información de reserva para un hotel urbano y un hotel resort, e incluye información como cuándo se realizó la reserva, duración de la estancia, número de adultos, niños y/o bebés, y número de plazas de aparcamiento disponibles.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.4.4 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readr)
library(skimr)
library(janitor)
##
## Attaching package: 'janitor'
##
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(ggplot2)
library(dplyr)
hotel_bookings <- read_csv("hotel_bookings.csv") # importar datos desde uno fichero .csv
## Rows: 119390 Columns: 32
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (13): hotel, arrival_date_month, meal, country, market_segment, distrib...
## dbl (18): is_canceled, lead_time, arrival_date_year, arrival_date_week_numb...
## date (1): reservation_status_date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(hotel_bookings) # resume de cada columna organizado horizontalmente
## spc_tbl_ [119,390 × 32] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ hotel : chr [1:119390] "Resort Hotel" "Resort Hotel" "Resort Hotel" "Resort Hotel" ...
## $ is_canceled : num [1:119390] 0 0 0 0 0 0 0 0 1 1 ...
## $ lead_time : num [1:119390] 342 737 7 13 14 14 0 9 85 75 ...
## $ arrival_date_year : num [1:119390] 2015 2015 2015 2015 2015 ...
## $ arrival_date_month : chr [1:119390] "July" "July" "July" "July" ...
## $ arrival_date_week_number : num [1:119390] 27 27 27 27 27 27 27 27 27 27 ...
## $ arrival_date_day_of_month : num [1:119390] 1 1 1 1 1 1 1 1 1 1 ...
## $ stays_in_weekend_nights : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ stays_in_week_nights : num [1:119390] 0 0 1 1 2 2 2 2 3 3 ...
## $ adults : num [1:119390] 2 2 1 1 2 2 2 2 2 2 ...
## $ children : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ babies : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ meal : chr [1:119390] "BB" "BB" "BB" "BB" ...
## $ country : chr [1:119390] "PRT" "PRT" "GBR" "GBR" ...
## $ market_segment : chr [1:119390] "Direct" "Direct" "Direct" "Corporate" ...
## $ distribution_channel : chr [1:119390] "Direct" "Direct" "Direct" "Corporate" ...
## $ is_repeated_guest : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ previous_cancellations : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ previous_bookings_not_canceled: num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ reserved_room_type : chr [1:119390] "C" "C" "A" "A" ...
## $ assigned_room_type : chr [1:119390] "C" "C" "C" "A" ...
## $ booking_changes : num [1:119390] 3 4 0 0 0 0 0 0 0 0 ...
## $ deposit_type : chr [1:119390] "No Deposit" "No Deposit" "No Deposit" "No Deposit" ...
## $ agent : chr [1:119390] "NULL" "NULL" "NULL" "304" ...
## $ company : chr [1:119390] "NULL" "NULL" "NULL" "NULL" ...
## $ days_in_waiting_list : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ customer_type : chr [1:119390] "Transient" "Transient" "Transient" "Transient" ...
## $ adr : num [1:119390] 0 0 75 75 98 ...
## $ required_car_parking_spaces : num [1:119390] 0 0 0 0 0 0 0 0 0 0 ...
## $ total_of_special_requests : num [1:119390] 0 0 0 0 1 1 0 1 1 0 ...
## $ reservation_status : chr [1:119390] "Check-Out" "Check-Out" "Check-Out" "Check-Out" ...
## $ reservation_status_date : Date[1:119390], format: "2015-07-01" "2015-07-01" ...
## - attr(*, "spec")=
## .. cols(
## .. hotel = col_character(),
## .. is_canceled = col_double(),
## .. lead_time = col_double(),
## .. arrival_date_year = col_double(),
## .. arrival_date_month = col_character(),
## .. arrival_date_week_number = col_double(),
## .. arrival_date_day_of_month = col_double(),
## .. stays_in_weekend_nights = col_double(),
## .. stays_in_week_nights = col_double(),
## .. adults = col_double(),
## .. children = col_double(),
## .. babies = col_double(),
## .. meal = col_character(),
## .. country = col_character(),
## .. market_segment = col_character(),
## .. distribution_channel = col_character(),
## .. is_repeated_guest = col_double(),
## .. previous_cancellations = col_double(),
## .. previous_bookings_not_canceled = col_double(),
## .. reserved_room_type = col_character(),
## .. assigned_room_type = col_character(),
## .. booking_changes = col_double(),
## .. deposit_type = col_character(),
## .. agent = col_character(),
## .. company = col_character(),
## .. days_in_waiting_list = col_double(),
## .. customer_type = col_character(),
## .. adr = col_double(),
## .. required_car_parking_spaces = col_double(),
## .. total_of_special_requests = col_double(),
## .. reservation_status = col_character(),
## .. reservation_status_date = col_date(format = "")
## .. )
## - attr(*, "problems")=<externalptr>
colnames(hotel_bookings) # nuestra los nombres de las columnas
## [1] "hotel" "is_canceled"
## [3] "lead_time" "arrival_date_year"
## [5] "arrival_date_month" "arrival_date_week_number"
## [7] "arrival_date_day_of_month" "stays_in_weekend_nights"
## [9] "stays_in_week_nights" "adults"
## [11] "children" "babies"
## [13] "meal" "country"
## [15] "market_segment" "distribution_channel"
## [17] "is_repeated_guest" "previous_cancellations"
## [19] "previous_bookings_not_canceled" "reserved_room_type"
## [21] "assigned_room_type" "booking_changes"
## [23] "deposit_type" "agent"
## [25] "company" "days_in_waiting_list"
## [27] "customer_type" "adr"
## [29] "required_car_parking_spaces" "total_of_special_requests"
## [31] "reservation_status" "reservation_status_date"
Organizo los datos de mayor a menor plazo de entrega para centrarme en las reservas que se realizaron con mucha antelación.
lead time (antelación de reserva) puede proporcionar información sobre el comportamiento del cliente, la previsión de la demanda y la gestión de ingresos.
arrange(hotel_bookings, desc(lead_time)) # ordena las filas por orden ascendente
## # A tibble: 119,390 × 32
## hotel is_canceled lead_time arrival_date_year arrival_date_month
## <chr> <dbl> <dbl> <dbl> <chr>
## 1 Resort Hotel 0 737 2015 July
## 2 Resort Hotel 0 709 2016 February
## 3 City Hotel 1 629 2017 March
## 4 City Hotel 1 629 2017 March
## 5 City Hotel 1 629 2017 March
## 6 City Hotel 1 629 2017 March
## 7 City Hotel 1 629 2017 March
## 8 City Hotel 1 629 2017 March
## 9 City Hotel 1 629 2017 March
## 10 City Hotel 1 629 2017 March
## # ℹ 119,380 more rows
## # ℹ 27 more variables: arrival_date_week_number <dbl>,
## # arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
## # stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
## # meal <chr>, country <chr>, market_segment <chr>,
## # distribution_channel <chr>, is_repeated_guest <dbl>,
## # previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …
max(hotel_bookings$lead_time) # plazo máximo
## [1] 737
min(hotel_bookings$lead_time) # plazo mínimo
## [1] 0
arrange(hotel_bookings, max(hotel_bookings$lead_time))
## # A tibble: 119,390 × 32
## hotel is_canceled lead_time arrival_date_year arrival_date_month
## <chr> <dbl> <dbl> <dbl> <chr>
## 1 Resort Hotel 0 342 2015 July
## 2 Resort Hotel 0 737 2015 July
## 3 Resort Hotel 0 7 2015 July
## 4 Resort Hotel 0 13 2015 July
## 5 Resort Hotel 0 14 2015 July
## 6 Resort Hotel 0 14 2015 July
## 7 Resort Hotel 0 0 2015 July
## 8 Resort Hotel 0 9 2015 July
## 9 Resort Hotel 1 85 2015 July
## 10 Resort Hotel 1 75 2015 July
## # ℹ 119,380 more rows
## # ℹ 27 more variables: arrival_date_week_number <dbl>,
## # arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
## # stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
## # meal <chr>, country <chr>, market_segment <chr>,
## # distribution_channel <chr>, is_repeated_guest <dbl>,
## # previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …
mean(hotel_bookings$lead_time) # promedio de entrega antes de reservar
## [1] 104.0114
filter(hotel_bookings, hotel_bookings$hotel=="City Hotel") # filtra los hoteles
## # A tibble: 79,330 × 32
## hotel is_canceled lead_time arrival_date_year arrival_date_month
## <chr> <dbl> <dbl> <dbl> <chr>
## 1 City Hotel 0 6 2015 July
## 2 City Hotel 1 88 2015 July
## 3 City Hotel 1 65 2015 July
## 4 City Hotel 1 92 2015 July
## 5 City Hotel 1 100 2015 July
## 6 City Hotel 1 79 2015 July
## 7 City Hotel 0 3 2015 July
## 8 City Hotel 1 63 2015 July
## 9 City Hotel 1 62 2015 July
## 10 City Hotel 1 62 2015 July
## # ℹ 79,320 more rows
## # ℹ 27 more variables: arrival_date_week_number <dbl>,
## # arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
## # stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
## # meal <chr>, country <chr>, market_segment <chr>,
## # distribution_channel <chr>, is_repeated_guest <dbl>,
## # previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …
hotel_summary <-
hotel_bookings %>%
group_by(hotel) %>%
summarise(average_lead_time=mean(lead_time),
min_lead_time=min(lead_time),
max_lead_time=max(lead_time))
head(hotel_summary) # Plazo de entrega máximo y mínimo de los hoteles urbanos y resort
## # A tibble: 2 × 4
## hotel average_lead_time min_lead_time max_lead_time
## <chr> <dbl> <dbl> <dbl>
## 1 City Hotel 110. 0 629
## 2 Resort Hotel 92.7 0 737
trimmed_df <- hotel_bookings %>%
select(hotel,is_canceled,lead_time)
trimmed_df %>%
select(hotel, is_canceled, lead_time) %>%
rename(tipo_hotel = hotel) # el operador pipe %>% concatena múltiples operaciones
## # A tibble: 119,390 × 3
## tipo_hotel is_canceled lead_time
## <chr> <dbl> <dbl>
## 1 Resort Hotel 0 342
## 2 Resort Hotel 0 737
## 3 Resort Hotel 0 7
## 4 Resort Hotel 0 13
## 5 Resort Hotel 0 14
## 6 Resort Hotel 0 14
## 7 Resort Hotel 0 0
## 8 Resort Hotel 0 9
## 9 Resort Hotel 1 85
## 10 Resort Hotel 1 75
## # ℹ 119,380 more rows
trimmed_df %>%
select(hotel, is_canceled, lead_time) %>%
rename(tipo_hotel = hotel)
## # A tibble: 119,390 × 3
## tipo_hotel is_canceled lead_time
## <chr> <dbl> <dbl>
## 1 Resort Hotel 0 342
## 2 Resort Hotel 0 737
## 3 Resort Hotel 0 7
## 4 Resort Hotel 0 13
## 5 Resort Hotel 0 14
## 6 Resort Hotel 0 14
## 7 Resort Hotel 0 0
## 8 Resort Hotel 0 9
## 9 Resort Hotel 1 85
## 10 Resort Hotel 1 75
## # ℹ 119,380 more rows
fechas_df <- hotel_bookings %>%
select(arrival_date_year, arrival_date_month) %>%
unite(fecha_mes_año, c("arrival_date_month", "arrival_date_year"), sep = " ") # fusiono las columnas especificadas
example_df <- hotel_bookings %>%
mutate(HUÉSPEDES = adults + children + babies) # encadeno varias expresiones en una misma sentencia
head(example_df)
## # A tibble: 6 × 33
## hotel is_canceled lead_time arrival_date_year arrival_date_month
## <chr> <dbl> <dbl> <dbl> <chr>
## 1 Resort Hotel 0 342 2015 July
## 2 Resort Hotel 0 737 2015 July
## 3 Resort Hotel 0 7 2015 July
## 4 Resort Hotel 0 13 2015 July
## 5 Resort Hotel 0 14 2015 July
## 6 Resort Hotel 0 14 2015 July
## # ℹ 28 more variables: arrival_date_week_number <dbl>,
## # arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
## # stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
## # meal <chr>, country <chr>, market_segment <chr>,
## # distribution_channel <chr>, is_repeated_guest <dbl>,
## # previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>,
## # reserved_room_type <chr>, assigned_room_type <chr>, …
¡Ahora es el momento de calcular algunas estadísticas resumidas!
example_df <- hotel_bookings %>%
summarise(tiempo_promedio_de_entrega = mean(lead_time)) # agrupo y condeso los valores
head(example_df)
## # A tibble: 1 × 1
## tiempo_promedio_de_entrega
## <dbl>
## 1 104.
La visualización de datos, me permite explorar más a fundo los datos y obtener nuevos conocimientos.
¿Las personas que reservan con anticipación son personas con niños?
Veamos:
library(ggplot2)
ggplot(data = hotel_bookings) +
geom_point(mapping = aes(x = lead_time, y = children)) +
labs(title = "Grupo con niños vs antelación de reserva")
## Warning: Removed 4 rows containing missing values (`geom_point()`).
–> La visualización revela que muchas de las reservas anticipadas las realizan personas sin hijos.
El eje x, el gráfico muestra con cuanta antelación se realiza una reserva, siendo las reservas más a la derecha las que se realizan con mayor antelación. En el eje y muestra cuántos niños hay en un grupo.
¿Qué grupo de huéspedes reserva la mayor cantidad de noches de fin de semana?
Veamos:
ggplot(data = hotel_bookings) +
geom_point(mapping = aes(x = stays_in_weekend_nights, y = children)) + # crea diagrama de dispersión
labs(title = "Grupo con/sin niños vs antelación de reserva")
## Warning: Removed 4 rows containing missing values (`geom_point()`).
–> La visualización revela que muchas de las reservas de fin de semana las realiza el grupo sin niños.
Las reservas de fin de semana son una importante fuente de ingresos para el hotel. El equipo de marketing debe conducir la nueva campaña de marketing al grupo sin niños.
¿Cuántas transacciones se producen para cada tipo de canal de distribución diferente?
Veamos:
ggplot(data = hotel_bookings) +
geom_bar(mapping = aes(x = distribution_channel)) + # crea diagrama de barras
labs(title = "Canal de distribución de reservas")
- –> El tipo de canal de
distribución de reservas TA/TO¹ tiene la mayor cantidad de
reservas.
¹ TA hace referencia a Agente de Viaje y TO a Tour-operador. GDS es un sistema de gestión de reservas global que conecta a muchos establecimientos hoteleros.
¿Si el número de reservas para cada tipo de distribución es diferente en función de si hubo o no depósito a qué segmento de mercado representan?
Veamos:
ggplot(data = hotel_bookings) +
geom_bar(mapping = aes(x = distribution_channel, fill=deposit_type)) +
labs(title = "Tipo de distribución de reserva ")
–> El canal de distribución TA/TO tiene la mayor cantidad de reservas, siendo uno 80% sin depósito y un 20% sin reembolso.
Otra observación es que el tipo de depósito reembolsable es zero.
Veamos:
ggplot(data = hotel_bookings) +
geom_bar(mapping = aes(x = distribution_channel, fill=market_segment)) +
labs(title = "Segmento de mercado")
–> El canal de distribución TA/TO¹ engloba la mayor cantidad de segmento de mercado. En una escala de mayor para menor: Online TA¹, Offline TA/TO y Groups.
¹ TA hace referencia a Agente de Viaje y TO a Tour-operador.
¿Qué se puede comprender del gráfico de cada tipo de depósito y del segmento de mercado?
Veamos:
ggplot(data = hotel_bookings) +
geom_bar(mapping = aes(x = distribution_channel,color="hotel")) +
facet_wrap(~deposit_type) +
theme(axis.text.x = element_text(angle = 45)) +
labs(title = "Tipos de depósito")
ggplot(data = hotel_bookings) +
geom_bar(mapping = aes(x = distribution_channel,color="hotel")) +
facet_wrap(~market_segment) +
theme(axis.text.x = element_text(angle = 45)) +
labs(title = "Segmento de mercado")
–> Podemos comprender que GDS ¹ es el tipo de depósito que más utilizado en el grupo de reserva sin depósito y sin reembolso.
¹ GDS es un sistema de gestión de reservas global que conecta a muchos establecimientos hoteleros.
¿Qué relación hay entre la antelación de la reserva y los huéspedes que viajan con niños para reservas online en hoteles urbanos?
Veamos:
onlineta_city_hotels_v2 <- hotel_bookings %>%
filter(hotel=="City Hotel") %>%
filter(market_segment=="Online TA")
head(onlineta_city_hotels_v2)
## # A tibble: 6 × 32
## hotel is_canceled lead_time arrival_date_year arrival_date_month
## <chr> <dbl> <dbl> <dbl> <chr>
## 1 City Hotel 1 88 2015 July
## 2 City Hotel 1 65 2015 July
## 3 City Hotel 1 92 2015 July
## 4 City Hotel 1 100 2015 July
## 5 City Hotel 1 79 2015 July
## 6 City Hotel 1 63 2015 July
## # ℹ 27 more variables: arrival_date_week_number <dbl>,
## # arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
## # stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
## # meal <chr>, country <chr>, market_segment <chr>,
## # distribution_channel <chr>, is_repeated_guest <dbl>,
## # previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>,
## # reserved_room_type <chr>, assigned_room_type <chr>, …
ggplot(data = onlineta_city_hotels_v2) +
geom_bar(mapping = aes(x = lead_time, fill=hotel)) +
labs(title = "Antelación de reserva online vs grupos con niños")
–> La relación existente es que los huéspedes que viajan con niños hacen reservar online con menor tiempo de antelación en hoteles urbanos.**
ggsave('hotel_booking_chart.png', width=7, # exporta gráfico creado con ggplot
height=7)
Este proyecto ha demostrado cómo el análisis de datos con R puede tener un impacto significativo en la toma de decisiones y la optimización de operaciones en una empresa de reservas de hoteles.
El certificado en Análisis de Datos de Google ha sido un paso crucial en mi carrera, proporcionándome las herramientas necesarias para enfrentar desafíos analíticos con confianza y habilidad. Este viaje ha ampliado mi perspectiva y fortalecido mi capacidad para transformar datos en conocimientos significativos.
==
Valéria Moreira - Publicist | Data analytics - - - - https://www.linkedin.com/in/valériamoreira - - - - morera.valeria@gmail.com - - - - https://sites.google.com/view/valeria-moreira/inicio?authuser=1
¡Muchas gracias!
Los datos de este ejemplo provienen originalmente del artículo Conjuntos de datos de demanda de reservas de hoteles (https://www.sciencedirect.com/science/article/pii/S2352340918315191), escrito por Nuno Antonio, Ana Almeida y Luis Nunes para Data in Brief. Volumen 22, febrero de 2019.Los datos fueron descargados y limpiados por Thomas Mock y Antoine Bichat para #TidyTuesday durante la semana del 11 de febrero de 2020 (https://github.com/rfordatascience/tidytuesday/blob/master/data/2020/2020-02-11/ Léame.md). Puede obtener más información sobre el conjunto de datos aquí (https://www.kaggle.com/jessemostipak/hotel-booking-demand).↩︎