Este script implementa un sistema de recomendación de ciudades basado en Random Forest Regressor, un algoritmo de aprendizaje supervisado. A diferencia de los enfoques basados en similitud (como KNN), este sistema utiliza datos históricos de usuarios con ratings reales para aprender patrones complejos entre las características de los usuarios, las características de las ciudades y los ratings que los usuarios asignaron.
El recomendador híbrido permite:
- Entrenar un modelo supervisado con historial de usuarios y sus ratings
- Aprender patrones complejos entre características de usuario y ciudad
- Predecir qué rating daría un usuario a cada ciudad disponible
- Generar recomendaciones basadas en ratings predichos (mayor rating = mejor recomendación)
El script utiliza Random Forest Regressor con:
- Algoritmo: Ensemble de árboles de decisión (100 árboles por defecto)
- Enfoque: Aprendizaje supervisado (requiere datos históricos con ratings)
- Modelo híbrido: Combina características del usuario con características de la ciudad
- Ventajas: Robusto a outliers, captura interacciones no lineales, no requiere normalización estricta
| Característica | KNN | Random Forest |
|---|---|---|
| Enfoque | No supervisado (similitud) | Supervisado (aprendizaje) |
| Datos requeridos | Solo CSV de ciudades | CSV de ciudades + CSV de historial con ratings |
| Output | Score de similitud (0-1) | Rating predicho (valor numérico) |
| Aprendizaje | No aprende de datos históricos | Aprende patrones de ratings históricos |
| Uso recomendado | Cuando no hay datos históricos | Cuando hay historial de usuarios con ratings |
La clase HybridRecommender encapsula toda la lógica del sistema de recomendación híbrido.
-
Modelo Random Forest (
self.model)- Instancia de
RandomForestRegressorde scikit-learn - Configurado con 100 estimadores (árboles) y random_state=42 para reproducibilidad
- Se entrena con datos combinados: [User_Features] + [City_Features] → Rating
- Instancia de
-
Base de Datos de Ciudades (
self.cities_df)- DataFrame de pandas que contiene toda la información de las ciudades
- Incluye nombres de ciudades y todas sus características numéricas
- Se guarda junto con el modelo para permitir predicciones futuras
-
Orden de Features (
feature_order)- Se guarda el orden exacto de las columnas usadas en el entrenamiento
- Crítico para asegurar que las predicciones usen el mismo orden de features
Entrenamiento:
CSV Ciudades + CSV Historial → JOIN por nombre → Combinar features → Entrenar Random Forest → Guardar modelo
Predicción:
Cargar modelo → Replicar features usuario → Combinar con features ciudades → Predecir ratings → Ordenar por rating
Entrena el modelo Random Forest combinando el historial de usuarios con las características de las ciudades.
Proceso:
-
Carga de datos:
- Lee el CSV de ciudades (
cities_path) - Lee el CSV de historial (
history_path)
- Lee el CSV de ciudades (
-
Feature Engineering - JOIN:
- Realiza un JOIN entre
df_historyydf_citiesusandoCity_Name(historial) yCity(ciudades) - El resultado contiene: [User_F1..F10] + [City_F1..F10] + [Rating]
- Realiza un JOIN entre
-
Preparación de datos:
- Excluye columnas no numéricas:
City_Name,City,Rating - Todas las demás columnas se usan como features (X)
Ratinges el target (y)
- Excluye columnas no numéricas:
-
Entrenamiento:
- Entrena el Random Forest con los datos combinados
- El modelo aprende: [User_Features + City_Features] → Rating
-
Persistencia:
- Guarda un diccionario con:
model: Modelo Random Forest entrenadocities_df: Base de datos completa de ciudadesfeature_order: Orden exacto de las columnas usadas
- Guarda un diccionario con:
Parámetros:
cities_path: Ruta al archivo CSV con los datos de ciudadeshistory_path: Ruta al archivo CSV con el historial de usuarios y ratingsmodel_out: Ruta donde guardar el modelo entrenado (.pkl)
Salida:
- Mensaje indicando cuántos registros históricos se usaron para entrenar
- Mensaje confirmando dónde se guardó el modelo
Predice qué rating daría un usuario a cada ciudad disponible.
Proceso:
-
Carga del modelo:
- Valida que el archivo del modelo exista
- Carga el diccionario con el modelo, ciudades y orden de features
-
Preparación de datos de predicción:
- Replica las 10 features del usuario tantas veces como ciudades hay
- Crea un DataFrame con las features del usuario (User_F1 a User_F10)
- Obtiene las features numéricas de todas las ciudades
- Concatena horizontalmente: [User_Features] + [City_Features] para cada ciudad
-
Predicción:
- El modelo predice un rating para cada combinación usuario-ciudad
- Cada predicción representa qué rating daría ese usuario a esa ciudad
-
Formateo de resultados:
- Añade la columna
Predicted_Ratingal DataFrame de ciudades - Ordena las ciudades por rating predicho descendente (mejores primero)
- Añade la columna
Parámetros:
model_path: Ruta al archivo.pkldel modelo entrenadouser_features: Lista de 10 valores numéricos representando las características del usuario
Retorna:
- DataFrame con todas las ciudades ordenadas por
Predicted_Ratingdescendente - Incluye todas las columnas originales de las ciudades más la columna
Predicted_Rating
Excepciones:
FileNotFoundError: Si el archivo del modelo no existe
El archivo CSV de ciudades debe cumplir con la siguiente estructura:
- Columna
City: Nombre de la ciudad (texto) - REQUERIDA para el JOIN - Siguientes columnas: Características numéricas de la ciudad (pueden ser 10 o más)
Estructura esperada:
City,Caracteristica1,Caracteristica2,Caracteristica3,Caracteristica4,Caracteristica5,Caracteristica6,Caracteristica7,Caracteristica8,Caracteristica9,Caracteristica10
Barcelona,8.5,7.2,9.0,6.5,8.0,7.8,9.2,6.0,8.5,7.5
Madrid,7.8,8.0,8.5,7.2,7.5,8.2,8.0,7.8,7.0,8.5
Tokyo,9.0,9.5,8.5,9.2,6.0,8.8,9.0,8.5,9.0,8.7Requisitos:
- Debe tener encabezados (nombres de columnas)
- La columna
Citydebe existir y contener los nombres de las ciudades - Las características deben ser valores numéricos (int o float)
- No deben existir valores faltantes (NaN) en las columnas numéricas
El archivo CSV de historial es crítico para el entrenamiento. Debe contener:
- 10 columnas de características del usuario: F1, F2, F3, ..., F10
- Columna
City_Name: Nombre de la ciudad visitada (debe coincidir conCitydel CSV de ciudades) - Columna
Rating: Rating que el usuario dio a esa ciudad (target para el modelo)
Estructura esperada:
F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,City_Name,Rating
6,3,10,7,4,6,9,2,6,10,Bali,8.4
10,7,4,3,7,7,2,5,4,1,Oslo,7.2
7,5,1,4,0,9,5,8,0,10,Tokyo,6.5Ejemplo completo con datos sintéticos:
F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,City_Name,Rating
6,3,10,7,4,6,9,2,6,10,Bali,8.4
10,7,4,3,7,7,2,5,4,1,Oslo,7.2
7,5,1,4,0,9,5,8,0,10,Tokyo,6.5
10,9,2,6,3,8,2,4,2,6,Dubai,7.7
4,8,6,1,3,8,1,9,8,9,Medellin,7.4
4,1,3,6,7,2,0,3,1,7,Berlin,6.3
3,1,5,5,9,3,5,1,9,1,New York,5.6
9,3,7,6,8,7,4,1,4,7,Berlin,6.9
9,8,8,0,8,6,8,7,0,7,Buenos Aires,6.9
7,10,2,0,7,2,2,0,10,4,Madrid,6.3
9,6,9,8,6,8,7,1,0,6,Medellin,8.6
6,7,4,2,7,5,10,2,0,2,Buenos Aires,6.0
4,2,0,4,9,6,6,10,8,9,Tokyo,7.6
9,2,6,0,3,3,4,6,6,10,New York,6.4
3,6,10,2,5,1,9,8,4,5,Oslo,8.1
3,10,9,6,8,6,0,0,8,10,Medellin,6.3
8,3,8,2,6,5,7,10,8,4,Medellin,7.5
0,2,9,7,10,5,7,8,3,0,Madrid,7.4
0,9,3,6,1,2,0,4,0,7,Sydney,5.1
0,10,0,1,1,5,6,4,0,0,Medellin,5.1Requisitos críticos:
- Orden de columnas: F1, F2, ..., F10, City_Name, Rating (en ese orden)
- City_Name debe coincidir: Los nombres en
City_Namedeben existir en la columnaCitydel CSV de ciudades - Rating numérico: Debe ser un valor numérico (float o int), típicamente en escala 0-10
- Múltiples usuarios: Puede contener historial de múltiples usuarios (el modelo aprende patrones generales)
- Múltiples ratings por ciudad: Una misma ciudad puede aparecer múltiples veces con diferentes usuarios/ratings
Interpretación de Ratings:
- 0-2: Muy bajo / No le gustó
- 3-4: Bajo / Poco satisfactorio
- 5-6: Medio / Aceptable
- 7-8: Alto / Muy satisfactorio
- 9-10: Muy alto / Excelente
Para realizar predicciones, se requieren 10 valores numéricos que representan las características del usuario.
Formato:
- 10 valores numéricos (float) separados por espacios
- Cada valor representa una característica del usuario
- El orden debe ser: F1, F2, F3, ..., F10
Ejemplo:
8.5 7.2 9.0 6.5 8.0 7.8 9.2 6.0 8.5 7.5
Importante: Las características del usuario deben representar los mismos aspectos que las características F1-F10 en el CSV de historial. El orden es crítico.
El script se ejecuta desde la línea de comandos y soporta dos subcomandos principales.
Entrena el modelo con archivos CSV de ciudades e historial, y lo guarda en disco.
Sintaxis:
python Random-Forest-Predictivo.py train --cities <ruta_csv_ciudades> --history <ruta_csv_historial> [--out <archivo_modelo>]Parámetros:
--cities(requerido): Ruta al archivo CSV con los datos de ciudades--history(requerido): Ruta al archivo CSV con el historial de usuarios y ratings--out(opcional): Nombre del archivo donde guardar el modelo. Por defecto:hybrid_model.pkl
Ejemplo:
python Random-Forest-Predictivo.py train --cities ciudades.csv --history historial.csv --out mi_modelo.pklSalida esperada:
Entrenando con 20 registros históricos...
Modelo híbrido guardado en: mi_modelo.pkl
Carga un modelo pre-entrenado y predice ratings para todas las ciudades basándose en las características del usuario.
Sintaxis:
python Random-Forest-Predictivo.py predict [--model <archivo_modelo>] <característica1> <característica2> ... <característica10>Parámetros:
--model(opcional): Ruta al archivo del modelo entrenado. Por defecto:hybrid_model.pklfeatures(requerido): 10 valores numéricos separados por espacios, representando las características del usuario (F1 a F10)
Ejemplo:
python Random-Forest-Predictivo.py predict --model mi_modelo.pkl 8.5 7.2 9.0 6.5 8.0 7.8 9.2 6.0 8.5 7.5Salida esperada:
City Predicted_Rating
Barcelona 8.45
Madrid 8.12
Tokyo 7.89
Oslo 7.65
Medellin 7.43
El modelo se guarda en formato .pkl usando la librería joblib, que es más eficiente que pickle para objetos de NumPy y scikit-learn.
El archivo .pkl contiene un diccionario con tres claves:
{
'model': RandomForestRegressor, # Modelo Random Forest entrenado
'cities_df': DataFrame, # Base de datos completa de ciudades
'feature_order': list # Orden exacto de columnas usadas en entrenamiento
}Es crítico guardar los tres componentes juntos porque:
- El modelo Random Forest contiene los árboles entrenados que aprendieron los patrones
- La base de datos de ciudades contiene los nombres y características para generar predicciones
- El orden de features asegura que las predicciones usen el mismo orden que el entrenamiento
El método predict() retorna un DataFrame con:
- Todas las ciudades de la base de datos
- Todas las columnas originales del CSV de ciudades
- Columna
Predicted_Rating: Rating predicho (valor numérico, típicamente 0-10) - Ordenadas por
Predicted_Ratingdescendente (mejores recomendaciones primero)
Interpretación de Ratings Predichos:
- Rating >= 8.0: Muy alta satisfacción esperada, excelente recomendación
- Rating >= 7.0: Alta satisfacción esperada, buena recomendación
- Rating >= 6.0: Satisfacción media esperada, opción aceptable
- Rating >= 5.0: Satisfacción baja esperada, considerar otras opciones
- Rating < 5.0: Baja satisfacción esperada, no recomendado
El script requiere las siguientes librerías de Python:
argparse: Manejo de argumentos de línea de comandossys: Funciones del sistemaos: Operaciones del sistema de archivos
Instalar con pip:
pip install pandas numpy scikit-learn joblibDependencias:
- pandas: Manipulación y análisis de datos (lectura de CSV, DataFrames, JOINs)
- numpy: Operaciones numéricas y arrays (replicación de features)
- scikit-learn: Algoritmos de machine learning (RandomForestRegressor)
- joblib: Serialización eficiente de objetos Python (guardado/carga de modelos)
- Python 3.7 o superior
- pandas >= 1.0.0
- numpy >= 1.18.0
- scikit-learn >= 0.22.0
- joblib >= 0.14.0
Archivo ciudades.csv:
City,Seguridad,Transporte,Cultura,Gastronomia,Naturaleza,Entretenimiento,Educacion,Salud,Economia,CalidadVida
Barcelona,8.5,9.0,9.5,9.2,7.0,8.8,8.5,8.0,7.5,8.7
Madrid,8.0,9.2,9.0,8.8,6.5,9.0,9.2,8.5,8.0,8.5
Tokyo,9.0,9.5,8.5,9.2,6.0,8.8,9.0,8.5,9.0,8.7
Oslo,9.5,8.5,7.5,7.0,9.5,7.0,8.0,9.0,8.5,9.2
Dubai,8.0,8.0,6.5,7.5,5.0,8.5,7.0,8.0,9.5,8.0
Medellin,7.5,7.0,8.0,8.5,8.5,7.5,7.5,7.5,6.5,8.0
Berlin,8.5,8.5,9.0,8.0,7.0,8.5,8.5,8.5,7.5,8.5
New York,7.5,8.0,9.5,9.0,6.0,9.5,9.0,8.0,9.0,8.0
Buenos Aires,7.0,7.5,8.5,9.0,7.0,8.0,7.5,7.0,6.0,7.5
Sydney,9.0,8.0,8.0,8.5,8.0,8.0,8.5,9.0,8.0,9.0
Bali,8.0,6.0,7.0,7.5,9.5,7.5,6.0,7.0,5.5,8.5Archivo historial.csv:
F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,City_Name,Rating
6,3,10,7,4,6,9,2,6,10,Bali,8.4
10,7,4,3,7,7,2,5,4,1,Oslo,7.2
7,5,1,4,0,9,5,8,0,10,Tokyo,6.5
10,9,2,6,3,8,2,4,2,6,Dubai,7.7
4,8,6,1,3,8,1,9,8,9,Medellin,7.4
4,1,3,6,7,2,0,3,1,7,Berlin,6.3
3,1,5,5,9,3,5,1,9,1,New York,5.6
9,3,7,6,8,7,4,1,4,7,Berlin,6.9
9,8,8,0,8,6,8,7,0,7,Buenos Aires,6.9
7,10,2,0,7,2,2,0,10,4,Madrid,6.3
9,6,9,8,6,8,7,1,0,6,Medellin,8.6
6,7,4,2,7,5,10,2,0,2,Buenos Aires,6.0
4,2,0,4,9,6,6,10,8,9,Tokyo,7.6
9,2,6,0,3,3,4,6,6,10,New York,6.4
3,6,10,2,5,1,9,8,4,5,Oslo,8.1
3,10,9,6,8,6,0,0,8,10,Medellin,6.3
8,3,8,2,6,5,7,10,8,4,Medellin,7.5
0,2,9,7,10,5,7,8,3,0,Madrid,7.4
0,9,3,6,1,2,0,4,0,7,Sydney,5.1
0,10,0,1,1,5,6,4,0,0,Medellin,5.1python Random-Forest-Predictivo.py train --cities ciudades.csv --history historial.csv --out hybrid_model.pklSalida:
Entrenando con 20 registros históricos...
Modelo híbrido guardado en: hybrid_model.pkl
Supongamos que un usuario tiene las siguientes características (F1 a F10):
- F1: 8.5 (Seguridad)
- F2: 7.2 (Transporte)
- F3: 9.0 (Cultura)
- F4: 6.5 (Gastronomía)
- F5: 8.0 (Naturaleza)
- F6: 7.8 (Entretenimiento)
- F7: 9.2 (Educación)
- F8: 6.0 (Salud)
- F9: 8.5 (Economía)
- F10: 7.5 (Calidad de Vida)
python Random-Forest-Predictivo.py predict --model hybrid_model.pkl 8.5 7.2 9.0 6.5 8.0 7.8 9.2 6.0 8.5 7.5Salida esperada:
City Predicted_Rating
Barcelona 8.45
Madrid 8.12
Tokyo 7.89
Oslo 7.65
Medellin 7.43
-
Barcelona (8.45): El modelo predice que este usuario le daría un rating de 8.45 a Barcelona, lo que indica muy alta satisfacción esperada. Es la mejor recomendación.
-
Madrid (8.12): También predice alta satisfacción. Buena opción alternativa.
-
Tokyo (7.89): Satisfacción alta esperada, pero ligeramente menor que las anteriores.
-
Oslo (7.65): Satisfacción alta, opción viable.
-
Medellin (7.43): Satisfacción alta pero menor que las opciones anteriores.
Coincidencia de nombres de ciudades:
- Los nombres en
City_Name(historial) deben coincidir exactamente con los nombres enCity(ciudades) - El JOIN es case-sensitive y debe coincidir exactamente (incluyendo espacios, acentos, etc.)
- Si una ciudad en el historial no existe en el CSV de ciudades, esa fila se excluirá del entrenamiento
Orden de características:
- El orden de las características F1-F10 en el historial debe ser consistente
- El orden de las características del usuario en la predicción debe ser el mismo
- El script actual no valida explícitamente el orden por nombre de columna (asume orden numérico)
-
Nombres de columnas asumidos:
- El script asume que las features del usuario se llaman F1-F10 en el historial
- En predicción, crea columnas User_F1-User_F10, pero no valida que coincidan con el entrenamiento
- Recomendación para producción: Validar que los nombres de columnas coincidan exactamente
-
Orden de features:
- El script asume que el orden numérico se mantiene
- No valida explícitamente que el orden de features de ciudad sea el mismo
- Recomendación para producción: Usar
feature_orderguardado para validar y reordenar columnas
-
Escalabilidad:
- Con muchas ciudades (1000+), la predicción replica las features del usuario muchas veces
- Random Forest es eficiente, pero el proceso de preparación de datos puede ser lento
- Recomendación: Para bases de datos muy grandes, considerar pre-filtrar ciudades o usar batch processing
-
Datos de entrenamiento:
- Mientras más datos históricos, mejor será el modelo
- Idealmente, tener ratings de múltiples usuarios para cada ciudad
- Evitar datos desbalanceados (todas las ciudades con ratings similares)
-
Calidad de datos:
- Validar que no haya valores faltantes (NaN) en los CSVs
- Asegurar que los ratings estén en un rango consistente (ej: 0-10)
- Revisar outliers en ratings que puedan afectar el modelo
-
Reentrenamiento:
- Si se añaden nuevas ciudades o se actualiza el historial, reentrenar el modelo
- Guardar versiones del modelo para comparar performance
- Considerar validación cruzada para evaluar la calidad del modelo
-
Validación del modelo:
- El script actual no incluye métricas de evaluación (R², MAE, RMSE)
- Recomendación: Añadir evaluación del modelo en producción usando un conjunto de prueba
Usa Random Forest cuando:
- Tienes datos históricos de usuarios con ratings reales
- Quieres predecir ratings numéricos específicos
- Necesitas capturar interacciones complejas entre usuario y ciudad
- Tienes suficientes datos históricos (mínimo 20-30 registros, idealmente 100+)
Usa KNN cuando:
- No tienes datos históricos con ratings
- Solo quieres encontrar ciudades similares a las preferencias del usuario
- Necesitas un sistema más simple y rápido de implementar
- Tienes solo el CSV de ciudades sin historial de usuarios
-
Versionado del modelo:
- Guardar modelos con timestamps o versiones
- Documentar qué datos se usaron para entrenar cada modelo
-
Manejo de errores:
- El script valida que el modelo exista antes de predecir
- No valida exhaustivamente el formato de los CSVs
- Recomendación: Añadir validaciones más robustas en producción
-
Performance:
- Random Forest con 100 árboles es rápido para entrenar (< 1 minuto con 1000 registros)
- Predicción es muy rápida (< 1 segundo para 100 ciudades)
- Para bases de datos muy grandes, considerar reducir
n_estimatorso usar modelos más ligeros
-
Reproducibilidad:
- El modelo usa
random_state=42para reproducibilidad - Mismo conjunto de datos siempre produce el mismo modelo
- Cambios en los datos o parámetros producirán modelos diferentes
- El modelo usa