Este proyecto permite generar datos sintéticos de huéspedes, realizar análisis de consumo y visualizar los resultados en un dashboard.
El proyecto dispone de cuatro modos principales de operación:
forge_daily→ Genera datos sintéticos diarios a partir de un dataset base y distribuciones definidas endist.json.forge_hourly→ Genera consumo horario a partir de un ZIP diario previamente forjado.modelling→ Permite analizar y modelar los datos generados.
Para visualizar los resultados y análisis, se puede ejecutar el dashboard con:
python3 launch_dashboard.pyEl módulo de forjado de datos permite generar datasets sintéticos de huéspedes a partir de un dataset base de hoteles y distribuciones definidas en dist.json.
Cada fila del dataset original se expande en varios huéspedes individuales respetando:
- Número de huéspedes (
Pax) y días de estancia (Dias de estancia). - Distribuciones condicionales y no condicionales definidas en
dist.json. - Reglas de consumo (
rules.json) que ajustan el consumo medio por huésped. - Variables compartidas por habitación, que deben coincidir entre los huéspedes que comparten unidad.
La función principal utilizada internamente es:
forge_guests(data: pd.DataFrame, dist: dict, rules: dict) -> pd.DataFrameEl dataset de entrada debe contener al menos estas columnas:
| Columna | Descripción |
|---|---|
| Pax | Número total de huéspedes de la fila |
| Consumo de electricidad por Pax | Consumo promedio por huésped |
| Mes | Mes de la estancia (1–12 o nombre) |
| Año | Año de la estancia |
| Estación | Estación del año (invierno, primavera, verano, otoño) |
Estas columnas son necesarias tanto para calcular el número de huéspedes y días de estancia como para servir de condicionante en distribuciones dependientes.
Al generar los datos sintéticos, ciertas columnas no deben aparecer en dist.json, ya que se crean automáticamente:
id_huesped→ identificador único de cada huéspedid_habitacion→ identificador de la habitación asignadaDias de estancia→ número de días que dura la estanciaDia inicio→ día del mes en que comienza la estanciaConsumo medio→ consumo calculado por huésped según reglasConsumo total→ consumo total (Consumo medio × Dias de estancia)
Las distribuciones definen cómo se generan las variables sintéticas. Existen tres tipos:
Se asignan según una probabilidad global.
Dependen de otra columna (base o sintética).
Ejemplo:
"nacionalidad": {
"condicion": "Estación",
"probabilidades": {
"invierno": {"España": 0.15, "Reino Unido": 0.30, "Alemania": 0.30, "Francia": 0.10, "Italia": 0.1, "Otros": 0.05},
"verano": {"España": 0.35, "Reino Unido": 0.15, "Alemania": 0.15, "Francia": 0.15, "Italia": 0.25, "Otros": 0.05}
}
}Algunas variables deben ser iguales para todos los huéspedes que comparten habitación.
Para ello se añade el campo:
"compartido_por_habitacion": trueEjemplo típico:
nacionalidadtipo_habitacion
Ejemplo completo:
"nacionalidad": {
"condicion": "Estación",
"compartido_por_habitacion": true,
"probabilidades": {
"invierno": {"España": 0.2, "Alemania": 0.3, "Reino Unido": 0.3, "Italia": 0.1, "Otros": 0.1}
}
}Si una habitación tiene ocupación 2, 3 o 4, estos huéspedes recibirán el mismo valor para cualquier variable donde este campo sea true.
⚠️ Nota: Las variables compartidas se generan antes que las individuales. Por ello, una variable compartida no puede estar condicionada a una variable individual, solo puede depender de columnas del dataset base o de otras variables compartidas que ya se hayan generado.
Define cuántos huéspedes se asignan a una misma habitación.
"ocupacion_habitacion": {
"probabilidades": {
"1": 0.75,
"2": 0.175,
"3": 0.05,
"4": 0.025
}
}Esta variable determina cuántas filas se generan por cada habitación y agrupa a los huéspedes que compartirán valores comunes.
Notas sobre las probabilidades:
- Cada conjunto de probabilidades debe sumar 1.
- Si las probabilidades no suman 1, se normalizan automáticamente durante el forjado de datos.
- Durante la ejecución, se muestra información en consola indicando qué variables han sido normalizadas y sus valores ajustados.
- Esta regla aplica tanto para variables simples, condicionadas, como para compartidas por habitación.
Las reglas modifican el consumo medio del huésped aplicando ajustes multiplicativos:
Además:
- Se añade ruido aleatorio por huésped, el valor por defecto es ±5%, pero es modificable.
- Los ajustes se aplican en cascada según las variables generadas.
- Después de aplicar todas las reglas y generar los huéspedes, se normaliza el consumo total para que la suma de todos los huéspedes coincida con el consumo real del dataset original. Esto introduce un factor de normalización aplicado a cada
Consumo medioyConsumo total.
Interpretación del factor de normalización:
- Factor ≈ 1 → las reglas están balanceadas y no alteran significativamente el consumo total.
- Factor > 1 → las reglas redujeron demasiado el consumo, se incrementa para ajustarlo.
- Factor < 1 → las reglas aumentaron demasiado el consumo, se reduce para ajustarlo.
Ejemplo de reglas:
{
"nacionalidad": {
"Alemania": 0.15,
"Reino Unido": 0.12,
"Francia": 0.1,
"España": -0.1,
"Italia": -0.1,
"Otros": 0
},
"sexo": {
"Mujer": 0.05,
"Hombre": -0.03
},
"uso_instalaciones": {
"Si": 0.1,
"No": -0.1
},
"tipo_habitacion": {
"Suite": 0.2,
"Estándar": -0.2
}
}python3 main.py --mode forge_daily --data mi_dataset.csv --dist dist.json --rules rules.jsonParámetros:
--mode forge_daily→ activa el módulo de forjado--data→ ruta del CSV base--dist→ JSON con distribuciones--rules→ JSON con reglas de consumo
El forjado genera automáticamente en data/forged/daily:
-
forged_XXXX.csv– Dataset sintético resultante. -
dist_XXXX.json– Distribuciones utilizadas. -
rules_XXXX.json– Reglas aplicadas. -
daily_XXXX.zip– Contenedor con los tres archivos anteriores. -
info_XXXX.json– Información adicional de la generación, incluyendo:num_guests– Número de huéspedes generados.noise_daily– Ruido aplicado.normalization_info– Factor aplicado para ajustar el consumo total al dataset original.
XXXX corresponde al siguiente índice disponible en la carpeta data/forged/daily, determinado automáticamente para evitar sobrescribir archivos existentes.
Esta sección permite generar datos de consumo por hora a partir de un dataset diario forjado (forge_daily) y unos perfiles horarios definidos en un archivo JSON.
Cada fila del dataset diario se expande en una fila por día de estancia del huésped, y cada fila diaria se divide en 24 horas siguiendo un perfil horario:
-
Se utiliza el Consumo total de todos los días de estancia de cada huésped para asignar el consumo diario.
- Recordatorio:
Consumo total = Consumo medio × Dias de estancia
- Recordatorio:
-
Cada día tiene un consumo ligeramente aleatorio (ruido controlado por parámetro) para que no todos los días sean idénticos, pero la suma total por huésped se mantiene igual que en los datos diarios.
-
El consumo diario se distribuye entre las 24 horas según el perfil horario seleccionado para cada huésped, heredando de forma implícita la variabilidad del consumo diario.
Los perfiles horarios se definen en un JSON con la siguiente estructura:
{
"morning_active": {
"descripcion": "High consumption in morning hours, low at night",
"probabilidades": [0.08, 0.08, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04]
},
...
}Notas:
-
Cada lista de
probabilidadesdebe tener exactamente 24 elementos, uno por hora. -
La suma de los valores en cada perfil debe ser 1.
- Si no suman 1, se normalizan automáticamente durante la ejecución.
- La normalización genera mensajes en consola indicando el nombre del perfil y la acción realizada.
-
El valor de ruido diario es configurable, por defecto ±5%, y afecta la variabilidad entre días de un mismo huésped.
python3 main.py --mode forge_hourly --daily_index 1 --profiles hourly_profiles.jsonParámetros:
--mode forge_hourly→ activa el módulo de forjado horario--daily_index→ índice del ZIP diario forjado a usar como base (por ejemplo,1→daily_0001.zip)--profiles→ archivo JSON con los perfiles horarios
El forjado horario genera automáticamente en data/forged/hourly:
hourly_XXXX.csv– Dataset sintético horariohourly_XXXX.zip– Contenedor ZIP con el dataset horario y el archivo de perfiles usadoinfo_XXXX.json– Información adicional sobre el forjado (archivo diario de origen, número de huéspedes, fecha de generación, ruido aplicado, etc.)
XXXX corresponde al siguiente índice disponible en la carpeta data/forged/hourly, determinado automáticamente para evitar sobrescribir archivos existentes.
La sección de modelling permite entrenar y evaluar modelos de regresión sobre los datos sintéticos diarios generados con la fase de forge_daily. Se centra en predecir el consumo medio de electricidad por huésped.
-
Cargar los datos sintéticos diarios
forged_data_df, forged_dist, rules = load_daily_zip(FORGED_DAILY_PATH, daily_index) forged_df = forged_data_df[forged_data_df['Hotel'] == ONE_HOTEL]
forged_dfcontiene los huéspedes de un hotel específico.- Se puede filtrar por hotel si el ZIP contiene varios hoteles.
-
Preparar las variables
- Variables numéricas:
Dias de estancia. - Variables categóricas:
sexo,nacionalidad,edad,tipo_habitacion,uso_instalaciones,viaje,comparte_habitacion. - Se aplica one-hot encoding a todas las variables categóricas.
- Se eliminan valores
NaNo infinitos, y se escalan las variables numéricas.
- Variables numéricas:
-
Eliminación de variables multicolineales
- Correlación alta: Se eliminan variables cuya correlación absoluta supere
corr_threshold(por defecto 0.8). - VIF alto: Se eliminan variables con VIF (
Variance Inflation Factor) superior avif_threshold(por defecto 10).
- Correlación alta: Se eliminan variables cuya correlación absoluta supere
-
Entrenamiento de modelos Se entrenan varios modelos de regresión para estimar el
Consumo medio:- Random Forest
- AdaBoost
- Ridge
- Lasso
- Bayesian Ridge
- XGBoost
-
Evaluación
-
Se calcula RMSE y MAE sobre un conjunto de prueba (80/20 split).
-
Se extraen importancias de características de cada modelo:
feature_importances_para RandomForest, AdaBoost, XGBoost.- Valor absoluto de
coef_para Ridge, Lasso, BayesianRidge.
-
-
Combinación con información teórica y correlaciones
- Las importancias se pueden comparar con las derivadas de las reglas (
rules.json) y distribuciones (dist.json) para obtener una visión teórica del efecto de cada variable. - Se normalizan y combinan las importancias en un DataFrame final (
importance_combined_normalized) que se guarda junto con los modelos entrenados.
- Las importancias se pueden comparar con las derivadas de las reglas (
| Argumento | Tipo | Descripción |
|---|---|---|
forged_df |
DataFrame |
Datos sintéticos diarios de huéspedes para un hotel. |
corr_threshold |
float |
Umbral de correlación para eliminar variables altamente correlacionadas. |
vif_threshold |
float |
Umbral de VIF para eliminar variables multicolineales. |
| Retorno | Tipo | Descripción |
|---|---|---|
importance_df |
DataFrame |
Importancia de cada variable para cada modelo. |
eliminated_vars |
dict |
Variables eliminadas por alta correlación o VIF. |
model_storage |
dict |
Modelos entrenados, escaladores y métricas de error (RMSE, MAE). |
from modelling import train_and_evaluate_models
importance_df, eliminated_vars, model_storage = train_and_evaluate_models(forged_df)
print(importance_df.head())
print(eliminated_vars)
print(model_storage['error_metrics'])- Las variables categóricas deben estar correctamente codificadas; el one-hot encoding se realiza dentro de la función.
- El DataFrame
forged_dfdebe contener al menos las columnas utilizadas en el forjado:Dias de estancia,sexo,nacionalidad,edad,tipo_habitacion,uso_instalaciones,viaje,comparte_habitacion,Consumo medio. - Se recomienda revisar los
eliminated_varspara comprender qué variables se descartan y por qué. - Las importancias combinadas pueden ser comparadas con las reglas definidas en
rules.jsonpara validar la coherencia del modelo con el dataset sintético.