El proyecto de GoMotion consiste en la creación de un sistema de predicción de picos de movilidad utilizando datos históricos de Telefónica combinados con datos públicos como son archivos meteorológicos y calendarios de eventos. Se implementan modelos predictivos que anticipan flujos de movilidad con la finalidad de mejorar la red de movilidad local.
For the English version of this readme, go here.
- GoMotion ha sido creado con la intención de hacer una predicción de la movilidad en Barcelona para poder detectar fuertes picos y comprender de dónde vienen. La falta de datos precisos e útiles sobre el transporte público en Barcelona hace que sea complicado intentar proponer cambios en el transporte para lidiar con éstos. Sin embargo, hemos creado un modelo capaz de predecir picos de movilidad con gran precisión que hace que, teniendo datos de calidad sobre el transporte público de Barcelona (como proporciones de trayectos en transporte privado / público, capacidades exactas de autobuses y metro, número diario de buses/metro de cada línea, etc...), sea fácil proponer cambios en éste para hacer frente a dichos picos.
- Para realizar un proyecto realmente útil es importante construir una buena base, por ello hemos creado un documento
mathematical_model.pdfcuyo objetivo es crear una abstracción matemática de la ciudad que permita modelar las propiedades reales a partir de un conjunto de datos proporcionados. - El alcance predictivo del programa es el estipulado de 7 días. Una implementación completa requeriría una conexión con los datos de Telefónica a tiempo real (granularidad diaria). Puesto que esto no es posible, la aplicación incluye un proceso de "relleno de datos", donde se crean datos utilizando el modelo predictivo desde la última fecha con datos determinados (31 de agosto de 2025) hasta el día de ejecución. Esto provoca una acumulación de error a medida que la fecha de predicción se aleja de la última estipulada (dado que hacemos predicciones sobre predicciones), por lo que los datos que se observan pueden presentar discrepancias mayores con la realidad que las medidas por las herramientas del programa.
- Python 3.12+
- Conexión a internet
- Acceso a la carpeta
output_limite_legalproporcionada por Telefónica (de no ser el caso, se pueden generar datos usandoexample_data.py) - Acceso a los archivos
barris.csv,events.csvyholidays.csv - Una llave de API para Gemini
Para poder utilizar GoMotion, es necesario crear un entorno virtual y descargar las librerías necesarias.
- Crear el entorno virtual desde la raíz:
python -m venv .venv - Activar el entorno virtual:
- MacOS / Linux:
source .venv/bin/activate - Windows PowerShell / Windows Cmd:
.venv\Scripts\Activate.ps1/.venv\Scripts\activate
- Descargar librerías
pip install -r requirements.txt - Añadir API key en
.env:GEMINI_API_KEY="key"
- Nota: dependiendo del tier de Gemini API que tenga el usuario, es posible que el regenerado repetido del programa consuma el límite de requests.
GoMotion/
├── data/
│ ├── output_limite_legal # Datos proporcionados por Telefónica
| └── barris.csv # Datos públicos de OpenData BCN
│ └── events.csv # Registro de eventos
| └── holidays.csv # Registro de festivos sacados del ajuntament de Barcelona
│
├── media/
| └── analysis.png
│ └── GoMotionLogo.png
| └── GoMotionShortLogo.png
| └── GoMotionShortLogo.ico
| └── barri_list.png
| └── daily_metrics.png
| └── general_stats.png
| └── heatmap.png
| └── model_analysis.png
│
├── stats/
| ├── stats/
| | └── statistics_day_of_the_week.py # Exceso de intensidad promedia por día de la semana
| | └── statistics_month.py # Exceso de intensidad promedia por mes del año
| |
│ ├── llm_scraper.py # Extracción de eventos y festivos con web scrapping
│ └── barri_manager.py # Creación del "mapa/grafo" con los barrios de Barcelona
| └── metadata_manager.py # Metadatos
| └── stats.py # Cálculo de estadísticas útiles
| └── xgb_model.py # Modelo de xgb para la predicción
| └── event_encoder # Conversión de eventos para una mejor predicción
| └── hyperparameter_optimizer.py # Entrenamiento y optimización de modelo de predicción
| └── peak_classifier.py # Definición y clasificación de picos
| └── data_filler.py # Realizar Predicciones (1 semana)
| └── meteo.py # Archivo y predicción meteorológica
| └── intensities.py # Modelo matemático para definir las intensidades
| └── pipeline.py # Pipeline y archivo principal del proyecto
| └── dashboard.py # Visualización con Streamlit
| └── example_data.py # Generación de datos de prueba simulando datos reales
│
├── .env # Variables de entorno (API Keys - No subir al repo)
└── requirements.txt # Dependencias del proyecto
└── mathematical_model.pdf # Documento con fundamentos matemáticos
Para acceder a todas las funciones del proyecto es suficiente con ejecutar el pipeline.
Desde la raíz del proyecto MacOS / Linux: python3 src/pipeline.py, Windows Cmd: python src/pipeline.py.
- Si no se dispone de la carpeta
output_limite_legal, se debe ejecutar anteriormente el archivosrc/example_data.pypara crear los datos de ejemplo. - Nota: Es normal que la primera vez que se ejecute
pipeline.pytarde un buen rato. El programa tiene que hacer una serie de procesos con costes computacionales relativamente alto. Sin embargo, una vez ejecutado, los archivos se guardan y sólo necesitan actualizarse poco a poco, lo que reduce drásticamente el tiempo de espera.
Una vez descargados los datos movilidad entre barrios proporcionados por Telefónica, eliminaremos las columnas irrelevantes para nuestro proyecto, y nos quedaremos únicamente con
las columnas siguientes: day, barrio_origen_name, barrio_destino_name y viajes.
Utilizaremos el dataset resultante y el modelo explicado en el pdf adjunto para crear un nuevo dataset con las siguientes columnas: day, barri, intensity.
Estas funciones se implementan en los archivos barri_manager.py y intensities.py.
Para mejorar la precisión de nuestro modelo utilizaremos datos históricos de eventos, festivos y meteorología.
Los archivos de eventos y festivos han sido recopilados a mano y se encuentran en data/events.csv y data/holidays.csv respectivamente.
Cada evento tiene asociados una fecha, una categoría, una lista de barrios en los que tiene lugar, y un impacto (del 1 al 5) relativo a su categoría. Los festivos únicamente tienen asociada una fecha.
Para recopilar datos meteorológicos utilizaremos la API de OpenMeteo, en el archivo meteo.py. Por simplicidad y porque creemos que es lo mejor para el modelo, recopilaremos para cada día, únicamente el nivel de lluvia y las temperaturas máxima y mínima.
También por simplicidad, y porque es prácticamente imposible tener controlados todos los eventos de una ciudad tan activa como Barcelona, hemos seleccionado para nuestro análisis los eventos que creemos más relevantes. Es decir:
- Eventos del Estadio Olímpico
- Partidos locales del Fútbol Club Barcelona
- Festivales de música: Primavera Sound, Crüilla y Sonar
- Eventos de Running: Cursa del Corte Inglés y Zurich Maratón de Barcelona
- Eventos de la Fira de Barcelona
- Fiestas Populares: Sant Jordi, La Diada y La Mercè
A lo largo del uso del programa hay información que necesita compartirse entre varios archivos y entre sesiones que no corresponde a datos en sí. Esta información la denominamos metadatos y la almacenamos en data/metadata.py (archivo creado automáticamente). No conviene modificar manualmente los metadatos, pues pueden haber comportamientos inesperados.
Este proyecto hace uso de la API de Google Gemini. Su utilización está sujeta a la posesión de una llave que deberá almacenarse en un archivo .env en el mismo directorio que el resto de carpetas.
GoMotion es capaz de predecir las intensidades de cada barrio con 7 días de antelación.
De la misma manera que para los datos meteorológicos históricos, utilizaremos la API de OpenMeteo (en meteo.py) para obtener las predicciones de temperatura y lluvia de la semana siguiente.
Para conocer los eventos y los de la semana siguiente hemos creado un sistema de web-scrapping. En llm_scrapper.py, utilizamos playwright y bs4 para obtener el texto de las páginas web de nuestro interés (mencionadas en el apartado 1.2.1), y un LLM (gemini) que formatea los eventos y festivos encontrados para dejarlos listos para entrenar al modelo.
Con la finalidad de extraer la información más importante y usarla en nuestro modelo de predicción, codificamos los eventos de un día en un barrio en un espacio latente de 5 dimensiones. Para ello entrenamos 2 modelos: un codificador y un descodificador. Esta arquitectura permite entrenar un modelo usando los eventos como input y validation al mismo tiempo, pues el descodificador debería actuar como una función inversa y llevar el vector del espacio latente de vuelta al espacio de eventos.
Una vez tenemos el modelo entrenado, guardamos la primera mitad del modelo entrenado en models/encoder.keras. Además, codificamos los eventos hasta la fecha necesaria (una semana a partir del día en el que se ejecuta el programa) usando el codificador. El resultado se guarda en data/encoded_events.csv.
La predicción de los datos se hará usando un modelo de extreme gradient boosting (XGBoost), dado que es una arquitectura que:
- permite la fácil integración de características categóricas
- tiene documentación y uso extensos
- es una de las más adecuadas para tratar datos de naturaleza tabular (todos nuestros datos se estructuran en archivos
.csv)
La definición del modelo y las herramientas para su entrenamiento y su optimización se encuentran en el archivo hyperparameter_optimizer.py.
A continuación están las características utilizadas y su descripción:
| Característica | Tipo | Descripción |
|---|---|---|
| barri | Categórica | Nombre del barrio |
| temperature_2m_max | Numérica | Temperatura máxima para el día (ºC) |
| temperature_2m_min | Numérica | Temperatura mínima para el día (ºC) |
| precipitation_sum | Numérica | Suma de mm de precipitación en el día |
| is_holiday | Booleana | Si el día es festivo o no |
| month_cat | Categórica | Nombre del mes |
| day_cat | Categórica | Día de la semana |
| lag_7 | Numérica | Intensidad calculada hace 7 días |
| lag_14 | Numérica | Intensidad calculada hace 14 días |
| lag_21 | Numérica | Intensidad calculada hace 21 días |
| lag_28 | Numérica | Intensidad calculada hace 28 días |
| dt_7_w1 | Numérica | Derivada discreta de la semana más reciente: (lag_7 - lag_14) / 7 |
| dt_7_w2 | Numérica | Derivada discreta de la segunda semana más reciente: (lag_14 - lag_21) / 7 |
| enc1 | Numérica | Coordenada 1 de la codificación de los eventos para el día en el barrio especificado |
| enc2 | Numérica | Coordenada 2 de la codificación de los eventos para el día en el barrio especificado |
| enc3 | Numérica | Coordenada 3 de la codificación de los eventos para el día en el barrio especificado |
| enc4 | Numérica | Coordenada 4 de la codificación de los eventos para el día en el barrio especificado |
| enc5 | Numérica | Coordenada 5 de la codificación de los eventos para el día en el barrio especificado |
El archivo permite entrenar el modelo en una variedad de hiperparámetros. Para ello implementa un grid search personalizado: dada una colección de rangos de hiperparámetros, se entrenan modelos para todos los elementos de su producto cartesiano y se guarda aquel con mayor precisión (evaluada según los aciertos en la predicción). Los hiperparámetros que se pueden ajustar son:
- Base de los pesos: el modelo XGBoost asigna unos pesos a los errores de cada muestra según su z-score con la siguiente formula:
$weight = max(1, base ^{\text{z-score}})$ . Aumentar el hiperparámetro base pondera más fuertemente los errores en días con movilidad excesiva. - Learning rate: se permite modificar el learning rate para variar el ritmo de convergencia y explorar nuevos mínimos. El valor especial
Noneimplementa el siguiente schedule:
| Ronda de corte | Learning rate |
|---|---|
| 0 | 0.1 |
| 100 | 0.001 |
| 150 | 0.0001 |
- Tree depth: también se permite modificar el número máximo de árboles que puede usar el modelo (más información en https://xgboosting.com/configure-xgboost-max_depth-parameter/)
Cuando se escoge el mejor modelo, se guarda en models/regressor.joblib.
Con el fin de entender mejor las predicciones, hemos implementado un dashboard utilizando streamlit.
Nada más entrar al dashboard se llamará a la función fill_data(), que se encuentra en data_filler.py. Así nos aseguramos de tener los datos y las predicciones de los días que nos interesan.
Tendremos la opción de seleccionar una fecha. Una vez seleccionada, se nos mostrarán algunas métricas relevantes de ese día: temperatura, lluvia, eventos, festivos y el tráfico total del día. Además obtenemos una comparación histórica de estos valores. En concreto:
Temperatura y Precipitaciones: Comparación con la media de los últimos 30 días.
Eventos: Comparación con la media de eventos diarios que hubo en los últimos 30 días.
Tráfico Total: Comparación con la media de los 4 últimos días con el mismo día de la semana (por ej. Domingo)
Para el día seleccionado se nos mostrará una tabla con, para cada barrio, su intensidad, la media histórica de esta, su z-score, su densidad (intensidad/superficie), y el típo de pico frente al que estamos (definidos en matematical_model.pdf y peak_classifier.py).
Además se mostrará un mapa de calor de las intensidades de cada barrio. Estas intensidades son relativas al barrio, es decir: se considera que hay un pico cuando el valor se aleja mucho de lo que suele haber en ese barrio. Podremos hacer clic en un barrio en concreto para seleccionarlo.
Una vez seleccionado un barrio en el mapa, obtendremos una serie de gráficas que explican cómo afecta cada variable a la intensidad histórica de ese barrio. En concreto:
- Impacto festivo/laborable
- Impacto de cada tipo de evento
- Impacto del día de la semana
- Impacto del mes
- Impacto de la precipitación
Encontraremos un análisis detallado del modelo, para tener una visión más general de cuáles son los parámetros que afectan en mayor medida a la intensidad de la movilidad en Barcelona. Además, podremos ver el porcentaje de precisión de picos acertados del modelo, el porcentaje de picos subestimados y el porcentaje de picos sobrestimado.
Finalmente, se podrá visualizar las siguientes estadísticas de todos los barrios: exceso de intensidad promedio como porcentaje según el día de la semana y exceso de intensidad promedio como porcentaje según el mes del año.
- Javier Badesa Pérez
- Alexander Cameron Hall Parrilla
- Oscar Senesi Aladjem
- Dan Manuel Vancea Boros






