knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
library(dplyr)
##
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(ggplot2)
library(lubridate)
##
## Adjuntando el paquete: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(readr)
library(stringr)
Este informe se realizo con el objetivo de analizar el comportamiento entre los miembros cusuales y los miembros anuales del sistema de bicicletas compartidas, con la finalidad de identificar estrategias u oportunidades que ayuden con el crecimiento en la conversión de usuarios ocasionales a miembros anuales.
¿Cómo difiere la duración de los viajes entre usuarios?
¿En qué días y meses se presenta mayor uso?
¿Qué patrones explican la baja conversión?
Se utilizaron 12 archivos correspondientes a un año completo de viajes, específicamente del año 2025. Estos datos fueron consolidados en un único conjunto de datos (dataset). Posteriormente, se realizó un proceso de limpieza de datos, en el cual se eliminaron valores nulos, duraciones negativas y viajes con una duración superior a 24 horas, ya que estos se consideran registros anómalos (ROOC) y no representan un comportamiento real de los usuarios.
# 1. Cargar archivos
archivos <- list.files("Datos", full.names = TRUE)
if (length(archivos) == 0) {
stop("❌ No se encontraron archivos en la carpeta 'data'")
}
rides_all <- archivos %>%
lapply(read_csv) %>%
bind_rows() %>%
rename_with(tolower)
# 2. Mostrar columnas reales (IMPORTANTE)
cat("📌 Columnas detectadas en el dataset:\n")
## 📌 Columnas detectadas en el dataset:
print(names(rides_all))
## [1] "ride_id" "rideable_type" "started_at"
## [4] "ended_at" "start_station_name" "start_station_id"
## [7] "end_station_name" "end_station_id" "start_lat"
## [10] "start_lng" "end_lat" "end_lng"
## [13] "member_casual"
# 3. Buscar columnas de fecha/hora (robusto)
start_col <- names(rides_all)[
str_detect(names(rides_all), "start|begin")
][1]
end_col <- names(rides_all)[
str_detect(names(rides_all), "end|stop|finish")
][1]
# 4. Validación fuerte
if (is.na(start_col) || is.na(end_col)) {
stop("❌ No se encontraron columnas de inicio o fin.
👉 Revisa arriba los nombres impresos y ajusta los patrones.")
}
cat("✅ Columna inicio:", start_col, "\n")
## ✅ Columna inicio: started_at
cat("✅ Columna fin:", end_col, "\n")
## ✅ Columna fin: ended_at
# 5. Renombrar
rides_all <- rides_all %>%
rename(
started_at = all_of(start_col),
ended_at = all_of(end_col)
)
# 6. Limpieza y variables nuevas
datos_limpios <- rides_all %>%
mutate(
started_at = as.POSIXct(started_at, tz = "UTC"),
ended_at = as.POSIXct(ended_at, tz = "UTC"),
ride_length_min = as.numeric(difftime(ended_at, started_at, units = "mins"))
) %>%
filter(
!is.na(ride_length_min),
ride_length_min > 0,
ride_length_min <= 1440
) %>%
mutate(
day_of_week = wday(started_at, label = TRUE),
month = month(started_at, label = TRUE)
)
cat("✅ Datos limpios:", nrow(datos_limpios), "registros\n")
## ✅ Datos limpios: 5547380 registros
El analisis que se realizo con base a los datos suministrados por el sistema de bicicletas compartidas. En esta etapa se realizaron las siguientes acciones:
datos_limpios %>%
group_by(member_casual) %>%
summarise(
duracion_promedio = mean(ride_length_min),
duracion_mediana = median(ride_length_min),
total_viajes = n(),
.groups = "drop"
)
## # A tibble: 2 × 4
## member_casual duracion_promedio duracion_mediana total_viajes
## <chr> <dbl> <dbl> <int>
## 1 casual 19.1 11.4 1994811
## 2 member 12.0 8.58 3552569
###Day_of_week
-day_of_week, que es para identificar el dia de la semana que el usuario utilizo el sistema
datos_limpios %>%
group_by(member_casual, day_of_week) %>%
summarise(
duracion_promedio = mean(ride_length_min),
.groups = "drop"
) %>%
ggplot(aes(day_of_week, duracion_promedio, fill = member_casual)) +
geom_col(position = "dodge") +
labs(
title = "Duración promedio del viaje por día",
x = "Día de la semana",
y = "Minutos",
fill = "Tipo de usuario"
)
Interpretación
Se puede evidenciar que los usuarios ocasioanels presentan viajes mucho más largos que los miembros; lo que supone que los usuarios ocasionales utilizan el sistema para uso recreativo.
-month, para identificar el mes correspondiente al uso del sistema.
datos_limpios %>%
group_by(member_casual, month) %>%
summarise(
total_viajes = n(),
.groups = "drop"
) %>%
ggplot(aes(month, total_viajes, fill = member_casual)) +
geom_col(position = "dodge") +
labs(
title = "Cantidad de viajes por mes",
x = "Mes",
y = "Número de viajes",
fill = "Tipo de usuario"
)
Interpretación
Se observa un aumento significativo de usuarios casuales durante los
meses de verano, especialmente en junio.
Con base en estos hallazgos, se concluye que la baja conversión de usuarios ocasionales a miembros anuales no se debe a una falta de interés en el servicio, sino a que el modelo actual de membresía no se adapta a su comportamiento de uso. Por lo tanto, se recomienda implementar membresías flexibles de corta duración, como planes mensuales, semestrales o estacionales, así como promociones específicas durante los meses de mayor uso, con el objetivo de incrementar la conversión y maximizar los ingresos de la empresa.