1 Descripción del Proyecto y Caso de Negocio

Este proyecto analiza los datos de viajes de todo el año 2024 del sistema público de bicicletas compartidas de Buenos Aires, Ecobici, para descubrir patrones de uso, picos de demanda y oportunidades para mejorar la movilidad urbana sostenible.

A medida que las ciudades en todo el mundo adoptan transportes más ecológicos, comprender el comportamiento en el uso de bicicletas compartidas es clave para optimizar la ubicación de estaciones, la gestión de la flota y la reducción de la congestión y las emisiones.

Utilizando el conjunto de datos más reciente y completo (~3,56 millones de viajes), este análisis ofrece información accionable para la planificación de ciudades inteligentes y estrategias de movilidad sostenible a nivel global.

Objetivos Clave:

  • Identificar las horas, días y patrones estacionales de mayor uso de bicicletas.

  • Mapear las estaciones y barrios más populares de origen y destino.

  • Explorar la duración de los viajes y la demografía de los usuarios (cuando esté disponible).

  • Proporcionar recomendaciones basadas en datos para mejorar el sistema.

Este informe reproducible demuestra habilidades en limpieza de datos, análisis exploratorio, visualización y narración de negocio utilizando R y Tableau.

2 Fuentes de Datos

Este análisis utiliza datos abiertos oficiales del Gobierno de la Ciudad de Buenos Aires.

  1. Viajes Ecobici 2024
  • Archivo: ecobici_recorridos_realizados_2024.csv
  • Fuente: https://data.buenosaires.gob.ar/dataset/bicicletas-publicas
  • Tamaño: ~3,56 millones de filas (año completo 2024)
  • Columnas clave (tras renombrar al inglés):
    • trip_id: ID único del viaje
    • duration_seconds: Duración del viaje (segundos)
    • start_datetime / end_datetime: Hora de inicio y fin
    • origin_station_id / origin_station_name: Estación de origen
    • origin_longitude / origin_latitude: Coordenadas de origen
    • destination_station_id / destination_station_name: Estación de destino
    • destination_longitude / destination_latitude: Coordenadas de destino
    • user_id, bike_model, gender
  1. Estaciones Ecobici (Nuevo Sistema)
  • Archivo: estaciones_ecobici_nuevo_sistema.csv
  • Fuente: https://data.buenosaires.gob.ar/dataset/estaciones-bicicletas-publicas
  • Nota: Descargado pero no utilizado debido a incompatibilidad total en los IDs de estaciones (0% unión exitosa). El dataset de viajes ya incluye nombres y coordenadas completas de estaciones para cada viaje, asegurando cobertura total.

Nota de Calidad de Datos
Se intentó unir información de barrio, comuna y ubicación desde el archivo de estaciones, pero falló (0% coincidencia). El análisis se basa completamente en los nombres y coordenadas integrados en el archivo de viajes. En futuras actualizaciones se podrán añadir insights a nivel de barrio con un dataset de estaciones sincronizado.

Todos los datos son públicos y abiertos. Los datos de 2025 aún no están completamente disponibles a enero de 2026. Los nombres de columnas se han estandarizado al inglés para mayor claridad y legibilidad internacional.

3 Carga y Preparación de Datos

Los datos brutos de viajes Ecobici (2024) se cargan silenciosamente. Los nombres originales de las columnas están en español, tal como fueron provistos por el portal de datos abiertos.

4 Limpieza de Datos, Renombrado y Creación de Variables

En esta etapa:

  • Renombro todas las columnas al inglés para mantener consistencia y legibilidad internacional.
  • Limpio y normalizo los nombres de las estaciones.
  • Creo nuevas variables derivadas (duración en minutos, hora de inicio, día de la semana, indicador de fin de semana, mes).
  • Filtro viajes inválidos o atípicos (por ejemplo, viajes menores a 1 minuto, mayores a 24 horas o con valores clave faltantes).

Después de la limpieza, muestro la estructura y las estadísticas básicas del dataset final.

trips_clean <- trips %>%
  # Rename columns to English
  rename(
    trip_id                    = id_recorrido,
    duration_seconds           = duracion_recorrido,
    start_datetime             = fecha_origen_recorrido,
    origin_station_id          = id_estacion_origen,
    origin_station_name        = nombre_estacion_origen,
    origin_station_address     = direccion_estacion_origen,
    origin_longitude           = long_estacion_origen,
    origin_latitude            = lat_estacion_origen,
    end_datetime               = fecha_destino_recorrido,
    destination_station_id     = id_estacion_destino,
    destination_station_name   = nombre_estacion_destino,
    destination_station_address = direccion_estacion_destino,
    destination_longitude      = long_estacion_destino,
    destination_latitude       = lat_estacion_destino,
    user_id                    = id_usuario,
    bike_model                 = modelo_bicicleta,
    gender                     = genero
  ) %>%
  # Clean and normalize station names
  mutate(
    # Remove initial numbers + spaces/dash
    origin_station_name = str_trim(str_remove(origin_station_name, r"(^\d+\s*-?\s*)")),
    origin_station_name = str_to_title(origin_station_name),
    # Fix Roman numerals
    origin_station_name = str_replace_all(origin_station_name, "\\bIi\\b", "II"),
    origin_station_name = str_replace_all(origin_station_name, "\\bIii\\b", "III"),
    origin_station_name = str_replace_all(origin_station_name, "\\bIv\\b", "IV"),
    # Same for destination
    destination_station_name = str_trim(str_remove(destination_station_name, r"(^\d+\s*-?\s*)")),
    destination_station_name = str_to_title(destination_station_name),
    destination_station_name = str_replace_all(destination_station_name, "\\bIi\\b", "II"),
    destination_station_name = str_replace_all(destination_station_name, "\\bIii\\b", "III"),
    destination_station_name = str_replace_all(destination_station_name, "\\bIv\\b", "IV")
  ) %>%
  # Create new time-based features
  mutate(
    duration_minutes = duration_seconds / 60,
    hour_start       = hour(start_datetime),
    weekday          = wday(start_datetime, label = TRUE, abbr = TRUE, locale = "en_US.UTF-8"),
    day_type         = ifelse(weekday %in% c("Sat", "Sun"), "Weekend", "Weekday"),
    month            = month(start_datetime, label = TRUE, abbr = FALSE, locale = "en_US.UTF-8")
  ) %>%
  # Filter invalid trips
  filter(
    duration_seconds >= 60 & duration_seconds <= 86400,
    !is.na(start_datetime), !is.na(end_datetime),
    !is.na(origin_station_id), !is.na(destination_station_id)
  )

# Show glimpse
glimpse(trips_clean)
## Rows: 3,234,209
## Columns: 22
## $ trip_id                     <dbl> 20428222, 20431744, 20424802, 20427241, 20…
## $ duration_seconds            <dbl> 568, 1355, 680, 466, 1176, 1906, 695, 492,…
## $ start_datetime              <dttm> 2024-01-23 18:36:00, 2024-01-23 22:41:20,…
## $ origin_station_id           <dbl> 513, 460, 137, 99, 68, 17, 284, 432, 26, 5…
## $ origin_station_name         <chr> "San Martin II", "Beiro Y Segurola", "Azop…
## $ origin_station_address      <chr> "Av. San Martín 5129", "Segurola 3194", "A…
## $ origin_longitude            <dbl> -58.49074, -58.51193, -58.36749, -58.43541…
## $ origin_latitude             <dbl> -34.59713, -34.60750, -34.61560, -34.59610…
## $ end_datetime                <dttm> 2024-01-23 18:45:28, 2024-01-23 23:03:55,…
## $ destination_station_id      <dbl> 498, 382, 150, 206, 68, 186, 432, 284, 32,…
## $ destination_station_name    <chr> "Habana", "Biarritz", "Rodrigo Bueno", "Fi…
## $ destination_station_address <chr> "Gral. José Gervasio Artigas 4298 (y Haban…
## $ destination_longitude       <dbl> -58.49496, -58.47726, -58.35547, -58.43734…
## $ destination_latitude        <dbl> -34.58660, -34.60543, -34.61875, -34.58495…
## $ user_id                     <dbl> 992557, 320782, 861425, 320714, 1041602, 9…
## $ bike_model                  <chr> "FIT", "FIT", "FIT", "FIT", "ICONIC", "FIT…
## $ gender                      <chr> "MALE", "FEMALE", "FEMALE", "OTHER", "MALE…
## $ duration_minutes            <dbl> 9.466667, 22.583333, 11.333333, 7.766667, …
## $ hour_start                  <int> 18, 22, 15, 17, 21, 22, 19, 21, 17, 12, 11…
## $ weekday                     <ord> Tue, Tue, Tue, Tue, Tue, Tue, Tue, Tue, Tu…
## $ day_type                    <chr> "Weekday", "Weekday", "Weekday", "Weekday"…
## $ month                       <ord> January, January, January, January, Januar…
# Summary stats
summary(trips_clean$duration_minutes)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##    1.000    9.867   16.100   21.748   25.850 1439.133

Resultados Clave Después de la Limpieza

  • El dataset final contiene 3.234.209 viajes válidos (tras eliminar registros inválidos).

  • Las duraciones de los viajes en minutos tienen una mediana de ~16,1 minutos y un promedio de ~21,7 minutos, lo cual es típico en sistemas de bicicletas compartidas urbanas (trayectos cortos con algunos recorridos más largos).

  • La mayoría de los viajes están entre ~10 y 26 minutos (entre el primer y tercer cuartil).

Resumen de Reducción de Datos

Etapa Número de Viajes % del Original
Original (crudo) 3.559.284 100%
Después de limpieza 3.234.209 ~90,8%
Eliminados (inválidos) ~325.075 ~9,1%

Este proceso de limpieza eliminó aproximadamente 325.000 registros inválidos o atípicos (~9% de los datos originales), lo cual es normal y esperado en datasets públicos a gran escala como este. Los datos restantes están limpios, consistentes y listos para un análisis más profundo.

5 Análisis Exploratorio de Datos (EDA)

5.1 Horas Pico de Uso

Este gráfico de barras muestra los viajes iniciados por hora del día (0–23), destacando los patrones diarios de uso y las horas de mayor demanda.

trips_clean %>%
  count(hour_start) %>%
  ggplot(aes(x = factor(hour_start), y = n)) + 
  geom_bar(stat = "identity", fill = "steelblue", color = "white") +
  labs(title = "Number of Trips by Hour of Day (2024)", 
       subtitle = "Clear morning and evening commute peaks",
       x = "Hour of Day (0-23)",
       y = "Number of Trips") +
  scale_y_continuous(labels = comma) +
  theme_minimal(base_size = 14) +
  theme(
        axis.text.x = element_text(angle = 0, vjust = 0.5),
        axis.title.x = element_text(margin = margin(t = 25)),
        axis.title.y = element_text(margin = margin(r = 25)),
        plot.title = element_text(face = "bold", size = 16, margin = margin(b = 15)),
        plot.subtitle = element_text(size = 12, margin = margin(b = 15)))

Principales Conclusiones

  • El pico vespertino (16–18 hs) domina con 271.500–308.297 viajes por hora — fuerte patrón de regreso a casa.

  • Pico secundario en la mañana (7–9 hs): 117.583–147.119 viajes — desplazamiento de entrada al trabajo/estudio.

  • Mediodía (12–15 hs) estable en 172.710–220.351 viajes — mezcla de trámites y ocio.

Recomendaciones Potenciales

  • Aumentar la disponibilidad de bicicletas durante el pico vespertino (16–18 hs) para satisfacer la mayor demanda.

  • Explorar incentivos para la devolución de bicicletas en períodos de alta demanda.

5.2 Top 10 Estaciones de Origen Más Populares

Este gráfico de barras horizontal muestra las 10 estaciones de origen más utilizadas en 2024 (nombres normalizados), revelando los puntos de partida de mayor demanda.

trips_clean %>%
  count(origin_station_name) %>%
  top_n(10, n) %>%
  mutate(origin_station_name = fct_reorder(origin_station_name, n)) %>%
  ggplot(aes(x = n, y = origin_station_name)) +
  geom_bar(stat = "identity", fill = "darkorange", color = "white") +
  geom_text(aes(label = comma(n)), hjust = -0.1, size = 3.5, color = "black") +
  labs(title = "Top 10 Origin Stations by Number of Trips (2024)",
       subtitle = "Stations with highest departure demand",
       x = "Number of trips",
       y = "Origin Station Name") +
  scale_x_continuous(labels = comma, expand = expansion(mult = c(0, 0.1))) +
  theme_minimal(base_size = 14) +
  theme(axis.text.y = element_text(size = 10),
        axis.title.x = element_text(margin = margin(t = 25)),
        axis.title.y = element_text(margin = margin(r = 25)),
        plot.title = element_text(face = "bold", size = 16, margin = margin(b = 15)),
        plot.subtitle = element_text(size = 12, margin = margin(b = 15))
  )

Conclusiones Clave

  • Estación principal: Constitución (35.051 viajes), seguida por Pacífico (34.119) y Plaza De La Shoá (32.473).

  • Las 10 estaciones principales representan aproximadamente el 8,9% de todos los viajes — mostrando una demanda concentrada en pocos puntos.

  • Muchas de las estaciones más utilizadas se alinean con nodos de transporte o áreas centrales, reforzando el enfoque en los desplazamientos diarios.

Recomendaciones Potenciales

  • Priorizar el suministro de bicicletas y el reequilibrio cerca de las principales estaciones de origen (ej. Constitución, Pacífico) durante las horas pico.

  • Considerar incentivos para la devolución de bicicletas en estos puntos de partida concurridos.

Nota sobre el Enfoque del Análisis

Este análisis prioriza las estaciones de origen como el principal indicador de la demanda de salida (donde más se necesitan bicicletas).
Las estaciones de destino reflejan patrones de llegada y pueden explorarse en futuros análisis para estudiar flujos completos de viajes o el movimiento neto de bicicletas.

5.3 Promedio de Viajes Diarios: Días Laborales vs Fin de Semana (Normalizado)

Este gráfico de barras muestra el número promedio de viajes por día en días laborables frente a fines de semana (normalizado por días únicos para tener en cuenta que los días laborables son ~2,5× más numerosos).

trips_clean %>%
  group_by(day_type) %>%
  summarise(
    total_trips = n(),
    num_days = n_distinct(as.Date(start_datetime)),
    avg_trips_per_day = total_trips/num_days
  ) %>%
  ggplot(aes(x = day_type, y = avg_trips_per_day, fill = day_type)) +
  geom_bar(stat = "identity", width = 0.6) +
  geom_text(aes(label = comma(round(avg_trips_per_day))), vjust = -0.5, size = 5, color = "black") +
  scale_fill_manual(values = c("Weekday" = "steelblue", "Weekend" = "darkorange")) +
  labs(title = "Average trips per Day: Weekday vs Weekend (2024)",
       subtitle = "Normalized by number of unique days in each category",
       x = "",
       y = "Average Trips per Day") +
  scale_y_continuous(labels = comma) +
  theme_minimal(base_size = 14) +
  theme(legend.position = "none",
        plot.title = element_text(face = "bold", size = 16, margin = margin(b = 15)),
        plot.subtitle = element_text(size = 12, margin = margin(b = 15)),
        axis.title.x = element_blank(),
        axis.title.y = element_text(margin = margin(r = 25)),
        )

Conclusiones Clave

  • El promedio de días laborables es significativamente mayor que el de fines de semana, confirmando el desplazamiento diario como principal caso de uso.

  • El uso en fines de semana es menor pero igualmente considerable, lo que sugiere cierta actividad recreativa o de ocio los sábados y domingos.

  • La relación entre días laborables y fines de semana (~2,6:1 por día) refuerza el patrón de desplazamiento observado en el análisis de horas pico.

Recomendaciones Potenciales

  • Priorizar la disponibilidad de bicicletas y el reequilibrio en días laborables durante las horas pico.

  • Explorar promociones de fin de semana para impulsar el uso recreativo.

5.4 Mapas de Calor Espaciales de Orígenes y Destinos de Viajes

Estos mapas de calor interactivos visualizan la distribución geográfica de los orígenes (azul) y destinos (rojo) de los viajes en Buenos Aires (muestra 2024).

5.4.1 Mapa de Calor de Orígenes Matutinos (6–11 hs)

morning_origins <- trips_clean %>% filter(hour_start >= 6 & hour_start <= 11)

leaflet(morning_origins %>% sample_n(10000)) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addHeatmap(
    lng = ~origin_longitude,
    lat = ~origin_latitude,
    radius = 10,
    blur = 20,
    max = 0.05,
    gradient = c("white", "lightblue", "blue", "darkblue")
  ) %>%
  setView(lng = -58.45, lat = -34.60, zoom = 12) %>%
  addControl(html = "<h4>Morning Origins (6–11 AM)</h4><p>Blue = high density</p>", position = "topright")

5.4.2 Mapa de Calor de Destinos Vespertinos (15–20 hs)

evening_destinations <- trips_clean %>% filter(hour_start >= 15 & hour_start <= 20)

leaflet(evening_destinations %>% sample_n(10000)) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addHeatmap(
    lng = ~destination_longitude,
    lat = ~destination_latitude,
    radius = 10,
    blur = 20,
    max = 0.05,
    gradient = c("white", "yellow", "orange", "red", "darkred")
  ) %>%
  setView(lng = -58.45, lat = -34.60, zoom = 12) %>%
  addControl(html = "<h4>Evening Destinations (15–20 hs)</h4><p>Red = high density</p>", position = "topright")

Conclusiones Clave – Flujos de Desplazamiento: Orígenes Matutinos vs Destinos Vespertinos

  • Los orígenes matutinos (azul) muestran una alta concentración en áreas centrales y del norte (ej. Microcentro, Palermo, Recoleta), mientras que los destinos vespertinos (rojo) mantienen alta densidad en el centro pero con mayor dispersión por la ciudad.

  • Este patrón indica un flujo de desplazamiento en el que las personas inician viajes en zonas centrales/residenciales por la mañana y regresan a zonas residenciales más dispersas por la tarde.

  • La fuerte saturación central en ambos períodos sugiere que el núcleo de la ciudad actúa como un gran hub tanto de partidas como de llegadas.

Recomendaciones Potenciales

  • Priorizar la disponibilidad de bicicletas en clusters centrales de alta densidad durante las salidas matutinas y las llegadas vespertinas para satisfacer la demanda persistente en el centro.

  • Usar un reequilibrio dinámico para redistribuir el exceso de bicicletas desde el centro (donde se acumulan durante el día) hacia áreas residenciales más dispersas en la tarde, asegurando disponibilidad para los viajes de regreso.

Nota sobre la muestra

Se utilizó una muestra aleatoria de 10.000 viajes para mejorar el rendimiento interactivo; el análisis del dataset completo muestra patrones espaciales idénticos.

5.5 Análisis de Duración de Viajes

Este diagrama de caja muestra la distribución de las duraciones de los viajes en minutos según el tipo de día, revelando longitudes de uso típicas.

trips_clean %>%
  ggplot(aes(x = day_type, y = duration_minutes, fill = day_type)) +
  geom_boxplot(outlier.shape = NA) +
  scale_fill_manual(values = c("Weekday" = "steelblue", "Weekend" = "darkorange")) +
  labs(title = "Trip Duration Distribution by Day Type",
       subtitle = "Boxplot of duration in minutes (outliers removed for clarity)",
       x = "",
       y = "Duration (minutes)",
       caption = "Boxplot explanation: The box encloses the middle 50% of trips (from 25% to 75%).\nThe thick line inside is the median (50%). Whiskers show the typical range (excluding extreme outliers).") +
  scale_y_continuous(limits = c(0, 60)) +
  theme_minimal() +
  theme(legend.position = "none",
        axis.title.y = element_text(margin = margin(r = 25), size = 13),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 10),
        plot.title = element_text(face = "bold", size = 15, margin = margin(b = 15)),
        plot.subtitle = element_text(size = 12, margin = margin(b = 15)),
        plot.caption = element_text(hjust = 0, size = 10, color = "gray50")) +
  # Add labels
  # Q1
  stat_summary(fun = quantile, fun.args = list(probs = 0.25), geom = "text",
               aes(label = "Q1 (25%)"), hjust = -0.4, vjust = 1.1, size = 3.5, color = "black") +
  # Median
  stat_summary(fun = median, geom = "text",
               aes(label = "Median (50%)"), hjust = -0.1, vjust = -0.5, size = 4, color = "black", fontface = "bold") +
  # Q3
  stat_summary(fun = quantile, fun.args = list(probs = 0.75), geom = "text",
               aes(label = "Q3 (75%)"), hjust = -0.4, vjust = -0.5, size = 3.5, color = "black")

Conclusiones Clave

  • Las duraciones de los viajes son generalmente cortas, con una mediana de ~16,1 minutos y un promedio de ~21,7 minutos — típico en sistemas de bicicletas compartidas urbanas (traslados rápidos o trámites).

  • Los viajes en días laborables tienden a ser ligeramente más cortos (más enfocados en el desplazamiento), mientras que los viajes de fin de semana muestran mayor variación (probablemente incluyendo ocio o recorridos recreativos más largos).

  • La mayoría de los viajes (75% o más) duran menos de ~26 minutos, lo que indica un uso eficiente y de corta distancia.

Recomendaciones Potenciales

  • Aprovechar el límite gratuito existente de 30 minutos en días laborables promoviendo viajes cortos y eficientes (ej. campañas que incentiven desplazamientos menores a 30 minutos para evitar cargos).

  • Explorar la extensión del tiempo gratuito o del número de viajes en fines de semana para impulsar el uso recreativo, ya que los viajes de fin de semana muestran más variación y potencial para recorridos de ocio más largos.

5.6 Uso por Género

Este gráfico de barras muestra el porcentaje de viajes por género, proporcionando información demográfica.

trips_clean %>%
  filter(!is.na(gender)) %>% # Remove NA
  count(gender) %>%
  mutate(pct = n / sum(n) * 100) %>%
  ggplot(aes(x = gender, y = pct, fill = gender)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = paste0(round(pct, 1), "%")), vjust = -0.5, size = 4) +
  scale_fill_manual(values = c("MALE" = "steelblue", "FEMALE" = "darkorange", "OTHER" = "gray")) +
  labs(title = "Percentage of Trips by Gender (2024)",
       x = "Gender",
       y = "Percentage (%)") +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  theme_minimal() +
  theme(legend.position = "none",
        axis.title.y = element_text(margin = margin(r = 25), size = 13),
        axis.title.x = element_text(margin = margin(t = 25), size = 13),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 10),
        plot.title = element_text(face = "bold", size = 15, margin = margin(b = 15)),
        plot.subtitle = element_text(size = 12, margin = margin(b = 15)))

Conclusiones Clave – Uso por Género

  • Los usuarios masculinos representan la mayoría de los viajes (~60,8%), mientras que las usuarias femeninas constituyen ~31,6%.

  • Un pequeño porcentaje (~7,3%) se clasifica como “Otro”, con valores faltantes insignificantes (0,3%).

  • El desequilibrio de género sugiere oportunidades para aumentar la participación femenina mediante campañas específicas, mejoras de seguridad o marketing inclusivo.

Recomendaciones Potenciales

  • Lanzar iniciativas para impulsar el uso femenino (ej. eventos exclusivos para mujeres, características de seguridad o promociones dirigidas a trabajadoras/estudiantes).

  • Monitorear las tendencias de género a lo largo del tiempo para evaluar el impacto de los esfuerzos de inclusión.

5.7 Viajes por Mes (Estacionalidad)

Este gráfico de barras muestra el número total de viajes por mes en 2024, revelando patrones estacionales de uso.

trips_clean %>%
  count(month) %>%
  mutate(month = factor(month, levels = month.name)) %>%  # Sort chronologically
  ggplot(aes(x = month, y = n, fill = n)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = comma(n)), vjust = -0.5, size = 3.5, color = "black") +
  scale_fill_gradient(low = "lightblue", high = "darkblue") +
  labs(title = "Total Trips by Month (2024)",
       subtitle = "Seasonal usage patterns throughout the year",
       x = "Month",
       y = "Number of Trips") +
  scale_y_continuous(labels = comma) +
  theme_minimal() +
  theme(
    axis.title.y = element_text(margin = margin(r = 25), size = 13),
    axis.title.x = element_text(margin = margin(t = 25), size = 13),
    axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
    axis.text.y = element_text(size = 10),    
    plot.title = element_text(face = "bold", size = 15, margin = margin(b = 15)),
    plot.subtitle = element_text(size = 12, margin = margin(b = 15))
  )

Conclusiones Clave

  • El uso alcanza su pico en la primavera tardía y el inicio del verano (septiembre–diciembre), con los valores más altos en octubre (335.005 viajes) y noviembre (322.054 viajes).

  • El menor uso ocurre en invierno (junio–agosto), con el mínimo en julio (215.163 viajes), probablemente debido al clima más frío en Buenos Aires.

  • En general, existe un claro patrón estacional: mayor cantidad de viajes durante los meses cálidos (primavera tardía/verano temprano) y menor en invierno, típico de los sistemas de bicicletas compartidas al aire libre en el hemisferio sur.

Recomendaciones Potenciales

  • Aumentar la disponibilidad de bicicletas y los esfuerzos de marketing durante la temporada alta (septiembre–diciembre) para aprovechar la mayor demanda.

  • Considerar operaciones reducidas o incentivos específicos en los meses de invierno (junio–agosto) para mantener el compromiso durante los períodos de menor uso.

5.8 Flujo Neto por Estación (Orígenes vs Destinos)

Este gráfico de barras horizontal muestra el flujo neto de bicicletas por estación (destinos menos orígenes, es decir, llegadas menos partidas) en 2024.

Los valores positivos (azul) indican exceso de bicicletas (más llegadas, es decir, destinos).

Los valores negativos (naranja) indican escasez de bicicletas (más partidas, es decir, orígenes).

Las estaciones están ordenadas desde el mayor flujo neto positivo (arriba) hasta el menor negativo (abajo).

# Count departures by station
departures <- trips_clean %>%
  count(origin_station_name, name = "departures")

# Count arrivals by station
arrivals <- trips_clean %>%
  count(destination_station_name, name = "arrivals")

# Join and calculate net_flow
net_flow <- departures %>%
  full_join(arrivals, by = c("origin_station_name" = "destination_station_name")) %>%
  mutate(
    departures = coalesce(departures, 0),
    arrivals = coalesce(arrivals, 0),
    net_flow = arrivals - departures,
    station_clean = fct_reorder(origin_station_name, net_flow, .desc = FALSE) # Sort desc by real net_flow
  ) %>%
  filter(abs(net_flow) > 1000) %>%
  top_n(20, abs(net_flow))

# Net flow bar
net_flow %>%
  ggplot(aes(x = net_flow, y = station_clean, fill = net_flow > 0)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = comma(net_flow)), hjust = ifelse(net_flow > 0, -0.1, 1.1), size = 3.5) +
  scale_fill_manual(values = c("TRUE" = "steelblue", "FALSE" = "darkorange")) +
  labs(title = "Net Bike Flow by Station (Top 20 Extremes)",
       subtitle = "Negative = excess departures (need addition), Positive = excess arrivals (need removal)",
       x = "Net Flow (Arrivals - Departures)",
       y = "Station Name") +
  scale_x_continuous(labels = comma) +
  theme_minimal() +
  theme(legend.position = "none",
        axis.title.y = element_text(margin = margin(r = 25), size = 16),
        axis.title.x = element_text(margin = margin(t = 25), size = 16),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 10),
        plot.title = element_text(face = "bold", size = 18, margin = margin(b = 15)),
        plot.subtitle = element_text(size = 14, margin = margin(b = 15)))

Conclusiones Clave

  • Estaciones como Juan Manuel De Blanes (+3.056) y Parque Lezama (+2.871) tienen el mayor flujo neto positivo (exceso de llegadas, es decir, destinos) — acumulan bicicletas y requieren retiro.

  • Estaciones como Cerrito (-2.064) y Aduana (-1.549) presentan el mayor flujo neto negativo (exceso de partidas, es decir, orígenes) — pierden bicicletas y necesitan reposición.

  • Los extremos principales destacan estaciones que requieren reequilibrio frecuente para mantener la disponibilidad.

Recomendaciones Potenciales

  • Priorizar el reequilibrio en estaciones con flujo neto positivo (retirar bicicletas en exceso).

  • Priorizar el reequilibrio en estaciones con flujo neto negativo (agregar bicicletas).

  • Usar modelos predictivos para anticipar el flujo neto diario según la hora del día y patrones históricos.

6 Panel Interactivo en Tableau

Para hacer el análisis más interactivo y exploratorio, se creó un panel en Tableau con el dataset limpio.

Aspectos Destacados del Panel

  • Mapas de calor de orígenes y destinos (interactivos con filtros por hora del día, tipo de día, mes y género).
  • Gráfico de barras de horas pico.
  • Gráfico de barras de viajes por mes.
  • Top 10 estaciones de origen.
  • Diagrama de caja de duración de viajes por tipo de día.
  • Gráfico de barras de uso por género.

Enlace al panel interactivo completo

Ver el Dashboard Ecobici 2024 en Tableau Public

Captura de Pantalla

Ecobici 2024 Dashboard
Ecobici 2024 Dashboard

7 Conclusión

Este proyecto analizó más de 3,2 millones de viajes de Ecobici en 2024 para descubrir patrones clave de uso en el sistema público de bicicletas compartidas de Buenos Aires.

Principales Hallazgos

  • Fuerte enfoque en desplazamientos: pico vespertino (16–18 hs, hasta 308k viajes/hora) y pico secundario matutino (7–10 hs).

  • Promedio en días laborables ~10.724 viajes/día vs ~4.081 en fines de semana (relación ~2,6:1).

  • Principales estaciones de origen (Constitución 35k, Pacífico 34k) y mapas de calor espaciales muestran demanda concentrada en áreas centrales (Microcentro, Palermo, Recoleta).

  • Los orígenes matutinos se agrupan en zonas residenciales/centrales, los destinos vespertinos se dispersan, confirmando el flujo bidireccional de desplazamientos.

  • Duración de viajes: mediana ~16,1 min, promedio ~21,7 min — la mayoría menores a 30 min.

  • Distribución por género: Hombres ~60,8%, Mujeres ~31,6%.

  • Picos estacionales en primavera tardía/verano temprano (octubre 335k), mínimos en invierno (julio 215k).

  • Extremos de flujo neto (ej. Juan Manuel De Blanes +3.056, Cerrito -2.064) destacan necesidades de reequilibrio.

Lecciones Aprendidas

  • Las fuentes de datos abiertos pueden tener inconsistencias (ej. incompatibilidad de IDs de estaciones) — confiar en coordenadas integradas aseguró cobertura completa sin dependencias externas.

  • Las técnicas de normalización (como promedios por día) son críticas para evitar conclusiones engañosas al comparar grupos de tamaños desiguales.

  • El muestreo (10.000 viajes) logra un equilibrio práctico entre rendimiento interactivo y precisión analítica en visualizaciones a gran escala.

  • La terminología consistente (ej. orígenes/destinos en lugar de mezclar con partidas/llegadas) y las explicaciones claras mejoran significativamente la legibilidad y la comprensión de los interesados.

Próximos Pasos

  • Incorporar datos en tiempo real para el reequilibrio dinámico.

  • Explorar demografía de usuarios (edad, género) y modelos predictivos para pronóstico de demanda.

  • Expandir al dataset de 2025 cuando esté disponible.

Este informe reproducible demuestra habilidades en R (tidyverse, leaflet), limpieza de datos, EDA, visualización, dashboards interactivos (Tableau) e insights de negocio — listo para roles de analista de datos.

Ver el informe completo en línea (con mapas de calor interactivos):

Abrir el Informe Ecobici 2024 en mi sitio web
(Versión interactiva con zoom, desplazamiento y tooltips — no requiere descarga.)

Panel Interactivo

Explora el dataset completo de manera interactiva en Tableau Public:
Ver el Dashboard Ecobici 2024

Descargar Informe en PDF

Para una versión imprimible:

Descargar PDF
(Informe completo con todas las visualizaciones e insights — 12 páginas)