FT: Mejora del modelo añadiendo control de tráfico además de demanda

This commit is contained in:
2026-01-06 21:18:00 +01:00
parent 355fe4092f
commit 7eae70a59e
2 changed files with 63 additions and 13 deletions

View File

@@ -166,6 +166,7 @@ def recommend_gruas():
- dow: día de la semana (0-6, requerido) - dow: día de la semana (0-6, requerido)
- hour: hora del día (0-23, requerido) - hour: hora del día (0-23, requerido)
- k: número de grúas (opcional, default=2) - k: número de grúas (opcional, default=2)
- traffic: nivel de tráfico 1=bajo, 2=medio, 3=alto (opcional, default=1)
- optimize: si es True, encuentra k óptimo (opcional, default=False) - optimize: si es True, encuentra k óptimo (opcional, default=False)
- min_gruas: mínimo de grúas para optimización (opcional, default=1) - min_gruas: mínimo de grúas para optimización (opcional, default=1)
- max_gruas: máximo de grúas para optimización (opcional, default=10) - max_gruas: máximo de grúas para optimización (opcional, default=10)
@@ -178,6 +179,7 @@ def recommend_gruas():
hour = int(request.args["hour"]) hour = int(request.args["hour"])
k = request.args.get("k", default=2, type=int) k = request.args.get("k", default=2, type=int)
traffic = request.args.get("traffic", default=1, type=int)
optimize = request.args.get("optimize", default="false").lower() == "true" optimize = request.args.get("optimize", default="false").lower() == "true"
min_gruas = request.args.get("min_gruas", default=1, type=int) min_gruas = request.args.get("min_gruas", default=1, type=int)
max_gruas = request.args.get("max_gruas", default=10, type=int) max_gruas = request.args.get("max_gruas", default=10, type=int)
@@ -190,6 +192,8 @@ def recommend_gruas():
return jsonify({"error": "hour debe estar entre 0 y 23"}), 400 return jsonify({"error": "hour debe estar entre 0 y 23"}), 400
if k < 1: if k < 1:
return jsonify({"error": "k debe ser al menos 1"}), 400 return jsonify({"error": "k debe ser al menos 1"}), 400
if not (1 <= traffic <= 3):
return jsonify({"error": "traffic debe estar entre 1 (bajo) y 3 (alto)"}), 400
# Obtener modelos # Obtener modelos
demand_model = model_manager.get_model("demand") demand_model = model_manager.get_model("demand")
@@ -201,10 +205,10 @@ def recommend_gruas():
}), 500 }), 500
if DEBUG_MODE: if DEBUG_MODE:
print(f"DEBUG: Building prediction grid for week={week}, dow={dow}, hour={hour}") print(f"DEBUG: Building prediction grid for week={week}, dow={dow}, hour={hour}, traffic={traffic}")
# Construir grilla de predicciones # Construir grilla de predicciones
df = build_prediction_grid(week, dow, hour, demand_model, nulo_model) df = build_prediction_grid(week, dow, hour, demand_model, nulo_model, traffic_level=traffic)
if df.empty: if df.empty:
return jsonify({ return jsonify({
@@ -234,6 +238,10 @@ def recommend_gruas():
"semana": week, "semana": week,
"dia_semana": dow, "dia_semana": dow,
"hora": hour, "hora": hour,
"trafico": {
"nivel": traffic,
"descripcion": {1: "bajo", 2: "medio", 3: "alto"}.get(traffic, "desconocido")
},
"optimizacion": { "optimizacion": {
"k_optimo": result['optimal_k'], "k_optimo": result['optimal_k'],
"porcentaje_cobertura": result['coverage_percentage'], "porcentaje_cobertura": result['coverage_percentage'],
@@ -263,7 +271,11 @@ def recommend_gruas():
"hora": hour, "hora": hour,
"parametros": { "parametros": {
"k": k, "k": k,
"optimizacion": optimize "optimizacion": optimize,
"trafico": {
"nivel": traffic,
"descripcion": {1: "bajo", 2: "medio", 3: "alto"}.get(traffic, "desconocido")
}
}, },
"h3_recomendados": result['selected'], "h3_recomendados": result['selected'],
"estadisticas": result.get('statistics', []), "estadisticas": result.get('statistics', []),
@@ -505,7 +517,7 @@ def index():
"ruta": "/recommend", "ruta": "/recommend",
"metodo": "GET", "metodo": "GET",
"descripcion": "Recomendar ubicaciones para grúas", "descripcion": "Recomendar ubicaciones para grúas",
"parametros": "week, dow, hour (requeridos), k, optimize, min_gruas, max_gruas, target_coverage (opcionales)" "parametros": "week, dow, hour (requeridos), k, traffic (1-3), optimize, min_gruas, max_gruas, target_coverage (opcionales)"
}, },
{ {
"ruta": "/coverage/radius", "ruta": "/coverage/radius",

View File

@@ -3,22 +3,58 @@ import pandas as pd
from typing import List, Set, Dict, Tuple from typing import List, Set, Dict, Tuple
import numpy as np import numpy as np
def coverage_radius(expected_demand: float) -> int: def get_max_radius_by_traffic(traffic_level: int) -> int:
""" """
Determina el radio de cobertura según la demanda esperada, como medimos anteriormente Determina el radio máximo de cobertura según el nivel de tráfico.
Nivel demanda | Radio H3 Nivel tráfico | Radio H3 máximo
---------------|----------------
Bajo (1) | 18
Medio (2) | 12
Alto (3) | 9
Args:
traffic_level: Nivel de tráfico (1=bajo, 2=medio, 3=alto)
Returns:
Radio máximo en hexágonos H3
"""
traffic_limits = {
1: 18, # Tráfico bajo
2: 12, # Tráfico medio
3: 9 # Tráfico alto
}
return traffic_limits.get(traffic_level, 18) # Default a tráfico bajo
def coverage_radius(expected_demand: float, traffic_level: int = 1) -> int:
"""
Determina el radio de cobertura según la demanda esperada y el nivel de tráfico.
El radio final es el mínimo entre el radio calculado por demanda y el límite de tráfico.
Nivel demanda | Radio H3 base
---------------|---------- ---------------|----------
baja (<2) | 18 baja (<2) | 18
media (2-5) | 12 media (2-5) | 12
alta (≥5) | 6 alta (≥5) | 6
Args:
expected_demand: Demanda esperada
traffic_level: Nivel de tráfico (1=bajo, 2=medio, 3=alto)
Returns:
Radio de cobertura ajustado por tráfico
""" """
# Radio base según demanda
if expected_demand < 2: if expected_demand < 2:
return 18 base_radius = 18
elif expected_demand < 5: elif expected_demand < 5:
return 12 base_radius = 12
else: else:
return 6 base_radius = 6
# Aplicar restricción de tráfico
max_radius = get_max_radius_by_traffic(traffic_level)
return min(base_radius, max_radius)
def get_coverage_weight(expected_demand: float) -> float: def get_coverage_weight(expected_demand: float) -> float:
""" """
@@ -82,12 +118,14 @@ def calculate_coverage_score(cell: str, radius: int, df: pd.DataFrame) -> float:
return total_risk * (1 + 0.1 * demand_weight) return total_risk * (1 + 0.1 * demand_weight)
def build_prediction_grid(week: int, dow: int, hour: int, def build_prediction_grid(week: int, dow: int, hour: int,
demand_model, nulo_model, h3_resolution: int = 8) -> pd.DataFrame: demand_model, nulo_model, h3_resolution: int = 8,
traffic_level: int = 1) -> pd.DataFrame:
""" """
Construye una cuadricula de predicciones para todas las celdas H3. Construye una cuadricula de predicciones para todas las celdas H3.
Args: Args:
h3_resolution: Resolución H3 (default=8 para áreas urbanas) h3_resolution: Resolución H3 (default=8 para áreas urbanas)
traffic_level: Nivel de tráfico (1=bajo, 2=medio, 3=alto)
""" """
# Obtener todas las celdas H3 únicas de la base de datos # Obtener todas las celdas H3 únicas de la base de datos
from db import get_all_h3_cells from db import get_all_h3_cells
@@ -117,8 +155,8 @@ def build_prediction_grid(week: int, dow: int, hour: int,
# Calcular riesgo # Calcular riesgo
risk = calculate_risk(expected_demand, nulo_prob) risk = calculate_risk(expected_demand, nulo_prob)
# Determinar radio de cobertura y peso # Determinar radio de cobertura y peso (ajustado por tráfico)
radius = coverage_radius(expected_demand) radius = coverage_radius(expected_demand, traffic_level)
coverage_weight = get_coverage_weight(expected_demand) coverage_weight = get_coverage_weight(expected_demand)
predictions.append({ predictions.append({