import numpy as np
import pandas as pd
import [Link] as plt
from [Link] import curve_fit, newton, minimize
from [Link] import odeint, solve_ivp
from [Link] import find_peaks
from datetime import datetime, timedelta
from [Link] import RandomForestRegressor, GradientBoostingRegressor
from [Link] import StandardScaler
from [Link] import mean_squared_error, r2_score, mean_absolute_error
import warnings
[Link]('ignore')
# ============================================================================
# 1. GENERACIÓN DE DATOS AVANZADA CON MÚLTIPLES FACTORES
# ============================================================================
class GeneradorDatosEnergia:
"""Generador avanzado de datos de consumo energético"""
def __init__(self, dias=180):
[Link] = dias
[Link] = ['Miraflores', 'Sopocachi', 'Calacoto', 'San Miguel', 'Centro',
'Achumani', 'Obrajes', 'Irpavi']
self.fecha_inicio = datetime(2024, 1, 1)
def generar_dataset_completo(self):
"""Genera dataset con múltiples factores realistas"""
dias_range = pd.date_range(self.fecha_inicio, periods=[Link], freq='D')
data = []
for zona in [Link]:
config = self._configurar_zona(zona)
for i, fecha in enumerate(dias_range):
consumo = self._calcular_consumo(i, fecha, config)
[Link]({
'fecha': fecha,
'zona': zona,
'consumo_kwh': round(consumo, 1),
'temperatura': self._temperatura(i),
'dia_semana': [Link],
'es_finde': 1 if [Link] >= 5 else 0,
'mes': [Link],
'poblacion': config['poblacion'],
'tipo_zona': config['tipo']
})
df = [Link](data)
df.to_csv("consumo_energia_lapaz_completo.csv", index=False)
print("✓ Dataset completo generado: consumo_energia_lapaz_completo.csv")
print(f" - {len([Link])} zonas, {[Link]} días, {len(df)} registros
totales")
return df
def _configurar_zona(self, zona):
"""Configuración específica por zona"""
configs = {
'Miraflores': {'base': 280, 'tendencia': 1.2, 'poblacion': 85000, 'tipo':
'residencial'},
'Sopocachi': {'base': 320, 'tendencia': 0.8, 'poblacion': 95000, 'tipo':
'comercial'},
'Calacoto': {'base': 350, 'tendencia': 1.5, 'poblacion': 65000, 'tipo':
'residencial_alto'},
'San Miguel': {'base': 240, 'tendencia': 0.5, 'poblacion': 70000, 'tipo':
'residencial'},
'Centro': {'base': 400, 'tendencia': -0.3, 'poblacion': 120000, 'tipo':
'comercial'},
'Achumani': {'base': 300, 'tendencia': 1.0, 'poblacion': 55000, 'tipo':
'residencial_alto'},
'Obrajes': {'base': 290, 'tendencia': 0.9, 'poblacion': 60000, 'tipo':
'residencial'},
'Irpavi': {'base': 260, 'tendencia': 1.3, 'poblacion': 50000, 'tipo':
'residencial'}
}
return [Link](zona, {'base': 250, 'tendencia': 0.5, 'poblacion': 60000,
'tipo': 'residencial'})
def _calcular_consumo(self, i, fecha, config):
"""Cálculo de consumo con múltiples componentes"""
consumo = config['base'] + config['tendencia'] * i
estacion = 25 * [Link](2 * [Link] * i / 365 - [Link]/2)
ciclo_semanal = 20 * [Link](2 * [Link] * i / 7)
pico_laboral = 15 if [Link] < 5 else -10
evento = [Link](40, 100) if [Link]() < 0.05 else 0
ruido = [Link](0, 18)
consumo_total = consumo + estacion + ciclo_semanal + pico_laboral + evento +
ruido
return [Link](consumo_total, 100, 500)
def _temperatura(self, i):
"""Temperatura simulada para La Paz"""
base_temp = 15
variacion = 5 * [Link](2 * [Link] * i / 365)
ruido = [Link](0, 2)
return round(base_temp + variacion + ruido, 1)
# ============================================================================
# 2. MODELOS DIFERENCIALES AVANZADOS
# ============================================================================
class ModeloDiferencialAvanzado:
"""Múltiples modelos de ecuaciones diferenciales"""
def __init__(self, df_zona):
[Link] = df_zona.copy()
[Link]['dias'] = ([Link]['fecha'] - [Link]['fecha'].min()).[Link]
self.ajustar_modelos()
def ajustar_modelos(self):
"""Ajusta múltiples modelos diferenciales"""
dias = [Link]['dias'].values
consumo = [Link]['consumo_kwh'].values
dE = [Link](consumo)
dt = [Link](dias)
E_mid = (consumo[:-1] + consumo[1:]) / 2
A = np.column_stack([np.ones_like(E_mid), E_mid])
params_lineal = [Link](A, dE/dt, rcond=None)[0]
self.a_lin, self.b_lin = params_lineal
def modelo_logistico(params, E):
r, K = params
return r * E * (1 - E/K)
def error_logistico(params):
predicciones = [modelo_logistico(params, E_mid[i]) for i in
range(len(E_mid))]
return [Link](([Link](predicciones) - dE/dt)**2)
resultado = minimize(error_logistico, x0=[0.01, 500], bounds=[(0.001, 1), (300,
1000)])
self.r_log, self.K_log = resultado.x
t_mid = (dias[:-1] + dias[1:]) / 2
try:
params_osc, _ = curve_fit(
lambda t, a, b, c, omega: a + b*E_mid + c*[Link](omega*t),
t_mid, dE/dt, p0=[self.a_lin, self.b_lin, 5, 2*[Link]/7],
maxfev=5000
)
self.a_osc, self.b_osc, self.c_osc, self.omega_osc = params_osc
except:
self.a_osc = self.a_lin
self.b_osc = self.b_lin
self.c_osc = 5
self.omega_osc = 2*[Link]/7
print(f"\n MODELOS DIFERENCIALES AJUSTADOS:")
print(f" 1. Lineal: dE/dt = {self.a_lin:.4f} + {self.b_lin:.6f}*E")
print(f" 2. Logístico: dE/dt = {self.r_log:.4f}*E*(1 - E/{self.K_log:.1f})")
print(f" 3. Oscilatorio: dE/dt = {self.a_osc:.4f} + {self.b_osc:.6f}*E +
{self.c_osc:.2f}*sin({self.omega_osc:.4f}*t)")
def edo_lineal(self, E, t):
return self.a_lin + self.b_lin * E
def edo_logistico(self, E, t):
return self.r_log * E * (1 - E / self.K_log)
def edo_oscilatorio(self, E, t):
return self.a_osc + self.b_osc * E + self.c_osc * [Link](self.omega_osc * t)
def resolver_todos(self, E0, t_final=90):
"""Resuelve con múltiples métodos y modelos"""
t_span = [0, t_final]
t_eval = [Link](0, t_final, t_final + 1)
sol_lineal = solve_ivp(self.edo_lineal, t_span, [E0], t_eval=t_eval,
method='RK45')
sol_logistico = solve_ivp(self.edo_logistico, t_span, [E0], t_eval=t_eval,
method='RK45')
sol_oscilatorio = solve_ivp(self.edo_oscilatorio, t_span, [E0], t_eval=t_eval,
method='RK45')
return {
'tiempo': sol_lineal.t,
'lineal': sol_lineal.y[0],
'logistico': sol_logistico.y[0],
'oscilatorio': sol_oscilatorio.y[0]
}
# ============================================================================
# 3. MACHINE LEARNING PARA PREDICCIÓN
# ============================================================================
class ModeloMLEnergia:
"""Modelos de Machine Learning para predicción"""
def __init__(self, df):
[Link] = [Link]()
self.preparar_datos()
def preparar_datos(self):
"""Prepara features para ML"""
[Link]['dia_año'] = [Link]['fecha'].[Link]
[Link]['semana'] = [Link]['fecha'].[Link]().week
[Link]['dias_desde_inicio'] = ([Link]['fecha'] -
[Link]['fecha'].min()).[Link]
for zona in [Link]['zona'].unique():
mask = [Link]['zona'] == zona
[Link][mask, 'consumo_lag1'] = [Link][mask, 'consumo_kwh'].shift(1)
[Link][mask, 'consumo_lag7'] = [Link][mask, 'consumo_kwh'].shift(7)
[Link][mask, 'consumo_rolling7'] = [Link][mask,
'consumo_kwh'].rolling(7).mean()
[Link] = [Link]()
[Link] = ['dias_desde_inicio', 'temperatura', 'dia_semana', 'es_finde',
'mes', 'dia_año', 'semana', 'poblacion',
'consumo_lag1', 'consumo_lag7', 'consumo_rolling7']
[Link]['tipo_zona_cod'] = [Link]([Link]['tipo_zona']).codes
[Link]('tipo_zona_cod')
def entrenar_modelos(self, zona, test_size=0.2):
"""Entrena múltiples modelos ML"""
df_zona = [Link][[Link]['zona'] == zona].copy()
n_test = int(len(df_zona) * test_size)
train = df_zona.iloc[:-n_test]
test = df_zona.iloc[-n_test:]
X_train = train[[Link]]
y_train = train['consumo_kwh']
X_test = test[[Link]]
y_test = test['consumo_kwh']
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = [Link](X_test)
rf_model = RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42)
rf_model.fit(X_train_scaled, y_train)
rf_pred = rf_model.predict(X_test_scaled)
gb_model = GradientBoostingRegressor(n_estimators=100, max_depth=5,
random_state=42)
gb_model.fit(X_train_scaled, y_train)
gb_pred = gb_model.predict(X_test_scaled)
print(f"\n MODELOS MACHINE LEARNING - {zona}:")
print(f"\n Random Forest:")
print(f" MAE: {mean_absolute_error(y_test, rf_pred):.2f} kWh")
print(f" RMSE: {[Link](mean_squared_error(y_test, rf_pred)):.2f} kWh")
print(f" R²: {r2_score(y_test, rf_pred):.4f}")
print(f"\n Gradient Boosting:")
print(f" MAE: {mean_absolute_error(y_test, gb_pred):.2f} kWh")
print(f" RMSE: {[Link](mean_squared_error(y_test, gb_pred)):.2f} kWh")
print(f" R²: {r2_score(y_test, gb_pred):.4f}")
importancias = [Link]({
'feature': [Link],
'importance': rf_model.feature_importances_
}).sort_values('importance', ascending=False)
print(f"\n Top 5 Features más importantes:")
for idx, row in [Link](5).iterrows():
print(f" {row['feature']}: {row['importance']:.4f}")
return {
'rf_model': rf_model,
'gb_model': gb_model,
'scaler': scaler,
'test_data': test,
'rf_pred': rf_pred,
'gb_pred': gb_pred,
'importancias': importancias
}
# ============================================================================
# 4. OPTIMIZACIÓN Y CONTROL
# ============================================================================
class OptimizadorConsumo:
"""Optimización del consumo energético"""
def __init__(self, df_zona):
[Link] = df_zona
self.consumo_actual = df_zona['consumo_kwh'].mean()
def optimizar_consumo(self, objetivo_reduccion=0.15, umbral_critico=350):
"""Encuentra estrategia óptima de reducción"""
print(f"\n OPTIMIZACIÓN DE CONSUMO:")
print(f" Consumo actual promedio: {self.consumo_actual:.1f} kWh")
print(f" Objetivo de reducción: {objetivo_reduccion*100:.0f}%")
print(f" Meta: {self.consumo_actual * (1-objetivo_reduccion):.1f} kWh")
estrategias = {
'Eficiencia iluminación': {'reduccion': 0.08, 'costo': 5000},
'Aislamiento térmico': {'reduccion': 0.12, 'costo': 15000},
'Sistemas HVAC eficientes': {'reduccion': 0.15, 'costo': 25000},
'Paneles solares': {'reduccion': 0.25, 'costo': 50000},
'Sensores inteligentes': {'reduccion': 0.10, 'costo': 8000},
'Educación usuarios': {'reduccion': 0.05, 'costo': 2000}
}
def objetivo(x):
reduccion_total = sum(x[i] * list([Link]())[i]['reduccion']
for i in range(len(x)))
costo_total = sum(x[i] * list([Link]())[i]['costo']
for i in range(len(x)))
penalizacion = 1000 * max(0, objetivo_reduccion - reduccion_total)**2
return costo_total - 100000 * reduccion_total + penalizacion
from [Link] import differential_evolution
bounds = [(0, 1)] * len(estrategias)
resultado = differential_evolution(objetivo, bounds, seed=42, maxiter=100)
solucion = (resultado.x > 0.5).astype(int)
print(f"\n ESTRATEGIAS RECOMENDADAS:")
reduccion_final = 0
costo_final = 0
for i, (nombre, datos) in enumerate([Link]()):
if solucion[i]:
print(f" ✓ {nombre}")
print(f" - Reducción: {datos['reduccion']*100:.0f}%")
print(f" - Costo: Bs. {datos['costo']:,}")
reduccion_final += datos['reduccion']
costo_final += datos['costo']
consumo_proyectado = self.consumo_actual * (1 - reduccion_final)
ahorro_anual = (self.consumo_actual - consumo_proyectado) * 365 * 1.2
print(f"\n RESULTADOS:")
print(f" Reducción total: {reduccion_final*100:.1f}%")
print(f" Consumo proyectado: {consumo_proyectado:.1f} kWh/día")
print(f" Inversión total: Bs. {costo_final:,}")
print(f" Ahorro anual estimado: Bs. {ahorro_anual:,.0f}")
print(f" ROI (años): {costo_final/ahorro_anual:.2f}")
if consumo_proyectado < umbral_critico:
print(f" ✅ Se mantiene por debajo del umbral crítico ({umbral_critico}
kWh)")
else:
print(f" Aún supera el umbral crítico ({umbral_critico} kWh)")
return solucion, reduccion_final, costo_final
# ============================================================================
# 5. ANÁLISIS DE PICOS Y ANOMALÍAS
# ============================================================================
def detectar_anomalias(df_zona):
"""Detecta picos y anomalías en el consumo"""
consumo = df_zona['consumo_kwh'].values
media = [Link](consumo)
std = [Link](consumo)
z_scores = [Link]((consumo - media) / std)
anomalias_zscore = z_scores > 2.5
Q1 = [Link](consumo, 25)
Q3 = [Link](consumo, 75)
IQR = Q3 - Q1
limite_inf = Q1 - 1.5 * IQR
limite_sup = Q3 + 1.5 * IQR
anomalias_iqr = (consumo < limite_inf) | (consumo > limite_sup)
picos, propiedades = find_peaks(consumo, height=media + 1.5*std, distance=5)
print(f"\n DETECCIÓN DE ANOMALÍAS:")
print(f" Anomalías Z-score (>2.5σ): {[Link](anomalias_zscore)}")
print(f" Anomalías IQR: {[Link](anomalias_iqr)}")
print(f" Picos detectados: {len(picos)}")
if len(picos) > 0:
print(f"\n PICOS CRÍTICOS:")
for i, pico_idx in enumerate(picos[:5]):
fecha = df_zona.iloc[pico_idx]['fecha']
valor = consumo[pico_idx]
print(f" {i+1}. {[Link]('%Y-%m-%d')}: {valor:.1f} kWh")
return {
'anomalias_zscore': anomalias_zscore,
'anomalias_iqr': anomalias_iqr,
'picos': picos,
'limite_superior': limite_sup
}
# ============================================================================
# 6. FUNCIÓN AUXILIAR PARA AGREGAR EXPLICACIONES
# ============================================================================
def agregar_explicacion_cuadro(fig, objetivo, aplicar, implementar, mejorar):
"""Agrega un cuadro de texto explicativo en la parte inferior de la figura"""
texto = (
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━\n"
f" ANÁLISIS Y RECOMENDACIONES\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━\n\n"
f" OBJETIVO:\n {objetivo}\n\n"
f" APLICAR:\n {aplicar}\n\n"
f" IMPLEMENTAR:\n {implementar}\n\n"
f" MEJORAR:\n {mejorar}\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━"
)
props = dict(boxstyle='round', facecolor='#FFF8DC', alpha=0.9, edgecolor='#8B4513',
linewidth=2)
[Link](0.5, 0.02, texto, transform=[Link], fontsize=10,
verticalalignment='bottom', horizontalalignment='center', bbox=props,
family='monospace', wrap=True)
# ============================================================================
# 7. VISUALIZACIONES INDIVIDUALES (MODIFICADAS PARA MOSTRAR SECUENCIALMENTE)
# ============================================================================
def grafica_1_predicciones_edo(df_zona, modelos_edo, zona):
"""Gráfica 1: Predicciones con Ecuaciones Diferenciales"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
[Link](df_zona['fecha'], df_zona['consumo_kwh'], 'o-',
label='Datos reales', alpha=0.6, markersize=4, color='blue', linewidth=2)
fechas_pred = pd.date_range(df_zona['fecha'].max(),
periods=len(modelos_edo['tiempo']), freq='D')
[Link](fechas_pred, modelos_edo['lineal'], '-', label='Modelo Lineal', linewidth=3)
[Link](fechas_pred, modelos_edo['logistico'], '-', label='Modelo Logístico',
linewidth=3)
[Link](fechas_pred, modelos_edo['oscilatorio'], '--', label='Modelo Oscilatorio',
linewidth=3)
[Link](y=350, color='red', linestyle='--', label='Umbral crítico (350 kWh)',
linewidth=2.5)
ax.set_title(f'GRÁFICA 1: PREDICCIONES CON ECUACIONES DIFERENCIALES - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Fecha', fontweight='bold', fontsize=13)
ax.set_ylabel('Consumo (kWh)', fontweight='bold', fontsize=13)
[Link](loc='best', fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, linestyle='--')
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_01_predicciones_edo_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_2_precision_ml(resultados_ml, zona):
"""Gráfica 2: Precisión de Modelos ML"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
test_data = resultados_ml['test_data']
[Link](test_data['fecha'], test_data['consumo_kwh'], 'o-',
label='Consumo Real', linewidth=3, markersize=7, color='darkgreen')
[Link](test_data['fecha'], resultados_ml['rf_pred'], 's-',
label='Random Forest', linewidth=2.5, alpha=0.8, markersize=6)
[Link](test_data['fecha'], resultados_ml['gb_pred'], '^-',
label='Gradient Boosting', linewidth=2.5, alpha=0.8, markersize=6)
ax.set_title(f'GRÁFICA 2: PRECISIÓN DE MODELOS DE MACHINE LEARNING - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Fecha', fontweight='bold', fontsize=13)
ax.set_ylabel('Consumo (kWh)', fontweight='bold', fontsize=13)
[Link](fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, linestyle='--')
ax.tick_params(axis='x', rotation=30)
mae_rf = mean_absolute_error(test_data['consumo_kwh'], resultados_ml['rf_pred'])
r2_rf = r2_score(test_data['consumo_kwh'], resultados_ml['rf_pred'])
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_02_precision_ml_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_3_importancia_features(resultados_ml, zona):
"""Gráfica 3: Importancia de Variables"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
top_features = resultados_ml['importancias'].head(10)
colores = [Link]([Link](0.3, 0.9, len(top_features)))
bars = [Link](top_features['feature'], top_features['importance'], color=colores,
edgecolor='black', linewidth=1.5)
ax.set_title(f'GRÁFICA 3: FACTORES MÁS INFLUYENTES EN EL CONSUMO - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Importancia Relativa', fontweight='bold', fontsize=13)
ax.set_ylabel('Variable', fontweight='bold', fontsize=13)
[Link](True, alpha=0.3, axis='x', linestyle='--')
for i, bar in enumerate(bars):
width = bar.get_width()
[Link](width + 0.005, bar.get_y() + bar.get_height()/2,
f'{width:.4f}', ha='left', va='center', fontsize=10, fontweight='bold')
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_03_importancia_features_{zona}.png', dpi=200,
bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_4_distribucion_estadistica(df_zona, anomalias, zona):
"""Gráfica 4: Distribución Estadística"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
umbral_critico = 350
n, bins, patches = [Link](df_zona['consumo_kwh'], bins=35, color='lightblue',
edgecolor='black', alpha=0.7, linewidth=1.5)
for i, patch in enumerate(patches):
if bins[i] >= umbral_critico:
patch.set_facecolor('red')
patch.set_alpha(0.8)
elif bins[i] >= umbral_critico * 0.85:
patch.set_facecolor('orange')
patch.set_alpha(0.7)
[Link](x=umbral_critico, color='red', linestyle='--', linewidth=3,
label=f'Umbral crítico ({umbral_critico} kWh)')
[Link](x=df_zona['consumo_kwh'].mean(), color='green',
linestyle='--', linewidth=3, label=f'Promedio
({df_zona["consumo_kwh"].mean():.1f} kWh)')
[Link](x=anomalias['limite_superior'], color='orange',
linestyle='--', linewidth=3, label=f'Límite anomalía
({anomalias["limite_superior"]:.1f} kWh)')
ax.set_title(f'GRÁFICA 4: DISTRIBUCIÓN ESTADÍSTICA DEL CONSUMO - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Consumo (kWh)', fontweight='bold', fontsize=13)
ax.set_ylabel('Frecuencia (días)', fontweight='bold', fontsize=13)
[Link](fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, axis='y', linestyle='--')
pct_sobre_umbral = (df_zona['consumo_kwh'] > umbral_critico).mean() * 100
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_04_distribucion_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_5_temperatura_consumo(df_zona, zona):
"""Gráfica 5: Relación Temperatura-Consumo"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
scatter = [Link](df_zona['temperatura'], df_zona['consumo_kwh'],
alpha=0.6, c=df_zona['dia_semana'], cmap='viridis',
s=80, edgecolors='black', linewidth=0.8)
z = [Link](df_zona['temperatura'], df_zona['consumo_kwh'], 1)
p = np.poly1d(z)
[Link](df_zona['temperatura'], p(df_zona['temperatura']),
"r--", linewidth=4, label=f'Tendencia: y = {z[0]:.2f}x + {z[1]:.1f}')
ax.set_title(f'GRÁFICA 5: RELACIÓN TEMPERATURA-CONSUMO - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Temperatura (°C)', fontweight='bold', fontsize=13)
ax.set_ylabel('Consumo (kWh)', fontweight='bold', fontsize=13)
[Link](fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, linestyle='--')
cbar = [Link](scatter, ax=ax)
cbar.set_label('Día de la semana', fontweight='bold', fontsize=12)
corr_temp = df_zona[['temperatura', 'consumo_kwh']].corr().iloc[0, 1]
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_05_temperatura_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_6_patron_semanal(df_zona, zona):
"""Gráfica 6: Patrón Semanal"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
consumo_por_dia = df_zona.groupby('dia_semana')['consumo_kwh'].agg(['mean', 'std',
'count'])
dias_nombres = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado',
'Domingo']
colores_dias = ['#3498db']*5 + ['#e74c3c', '#e74c3c']
bars = [Link](range(7), consumo_por_dia['mean'], yerr=consumo_por_dia['std'],
color=colores_dias, alpha=0.8, capsize=8, edgecolor='black',
linewidth=2,
error_kw={'linewidth': 2, 'ecolor': 'black'})
ax.set_xticks(range(7))
ax.set_xticklabels(dias_nombres, fontweight='bold', fontsize=12)
ax.set_title(f'GRÁFICA 6: COMPORTAMIENTO SEMANAL DEL CONSUMO - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_ylabel('Consumo promedio (kWh)', fontweight='bold', fontsize=13)
[Link](True, alpha=0.3, axis='y', linestyle='--')
for i, bar in enumerate(bars):
height = bar.get_height()
[Link](bar.get_x() + bar.get_width()/2., height +
consumo_por_dia.iloc[i]['std'],
f'{height:.1f}', ha='center', va='bottom', fontweight='bold',
fontsize=11)
dif_finde = consumo_por_dia.loc[5:6, 'mean'].mean() - consumo_por_dia.loc[0:4,
'mean'].mean()
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_06_patron_semanal_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_7_deteccion_anomalias(df_zona, anomalias, zona):
"""Gráfica 7: Detección de Anomalías"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
[Link](range(len(df_zona)), df_zona['consumo_kwh'].values,
'b-', alpha=0.5, linewidth=2, label='Consumo histórico')
[Link](anomalias['picos'], df_zona['consumo_kwh'].values[anomalias['picos']],
'ro', markersize=15, label=f"{len(anomalias['picos'])} picos detectados",
markeredgecolor='darkred', markeredgewidth=2.5)
[Link](y=anomalias['limite_superior'], color='orange',
linestyle='--', linewidth=3, label=f'Límite anomalía IQR
({anomalias["limite_superior"]:.1f} kWh)')
[Link](y=df_zona['consumo_kwh'].mean(), color='green',
linestyle='--', linewidth=2.5, alpha=0.7, label=f'Promedio
({df_zona["consumo_kwh"].mean():.1f} kWh)')
ax.fill_between(range(len(df_zona)),
df_zona['consumo_kwh'].mean() - df_zona['consumo_kwh'].std(),
df_zona['consumo_kwh'].mean() + df_zona['consumo_kwh'].std(),
alpha=0.2, color='green', label='±1σ (rango normal)')
ax.set_title(f'GRÁFICA 7: DETECCIÓN AUTOMÁTICA DE ANOMALÍAS Y PICOS - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Día (índice temporal)', fontweight='bold', fontsize=13)
ax.set_ylabel('Consumo (kWh)', fontweight='bold', fontsize=13)
[Link](loc='best', fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, linestyle='--')
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_07_anomalias_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_8_tendencia_temporal(df_zona, zona):
"""Gráfica 8: Tendencia Temporal"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
df_zona_temp = df_zona.copy()
df_zona_temp['ma7'] = df_zona_temp['consumo_kwh'].rolling(window=7,
center=True).mean()
df_zona_temp['ma30'] = df_zona_temp['consumo_kwh'].rolling(window=30,
center=True).mean()
[Link](df_zona_temp['fecha'], df_zona_temp['consumo_kwh'],
'o', alpha=0.3, markersize=4, color='gray', label='Datos diarios (ruido)')
[Link](df_zona_temp['fecha'], df_zona_temp['ma7'],
'-', linewidth=3, color='blue', label='Promedio móvil 7 días')
[Link](df_zona_temp['fecha'], df_zona_temp['ma30'],
'-', linewidth=4, color='red', label='Promedio móvil 30 días (tendencia)')
[Link](y=350, color='darkred', linestyle='--', linewidth=3, label='Umbral
crítico')
ax.set_title(f'GRÁFICA 8: TENDENCIA TEMPORAL Y ESTACIONALIDAD - {zona}',
fontweight='bold', fontsize=16, pad=20)
ax.set_xlabel('Fecha', fontweight='bold', fontsize=13)
ax.set_ylabel('Consumo (kWh)', fontweight='bold', fontsize=13)
[Link](loc='best', fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, linestyle='--')
ax.tick_params(axis='x', rotation=30)
tendencia_mensual = (df_zona_temp['ma30'].iloc[-1] -
df_zona_temp['ma30'].dropna().iloc[0]) / df_zona_temp['ma30'].dropna().iloc[0] * 100
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link](f'grafica_08_tendencia_{zona}.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
# ============================================================================
# 8. VISUALIZACIONES COMPARATIVAS INDIVIDUALES (MODIFICADAS)
# ============================================================================
def grafica_comp_1_ranking_consumo(df, metricas):
"""Gráfica Comparativa 1: Ranking de Zonas"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
metricas_sorted = metricas.sort_values('Promedio', ascending=False)
umbral = 350
colors = ['#c0392b' if x > umbral*0.9 else '#e67e22' if x > umbral*0.7 else
'#27ae60'
for x in metricas_sorted['Promedio']]
bars = [Link](metricas_sorted.index, metricas_sorted['Promedio'],
color=colors, alpha=0.8, edgecolor='black', linewidth=2)
[Link](x=umbral, color='red', linestyle='--', linewidth=4, label='Umbral
crítico')
ax.set_xlabel('Consumo Promedio (kWh)', fontweight='bold', fontsize=13)
ax.set_title('GRÁFICA COMPARATIVA 1: RANKING DE ZONAS POR CONSUMO',
fontweight='bold', fontsize=16, pad=20)
[Link](fontsize=12, framealpha=0.9)
[Link](True, alpha=0.3, axis='x', linestyle='--')
for i, (idx, bar) in enumerate(zip(metricas_sorted.index, bars)):
width = bar.get_width()
[Link](width + 5, bar.get_y() + bar.get_height()/2,
f'{width:.1f}', ha='left', va='center', fontweight='bold', fontsize=11)
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link]('grafica_comp_01_ranking.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_comp_2_evolucion_zonas(df):
"""Gráfica Comparativa 2: Evolución Temporal"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
umbral = 350
for zona in df['zona'].unique():
df_zona = df[df['zona'] == zona]
df_zona_weekly =
df_zona.groupby(df_zona['fecha'].dt.to_period('W'))['consumo_kwh'].mean()
[Link](df_zona_weekly.index.to_timestamp(), df_zona_weekly.values,
marker='o', label=zona, linewidth=2.5, markersize=5, alpha=0.8)
[Link](y=umbral, color='red', linestyle='--', linewidth=3, alpha=0.7,
label='Umbral crítico')
ax.set_xlabel('Semana', fontweight='bold', fontsize=13)
ax.set_ylabel('Consumo Promedio Semanal (kWh)', fontweight='bold', fontsize=13)
ax.set_title('GRÁFICA COMPARATIVA 2: EVOLUCIÓN TEMPORAL DE TODAS LAS ZONAS',
fontweight='bold', fontsize=16, pad=20)
[Link](loc='best', ncol=3, fontsize=10, framealpha=0.9)
[Link](True, alpha=0.3, linestyle='--')
ax.tick_params(axis='x', rotation=45)
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link]('grafica_comp_02_evolucion.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_comp_3_per_capita(metricas):
"""Gráfica Comparativa 3: Consumo Per Cápita"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
metricas_sorted_pc = metricas.sort_values('Consumo_per_capita', ascending=False)
colors_pc = [Link].RdYlGn_r([Link](0.2, 0.8, len(metricas_sorted_pc)))
bars = [Link](range(len(metricas_sorted_pc)),
metricas_sorted_pc['Consumo_per_capita'],
color=colors_pc, alpha=0.8, edgecolor='black', linewidth=2)
ax.set_xticks(range(len(metricas_sorted_pc)))
ax.set_xticklabels(metricas_sorted_pc.index, rotation=45, ha='right',
fontweight='bold', fontsize=12)
ax.set_ylabel('kWh por 1000 habitantes/año', fontweight='bold', fontsize=13)
ax.set_title('GRÁFICA COMPARATIVA 3: EFICIENCIA POBLACIONAL (CONSUMO PER CÁPITA)',
fontweight='bold', fontsize=16, pad=20)
[Link](True, alpha=0.3, axis='y', linestyle='--')
for bar in bars:
height = bar.get_height()
[Link](bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}', ha='center', va='bottom', fontweight='bold',
fontsize=10)
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link]('grafica_comp_03_per_capita.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_comp_4_variabilidad(df, metricas):
"""Gráfica Comparativa 4: Variabilidad por Zona"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
zonas_ordenadas = metricas.sort_values('Promedio', ascending=False).index
datos_box = [df[df['zona'] == zona]['consumo_kwh'].values for zona in
zonas_ordenadas]
bp = [Link](datos_box, labels=zonas_ordenadas, patch_artist=True,
medianprops=dict(color='red', linewidth=3),
boxprops=dict(facecolor='lightblue', alpha=0.7, linewidth=1.5),
whiskerprops=dict(linewidth=2),
capprops=dict(linewidth=2),
flierprops=dict(marker='o', markerfacecolor='red', markersize=8,
alpha=0.6))
[Link](y=350, color='red', linestyle='--', linewidth=3, label='Umbral crítico')
ax.set_xticklabels(zonas_ordenadas, rotation=45, ha='right', fontweight='bold',
fontsize=12)
ax.set_ylabel('Consumo (kWh)', fontweight='bold', fontsize=13)
ax.set_title('GRÁFICA COMPARATIVA 4: VARIABILIDAD Y DISPERSIÓN POR ZONA (BOXPLOT)',
fontweight='bold', fontsize=16, pad=20)
[Link](fontsize=12, framealpha=0.9)
[Link](True, alpha=0.3, axis='y', linestyle='--')
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link]('grafica_comp_04_variabilidad.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_comp_5_correlacion(df, metricas):
"""Gráfica Comparativa 5: Matriz de Correlación"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
zonas_para_corr = [Link](6).index
matriz_consumo = df[df['zona'].isin(zonas_para_corr)].pivot_table(
values='consumo_kwh', index='fecha', columns='zona'
)
corr_matrix = matriz_consumo.corr()
im = [Link](corr_matrix, cmap='coolwarm', aspect='auto', vmin=-1, vmax=1)
ax.set_xticks(range(len(corr_matrix)))
ax.set_yticks(range(len(corr_matrix)))
ax.set_xticklabels(corr_matrix.columns, rotation=45, ha='right', fontsize=11,
fontweight='bold')
ax.set_yticklabels(corr_matrix.columns, fontsize=11, fontweight='bold')
ax.set_title('GRÁFICA COMPARATIVA 5: MATRIZ DE CORRELACIÓN ENTRE ZONAS',
fontweight='bold', fontsize=16, pad=20)
for i in range(len(corr_matrix)):
for j in range(len(corr_matrix)):
text = [Link](j, i, f'{corr_matrix.iloc[i, j]:.2f}',
ha="center", va="center", color="black", fontsize=10,
fontweight='bold')
cbar = [Link](im, ax=ax)
cbar.set_label('Coeficiente de correlación de Pearson', fontweight='bold',
fontsize=12)
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link]('grafica_comp_05_correlacion.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
def grafica_comp_6_score_desempeno(metricas):
"""Gráfica Comparativa 6: Score de Desempeño"""
fig = [Link](figsize=(16, 10))
ax = [Link](111)
metricas_temp = [Link]()
metricas_temp['score_eficiencia'] = 100 - (metricas_temp['Promedio'] -
metricas_temp['Promedio'].min()) / (metricas_temp['Promedio'].max() -
metricas_temp['Promedio'].min()) * 50
metricas_temp['score_estabilidad'] = 100 - (metricas_temp['[Link]'] -
metricas_temp['[Link]'].min()) / (metricas_temp['[Link]'].max() -
metricas_temp['[Link]'].min()) * 50
metricas_temp['Score_Total'] = (metricas_temp['score_eficiencia'] +
metricas_temp['score_estabilidad']) / 2
metricas_sorted_score = metricas_temp.sort_values('Score_Total', ascending=True)
colors_score = ['#27ae60' if x > 70 else '#f39c12' if x > 50 else '#c0392b'
for x in metricas_sorted_score['Score_Total']]
bars = [Link](metricas_sorted_score.index, metricas_sorted_score['Score_Total'],
color=colors_score, alpha=0.8, edgecolor='black', linewidth=2)
[Link](x=70, color='green', linestyle='--', linewidth=2.5, alpha=0.7,
label='Excelente (>70)')
[Link](x=50, color='orange', linestyle='--', linewidth=2.5, alpha=0.7,
label='Aceptable (>50)')
ax.set_xlabel('Score Total (0-100)', fontweight='bold', fontsize=13)
ax.set_title('GRÁFICA COMPARATIVA 6: RANKING INTEGRADO DE DESEMPEÑO ENERGÉTICO',
fontweight='bold', fontsize=16, pad=20)
[Link](fontsize=11, framealpha=0.9)
[Link](True, alpha=0.3, axis='x', linestyle='--')
medallas = [' ', ' ', ' '] + [' ']*(len(metricas_sorted_score)-3)
for i, (idx, bar, medalla) in enumerate(zip(metricas_sorted_score.index, bars,
medallas[::-1])):
width = bar.get_width()
[Link](width + 2, bar.get_y() + bar.get_height()/2,
f'{medalla} {width:.1f}', ha='left', va='center', fontweight='bold',
fontsize=11)
plt.tight_layout(rect=[0, 0.18, 1, 0.98])
[Link]('grafica_comp_06_score.png', dpi=200, bbox_inches='tight')
# MOSTRAR SIN BLOQUEAR
[Link](block=False)
[Link](2)
# ============================================================================
# 9. PROGRAMA PRINCIPAL CON CONTROL INTERACTIVO
# ============================================================================
def ejecutar_analisis_completo():
"""Función principal que ejecuta todo el análisis con control interactivo"""
print("\n" + "="*80)
print("⚡ SISTEMA AVANZADO DE ANÁLISIS ENERGÉTICO - LA PAZ, BOLIVIA")
print(" VERSIÓN CON GRÁFICAS SECUENCIALES")
print("="*80)
# 1. Generar dataset
print("\n[PASO 1/9] Generando dataset completo...")
generador = GeneradorDatosEnergia(dias=180)
df = generador.generar_dataset_completo()
# 2. Seleccionar zona para análisis detallado
zona_analisis = "Miraflores"
df_zona = df[df["zona"] == zona_analisis].copy()
print(f"\n[PASO 2/9] Zona seleccionada para análisis detallado: {zona_analisis}")
# 3. Modelos diferenciales
print(f"\n[PASO 3/9] Ajustando modelos diferenciales...")
modelo_dif = ModeloDiferencialAvanzado(df_zona)
modelos_edo = modelo_dif.resolver_todos(E0=df_zona["consumo_kwh"].iloc[-1])
# 4. Machine Learning
print(f"\n[PASO 4/9] Entrenando modelos de Machine Learning...")
modelo_ml = ModeloMLEnergia(df)
resultados_ml = modelo_ml.entrenar_modelos(zona_analisis)
# 5. Detección de anomalías
print(f"\n[PASO 5/9] Detectando anomalías y picos...")
anomalias = detectar_anomalias(df_zona)
# 6. Optimización
print(f"\n[PASO 6/9] Optimizando estrategias de consumo...")
optimizador = OptimizadorConsumo(df_zona)
optimizador.optimizar_consumo()
# 7. Calcular métricas comparativas
print(f"\n[PASO 7/9] Calculando métricas comparativas entre zonas...")
metricas = [Link]('zona').agg({
'consumo_kwh': ['mean', 'max', 'std', 'min'],
'poblacion': 'first'
}).round(2)
[Link] = ['Promedio', 'Máximo', '[Link]', 'Mínimo', 'Población']
metricas['Consumo_per_capita'] = (metricas['Promedio'] * 365 / metricas['Población']
* 1000).round(2)
# 8. Generar gráficas individuales de la zona con control interactivo
print(f"\n[PASO 8/9] Generando 8 gráficas individuales para {zona_analisis}...")
graficas_individuales = [
("Predicciones con Ecuaciones Diferenciales",
lambda: grafica_1_predicciones_edo(df_zona, modelos_edo, zona_analisis)),
("Precisión de Modelos ML",
lambda: grafica_2_precision_ml(resultados_ml, zona_analisis)),
("Importancia de Variables Predictivas",
lambda: grafica_3_importancia_features(resultados_ml, zona_analisis)),
("Distribución Estadística del Consumo",
lambda: grafica_4_distribucion_estadistica(df_zona, anomalias, zona_analisis)),
("Relación Temperatura-Consumo",
lambda: grafica_5_temperatura_consumo(df_zona, zona_analisis)),
("Patrón Semanal de Consumo",
lambda: grafica_6_patron_semanal(df_zona, zona_analisis)),
("Detección de Anomalías y Picos",
lambda: grafica_7_deteccion_anomalias(df_zona, anomalias, zona_analisis)),
("Tendencia Temporal y Estacionalidad",
lambda: grafica_8_tendencia_temporal(df_zona, zona_analisis))
]
for i, (nombre, funcion) in enumerate(graficas_individuales, 1):
print(f"\n Gráfica {i}: {nombre}")
input(" Presiona Enter para mostrar esta gráfica...")
funcion()
[Link](0.5) # Pequeña pausa para asegurar que se muestre
# 9. Generar gráficas comparativas con control interactivo
print(f"\n[PASO 9/9] Generando 6 gráficas comparativas entre todas las zonas...")
graficas_comparativas = [
("Ranking de Zonas por Consumo",
lambda: grafica_comp_1_ranking_consumo(df, metricas)),
("Evolución Temporal de Todas las Zonas",
lambda: grafica_comp_2_evolucion_zonas(df)),
("Consumo Per Cápita por Zona",
lambda: grafica_comp_3_per_capita(metricas)),
("Variabilidad y Dispersión por Zona",
lambda: grafica_comp_4_variabilidad(df, metricas)),
("Matriz de Correlación entre Zonas",
lambda: grafica_comp_5_correlacion(df, metricas)),
("Ranking Integrado de Desempeño Energético",
lambda: grafica_comp_6_score_desempeno(metricas))
]
for i, (nombre, funcion) in enumerate(graficas_comparativas, 1):
print(f"\n Gráfica Comparativa {i}: {nombre}")
input(" Presiona Enter para mostrar esta gráfica...")
funcion()
[Link](0.5)
# Resumen final
print(f"\n" + "="*80)
print("✅ ANÁLISIS COMPLETADO EXITOSAMENTE")
print("="*80)
print(f"\n Archivos generados:")
print(f"\n DATASET:")
print(f" • consumo_energia_lapaz_completo.csv")
print(f"\n GRÁFICAS INDIVIDUALES ({zona_analisis}):")
for i in range(1, 9):
print(f" • grafica_{i:02d}_*.png")
print(f"\n GRÁFICAS COMPARATIVAS (todas las zonas):")
for i in range(1, 7):
print(f" • grafica_comp_{i:02d}_*.png")
print(f"\n Características del sistema:")
print(f" ✓ 8 gráficas individuales detalladas")
print(f" ✓ 6 gráficas comparativas entre zonas")
print(f" ✓ Control interactivo para ver cada gráfica")
print(f" ✓ Explicaciones detalladas en cada visualización")
print(f" ✓ Archivos PNG guardados automáticamente")
input(f"\n Presiona Enter para finalizar y cerrar todas las ventanas...")
[Link]('all')
print("¡Análisis finalizado! Todas las gráficas han sido guardadas.")
if __name__ == "__main__":
ejecutar_analisis_completo()