C2MV commited on
Commit
eef6991
verified
1 Parent(s): e579437

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -362
app.py CHANGED
@@ -1,11 +1,11 @@
1
  import pandas as pd
2
- import matplotlib.pyplot as plt
3
  import numpy as np
4
- import matplotlib.dates as mdates
5
- from datetime import datetime
6
- import seaborn as sns
7
  import gradio as gr
8
- from matplotlib.figure import Figure
 
 
9
 
10
  cities_data = {
11
  'Abancay': {
@@ -1151,17 +1151,15 @@ cities_data = {
1151
  }
1152
 
1153
 
1154
- plt.rcParams['figure.figsize'] = (14, 9)
1155
- plt.rcParams['font.size'] = 13
1156
- plt.rcParams['font.family'] = 'sans-serif'
1157
- plt.style.use('seaborn-v0_8-whitegrid')
1158
 
1159
  COLORES = {
1160
  'Total': '#2C3E50',
1161
  'Hombres': '#3498DB',
1162
  'Mujeres': '#E74C3C',
1163
- 'Brecha': '#8E44AD',
1164
- 'Fondo': '#F8F9F9'
1165
  }
1166
 
1167
  def normalizar_nombres_ciudades(nombre):
@@ -1176,388 +1174,251 @@ def normalizar_nombres_ciudades(nombre):
1176
  'Pura': 'Piura',
1177
  'Posalipa': 'Pucallpa',
1178
  'Tagapito': 'Talara',
1179
- 'Juliana': 'Juliaca'
 
 
 
1180
  }
1181
  return correcciones.get(nombre, nombre)
1182
 
1183
- def ordenar_trimestres(df, col_fecha='Trimestre'):
1184
- df = df.copy()
1185
- if col_fecha == 'Trimestre':
1186
- fechas = []
1187
- for t in df[col_fecha]:
1188
- if 'Q' in t:
1189
- a帽o = t.split('-')[0]
1190
- trimestre = t.split('-')[1]
1191
- mes = {'Q1': '03', 'Q2': '06', 'Q3': '09', 'Q4': '12'}[trimestre]
1192
- fechas.append(f"{a帽o}-{mes}")
1193
- else:
1194
- fechas.append(f"{t}-01")
1195
- df['fecha_orden'] = pd.to_datetime(fechas, format='%Y-%m')
1196
- else:
1197
- fechas = []
1198
- for p in df[col_fecha]:
1199
- if '/' in p:
1200
- fechas.append(p.split('/')[0])
1201
- else:
1202
- fechas.append(f"{p}-01")
1203
- df['fecha_orden'] = pd.to_datetime(fechas, format='%Y-%m', errors='coerce')
1204
- df = df.sort_values('fecha_orden')
1205
- return df
1206
-
1207
- def calcular_rango_y(df, categorias, padding=0.15):
1208
- valores = df[categorias].values.flatten()
1209
- valores = valores[~np.isnan(valores)]
1210
- if len(valores) == 0:
1211
- return (0, 1)
1212
- min_val = np.nanmin(valores)
1213
- max_val = np.nanmax(valores)
1214
- rango = max_val - min_val
1215
- return (max(0, min_val - rango*padding), max_val + rango*padding)
1216
-
1217
- def graficar_datos_mejorados(df, titulo, subtitulo, ylabel, col_fecha='Trimestre', mostrar_valores=True, formato_valores='.1f'):
1218
- fig, ax = plt.subplots(figsize=(14, 8))
1219
- ax.set_facecolor(COLORES['Fondo'])
1220
- fig.patch.set_facecolor(COLORES['Fondo'])
1221
- ax.grid(axis='y', linestyle='--', alpha=0.7)
1222
-
1223
- categorias = df.columns[1:4]
1224
- x_indices = np.arange(len(df))
1225
- ylim = calcular_rango_y(df, categorias)
1226
-
1227
- for categoria in categorias:
1228
- if categoria in df.columns and not df[categoria].isna().all():
1229
- y_vals = df[categoria].values
1230
- valid_mask = ~np.isnan(y_vals)
1231
-
1232
- ax.plot(x_indices[valid_mask], y_vals[valid_mask],
1233
- marker='o', linewidth=3, markersize=8,
1234
- label=categoria, color=COLORES[categoria])
1235
-
1236
- if mostrar_valores:
1237
- for i, valor in zip(x_indices[valid_mask], y_vals[valid_mask]):
1238
- offset = 0.2 if categoria == 'Hombres' else -0.8 if categoria == 'Mujeres' else 0
1239
- ax.annotate(f'{valor:{formato_valores}}',
1240
- xy=(i, valor),
1241
- xytext=(0, 5 + offset),
1242
- textcoords='offset points',
1243
- ha='center', va='bottom',
1244
- fontsize=10, fontweight='bold',
1245
- color=COLORES[categoria],
1246
- bbox=dict(boxstyle='round,pad=0.3', fc='white', alpha=0.7))
1247
-
1248
- ax.set_title(titulo, fontsize=18, fontweight='bold', pad=20)
1249
- plt.figtext(0.5, 0.01, subtitulo, ha='center', fontsize=12, fontstyle='italic')
1250
- ax.set_ylabel(ylabel, fontsize=14, fontweight='bold')
1251
- ax.set_xticks(x_indices)
1252
- ax.set_xticklabels(df[col_fecha].astype(str), rotation=45, ha='right')
1253
- ax.set_ylim(ylim)
1254
 
1255
- for spine in ['top', 'right']:
1256
- ax.spines[spine].set_visible(False)
1257
-
1258
- legend = ax.legend(fontsize=12, frameon=True, framealpha=0.9,
1259
- facecolor='white', edgecolor='lightgrey',
1260
- loc='upper right', bbox_to_anchor=(0.98, 0.98))
1261
- plt.tight_layout(rect=[0, 0.03, 1, 1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1262
  return fig
1263
 
1264
- def generar_analisis_global():
1265
- figuras = []
1266
- estilo_comun = {
1267
- 'marker': 'o',
1268
- 'linewidth': 2,
1269
- 'markersize': 6,
1270
- 'alpha': 0.8
1271
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1272
 
1273
- # Gr谩fico de Desempleo Global
1274
- fig_desempleo = Figure(figsize=(16, 10))
1275
- ax_desempleo = fig_desempleo.add_subplot(111)
1276
- for ciudad, datos in cities_data.items():
1277
  nombre = normalizar_nombres_ciudades(ciudad)
1278
- df = ordenar_trimestres(pd.DataFrame(datos['desempleo_trimestral'],
1279
- columns=["Trimestre", "Total", "Hombres", "Mujeres"]))
1280
  if not df.empty:
1281
- ax_desempleo.plot(df['fecha_orden'], df['Total'],
1282
- label=nombre, **estilo_comun)
 
 
 
1283
 
1284
- ax_desempleo.set_title('TASA DE DESEMPLEO - COMPARATIVA ENTRE CIUDADES',
1285
- fontsize=18, fontweight='bold', pad=20)
1286
- ax_desempleo.set_ylabel('Tasa (%)', fontsize=14)
1287
- ax_desempleo.grid(True, linestyle='--', alpha=0.5)
1288
- ax_desempleo.xaxis.set_major_locator(mdates.YearLocator())
1289
- ax_desempleo.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
1290
 
1291
- handles, labels = ax_desempleo.get_legend_handles_labels()
1292
- leg = fig_desempleo.legend(handles, labels,
1293
- loc='upper center',
1294
- bbox_to_anchor=(0.5, -0.12),
1295
- ncol=5,
1296
- fontsize=10,
1297
- frameon=True,
1298
- fancybox=True,
1299
- shadow=True,
1300
- title='Ciudades',
1301
- title_fontsize='12')
1302
- fig_desempleo.tight_layout(rect=[0, 0.1, 1, 0.95])
1303
- figuras.append(fig_desempleo)
 
 
 
 
1304
 
1305
- # Gr谩fico de Ingresos Global
1306
- fig_ingresos = Figure(figsize=(16, 10))
1307
- ax_ingresos = fig_ingresos.add_subplot(111)
1308
- for ciudad, datos in cities_data.items():
 
 
 
 
 
1309
  nombre = normalizar_nombres_ciudades(ciudad)
1310
- df = ordenar_trimestres(pd.DataFrame(datos['ingresos_periodo'],
1311
- columns=["Periodo", "Total", "Hombres", "Mujeres"]),
1312
- 'Periodo')
1313
  if not df.empty:
1314
- ax_ingresos.plot(df['fecha_orden'], df['Total'],
1315
- label=nombre, **estilo_comun)
1316
 
1317
- ax_ingresos.set_title('INGRESOS PROMEDIO - COMPARATIVA ENTRE CIUDADES',
1318
- fontsize=18, fontweight='bold', pad=20)
1319
- ax_ingresos.set_ylabel('Ingresos (Soles)', fontsize=14)
1320
- ax_ingresos.grid(True, linestyle='--', alpha=0.5)
1321
- ax_ingresos.xaxis.set_major_locator(mdates.YearLocator())
1322
- ax_ingresos.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
 
 
 
 
 
 
1323
 
1324
- handles, labels = ax_ingresos.get_legend_handles_labels()
1325
- leg = fig_ingresos.legend(handles, labels,
1326
- loc='upper center',
1327
- bbox_to_anchor=(0.5, -0.12),
1328
- ncol=5,
1329
- fontsize=10,
1330
- frameon=True,
1331
- fancybox=True,
1332
- shadow=True,
1333
- title='Ciudades',
1334
- title_fontsize='12')
1335
- fig_ingresos.tight_layout(rect=[0, 0.1, 1, 0.95])
1336
- figuras.append(fig_ingresos)
1337
-
1338
- # Gr谩fico de Brecha Salarial Global
1339
- fig_brecha = Figure(figsize=(16, 10))
1340
- ax_brecha = fig_brecha.add_subplot(111)
1341
- for ciudad, datos in cities_data.items():
1342
  nombre = normalizar_nombres_ciudades(ciudad)
1343
- df = ordenar_trimestres(pd.DataFrame(datos['ingresos_periodo'],
1344
- columns=["Periodo", "Total", "Hombres", "Mujeres"]),
1345
- 'Periodo')
1346
- if not df.empty and 'Hombres' in df.columns and 'Mujeres' in df.columns:
1347
- df['Brecha'] = (df['Hombres'] - df['Mujeres']) / df['Hombres'] * 100
1348
- ax_brecha.plot(df['fecha_orden'], df['Brecha'],
1349
- label=nombre, **estilo_comun)
1350
 
1351
- ax_brecha.set_title('BRECHA SALARIAL DE G脡NERO - COMPARATIVA ENTRE CIUDADES',
1352
- fontsize=18, fontweight='bold', pad=20)
1353
- ax_brecha.set_ylabel('Brecha (%)', fontsize=14)
1354
- ax_brecha.grid(True, linestyle='--', alpha=0.5)
1355
- ax_brecha.xaxis.set_major_locator(mdates.YearLocator())
1356
- ax_brecha.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
 
 
 
 
 
 
 
1357
 
1358
- handles, labels = ax_brecha.get_legend_handles_labels()
1359
- leg = fig_brecha.legend(handles, labels,
1360
- loc='upper center',
1361
- bbox_to_anchor=(0.5, -0.12),
1362
- ncol=5,
1363
- fontsize=10,
1364
- frameon=True,
1365
- fancybox=True,
1366
- shadow=True,
1367
- title='Ciudades',
1368
- title_fontsize='12')
1369
- fig_brecha.tight_layout(rect=[0, 0.1, 1, 0.95])
1370
- figuras.append(fig_brecha)
1371
-
1372
- return figuras
1373
-
1374
- def load_data(city):
1375
- data = cities_data[city]
1376
- return [
1377
- data['desempleo_trimestral'],
1378
- data['ingresos_periodo'],
1379
- data['informal_periodo'],
1380
- data['actividad_trimestral'],
1381
- data['poblacion_ocupada']
1382
- ]
1383
-
1384
- def generate_plots(desempleo_df, ingresos_df, informal_df, actividad_df, poblacion_df):
1385
- dfs = {
1386
- 'desempleo': pd.DataFrame(desempleo_df, columns=["Trimestre", "Total", "Hombres", "Mujeres"]),
1387
- 'ingresos': pd.DataFrame(ingresos_df, columns=["Periodo", "Total", "Hombres", "Mujeres"]),
1388
- 'informal': pd.DataFrame(informal_df, columns=["Periodo", "Total", "Hombres", "Mujeres"]),
1389
- 'actividad': pd.DataFrame(actividad_df, columns=["Trimestre", "Total", "Hombres", "Mujeres"]),
1390
- 'poblacion': pd.DataFrame(poblacion_df, columns=["Trimestre", "Total", "Hombres", "Mujeres"])
1391
- }
1392
-
1393
- for key in dfs:
1394
- for col in ["Total", "Hombres", "Mujeres"]:
1395
- dfs[key][col] = pd.to_numeric(dfs[key][col], errors='coerce')
1396
-
1397
- for key in dfs:
1398
- dfs[key] = ordenar_trimestres(dfs[key], 'Trimestre' if key in ['desempleo', 'actividad', 'poblacion'] else 'Periodo')
1399
-
1400
- figs = []
1401
-
1402
- # Radar Plot
1403
- problem_metrics = {
1404
- 'Desempleo': dfs['desempleo']['Total'].iloc[-1] if not dfs['desempleo'].empty else 0,
1405
- 'Informalidad': dfs['informal']['Total'].iloc[-1] if not dfs['informal'].empty else 0,
1406
- 'Brecha Salarial': (
1407
- (dfs['ingresos']['Hombres'].iloc[-1] - dfs['ingresos']['Mujeres'].iloc[-1]) /
1408
- dfs['ingresos']['Hombres'].iloc[-1] * 100
1409
- if not dfs['ingresos'].empty and pd.notna(dfs['ingresos']['Hombres'].iloc[-1]) and pd.notna(dfs['ingresos']['Mujeres'].iloc[-1]) else 0
1410
- ),
1411
- 'Actividad': dfs['actividad']['Total'].iloc[-1] if not dfs['actividad'].empty else 0
1412
- }
1413
-
1414
- categories = list(problem_metrics.keys())
1415
- values = list(problem_metrics.values())
1416
- angles = np.linspace(0, 2*np.pi, len(categories), endpoint=False).tolist()
1417
- values += values[:1]
1418
- angles += angles[:1]
1419
-
1420
- fig_radar, ax_radar = plt.subplots(figsize=(10, 10), subplot_kw=dict(polar=True))
1421
- ax_radar.fill(angles, values, color=COLORES['Brecha'], alpha=0.2)
1422
- ax_radar.set_theta_offset(np.pi/2)
1423
- ax_radar.set_theta_direction(-1)
1424
- ax_radar.set_thetagrids(np.degrees(angles[:-1]), labels=categories)
1425
- ax_radar.set_rlabel_position(0)
1426
- plt.yticks([20,40,60,80], ["20%","40%","60%","80%"], color="grey", size=10)
1427
- plt.ylim(0,100)
1428
- ax_radar.set_title(f'RADAR DE PROBLEM脕TICAS LABORALES\n{dfs["desempleo"]["Trimestre"].iloc[-1]}',
1429
- pad=20, fontsize=14, fontweight='bold')
1430
- figs.append(fig_radar)
1431
-
1432
- # Gr谩ficos principales
1433
- figs.append(graficar_datos_mejorados(dfs['desempleo'], 'TASA DE DESEMPLEO', 'Evoluci贸n por g茅nero', 'Tasa (%)'))
1434
- figs.append(graficar_datos_mejorados(dfs['ingresos'], 'INGRESOS PROMEDIO', 'Por per铆odo y g茅nero', 'Ingreso (soles)', col_fecha='Periodo', formato_valores='.0f'))
1435
- figs.append(graficar_datos_mejorados(dfs['informal'], 'TASA DE INFORMALIDAD', 'Por per铆odo', 'Tasa (%)', col_fecha='Periodo'))
1436
- figs.append(graficar_datos_mejorados(dfs['actividad'], 'TASA DE ACTIVIDAD', 'Participaci贸n econ贸mica', 'Tasa (%)'))
1437
- figs.append(graficar_datos_mejorados(dfs['poblacion'], 'POBLACI脫N OCUPADA', 'En miles de personas', 'Poblaci贸n (miles)'))
1438
 
1439
- # Brecha Salarial
1440
- ingresos = dfs['ingresos']
1441
- brecha = []
1442
- for h, m in zip(ingresos['Hombres'], ingresos['Mujeres']):
1443
- if pd.notna(h) and pd.notna(m) and h != 0:
1444
- brecha.append((h-m)/h*100)
1445
- else:
1446
- brecha.append(np.nan)
1447
 
1448
- fig_brecha, ax_brecha = plt.subplots(figsize=(14,8))
1449
- valid_indices = [i for i, b in enumerate(brecha) if pd.notna(b)]
1450
- valid_periods = [str(ingresos['Periodo'].iloc[i]) for i in valid_indices]
1451
- valid_brecha = [brecha[i] for i in valid_indices]
1452
 
1453
- if valid_periods:
1454
- bars = ax_brecha.bar(valid_periods, valid_brecha, color=COLORES['Brecha'])
1455
- for bar in bars:
1456
- height = bar.get_height()
1457
- ax_brecha.text(bar.get_x() + bar.get_width()/2., height + 0.5,
1458
- f'{height:.1f}%', ha='center', va='bottom',
1459
- fontsize=10, fontweight='bold')
1460
- ax_brecha.set_title('BRECHA SALARIAL DE G脡NERO', fontsize=18, pad=20)
1461
- ax_brecha.set_ylabel('Brecha (%)', fontsize=14)
1462
- ax_brecha.grid(axis='y', linestyle='--', alpha=0.7)
1463
- figs.append(fig_brecha)
1464
-
1465
- return figs
1466
-
1467
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), css=".gradio-container {background-color: #F8F9F9}") as app:
1468
- gr.Markdown("# 馃搳 Dashboard de Indicadores Laborales por G茅nero")
1469
- gr.Markdown("Analiza los principales indicadores del mercado laboral con perspectiva de g茅nero")
1470
-
1471
  with gr.Row():
1472
- city = gr.Dropdown(
1473
  list(cities_data.keys()),
1474
- label="Seleccione una Ciudad",
1475
  value="Chimbote",
1476
- info="Elija la ciudad para visualizar sus datos"
1477
  )
1478
-
1479
- with gr.Tab("Datos"):
1480
- with gr.Accordion("Tasa de Desempleo", open=True):
1481
- desempleo_df = gr.Dataframe(
1482
- headers=["Trimestre", "Total", "Hombres", "Mujeres"],
1483
- datatype=["str", "number", "number", "number"],
1484
- label="Datos de Desempleo"
1485
- )
1486
- with gr.Accordion("Ingresos", open=False):
1487
- ingresos_df = gr.Dataframe(
1488
- headers=["Periodo", "Total", "Hombres", "Mujeres"],
1489
- datatype=["str", "number", "number", "number"],
1490
- label="Datos de Ingresos"
1491
- )
1492
- with gr.Accordion("Informalidad", open=False):
1493
- informal_df = gr.Dataframe(
1494
- headers=["Periodo", "Total", "Hombres", "Mujeres"],
1495
- datatype=["str", "number", "number", "number"],
1496
- label="Datos de Informalidad"
1497
- )
1498
- with gr.Accordion("Actividad Econ贸mica", open=False):
1499
- actividad_df = gr.Dataframe(
1500
- headers=["Trimestre", "Total", "Hombres", "Mujeres"],
1501
- datatype=["str", "number", "number", "number"],
1502
- label="Datos de Actividad"
1503
- )
1504
- with gr.Accordion("Poblaci贸n Ocupada", open=False):
1505
- poblacion_df = gr.Dataframe(
1506
- headers=["Trimestre", "Total", "Hombres", "Mujeres"],
1507
- datatype=["str", "number", "number", "number"],
1508
- label="Datos de Poblaci贸n Ocupada"
1509
- )
1510
-
1511
- btn = gr.Button("Generar Visualizaciones", variant="primary")
1512
-
1513
- with gr.Tab("Visualizaciones"):
1514
- with gr.Row():
1515
- radar_plot = gr.Plot(label="Radar de Problem谩ticas Laborales")
1516
- with gr.Row():
1517
- desempleo_plot = gr.Plot(label="Tasa de Desempleo")
1518
- with gr.Row():
1519
- ingresos_plot = gr.Plot(label="Ingresos Promedio")
1520
- brecha_salarial_plot = gr.Plot(label="Brecha Salarial de G茅nero")
1521
  with gr.Row():
1522
- informalidad_plot = gr.Plot(label="Tasa de Informalidad")
1523
- actividad_plot = gr.Plot(label="Tasa de Actividad")
1524
  with gr.Row():
1525
- poblacion_plot = gr.Plot(label="Poblaci贸n Ocupada")
1526
-
1527
- with gr.Tab("An谩lisis Global"):
1528
- gr.Markdown("## An谩lisis Comparativo entre Ciudades")
1529
- global_btn = gr.Button("Generar An谩lisis Global", variant="primary")
1530
  with gr.Row():
1531
- global_desempleo_plot = gr.Plot(label="Comparativa de Desempleo")
 
 
 
1532
  with gr.Row():
1533
- global_ingresos_plot = gr.Plot(label="Comparativa de Ingresos")
1534
  with gr.Row():
1535
- global_brecha_plot = gr.Plot(label="Comparativa de Brecha Salarial")
1536
-
1537
- city.change(
1538
- fn=load_data,
1539
- inputs=city,
1540
- outputs=[desempleo_df, ingresos_df, informal_df, actividad_df, poblacion_df]
1541
- )
1542
-
1543
- btn.click(
1544
- fn=generate_plots,
1545
- inputs=[desempleo_df, ingresos_df, informal_df, actividad_df, poblacion_df],
1546
- outputs=[
1547
- radar_plot,
1548
- desempleo_plot,
1549
- ingresos_plot,
1550
- informalidad_plot,
1551
- actividad_plot,
1552
- poblacion_plot,
1553
- brecha_salarial_plot
 
 
 
 
 
 
 
 
 
 
1554
  ]
1555
- )
1556
-
1557
- global_btn.click(
1558
- fn=generar_analisis_global,
1559
- inputs=[],
1560
- outputs=[global_desempleo_plot, global_ingresos_plot, global_brecha_plot]
1561
- )
1562
 
1563
  app.launch(debug=True)
 
1
  import pandas as pd
 
2
  import numpy as np
3
+ import plotly.express as px
4
+ import plotly.graph_objects as go
 
5
  import gradio as gr
6
+ from gradio.components import Plot
7
+
8
+ #DATOS
9
 
10
  cities_data = {
11
  'Abancay': {
 
1151
  }
1152
 
1153
 
1154
+ #DATOS
1155
+
1156
+
 
1157
 
1158
  COLORES = {
1159
  'Total': '#2C3E50',
1160
  'Hombres': '#3498DB',
1161
  'Mujeres': '#E74C3C',
1162
+ 'Brecha': '#8E44AD'
 
1163
  }
1164
 
1165
  def normalizar_nombres_ciudades(nombre):
 
1174
  'Pura': 'Piura',
1175
  'Posalipa': 'Pucallpa',
1176
  'Tagapito': 'Talara',
1177
+ 'Juliana': 'Juliaca',
1178
+ 'Chimbote': 'Chimbote',
1179
+ 'Arequipa': 'Arequipa',
1180
+ 'Trujillo': 'Trujillo'
1181
  }
1182
  return correcciones.get(nombre, nombre)
1183
 
1184
+ def procesar_dataframe(df, columnas):
1185
+ df_procesado = pd.DataFrame(df, columns=columnas)
1186
+ for col in columnas[1:]:
1187
+ df_procesado[col] = pd.to_numeric(df_procesado[col], errors='coerce')
1188
+ return df_procesado.dropna(how='all')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1189
 
1190
+ def crear_grafico_lineas(df, titulo, eje_y, color=None, formato=None):
1191
+ fig = go.Figure()
1192
+
1193
+ for columna in ['Total', 'Hombres', 'Mujeres']:
1194
+ if columna in df.columns and not df[columna].isnull().all():
1195
+ fig.add_trace(go.Scatter(
1196
+ x=df.iloc[:,0],
1197
+ y=df[columna],
1198
+ name=columna,
1199
+ mode='lines+markers',
1200
+ line=dict(color=COLORES.get(columna, color),
1201
+ marker=dict(size=8),
1202
+ hovertemplate=f'<b>{columna}</b>: %{{y:{formato or ".2f"}}}<extra></extra>'
1203
+ ))
1204
+
1205
+ fig.update_layout(
1206
+ title=dict(text=titulo, x=0.5, font=dict(size=20)),
1207
+ yaxis_title=eje_y,
1208
+ xaxis_title='Periodo',
1209
+ hovermode='x unified',
1210
+ template='plotly_white',
1211
+ legend=dict(
1212
+ orientation="h",
1213
+ yanchor="bottom",
1214
+ y=1.02,
1215
+ xanchor="right",
1216
+ x=1
1217
+ ),
1218
+ height=500
1219
+ )
1220
+
1221
+ if formato:
1222
+ fig.update_layout(yaxis_tickformat=formato)
1223
+
1224
  return fig
1225
 
1226
+ def crear_radar_plot(dfs):
1227
+ metricas = {}
1228
+
1229
+ try:
1230
+ metricas['Desempleo'] = dfs['desempleo']['Total'].iloc[-1]
1231
+ metricas['Informalidad'] = dfs['informal']['Total'].iloc[-1]
1232
+ metricas['Actividad'] = dfs['actividad']['Total'].iloc[-1]
1233
+ metricas['Brecha Salarial'] = ((dfs['ingresos']['Hombres'].iloc[-1] -
1234
+ dfs['ingresos']['Mujeres'].iloc[-1]) / \
1235
+ dfs['ingresos']['Hombres'].iloc[-1] * 100
1236
+ except (KeyError, IndexError, ZeroDivisionError):
1237
+ return go.Figure()
1238
+
1239
+ categories = list(metricas.keys())
1240
+ values = list(metricas.values())
1241
+
1242
+ fig = go.Figure()
1243
+
1244
+ fig.add_trace(go.Scatterpolar(
1245
+ r=values + [values[0]],
1246
+ theta=categories + [categories[0]],
1247
+ fill='toself',
1248
+ fillcolor='rgba(142, 68, 173, 0.2)',
1249
+ line=dict(color=COLORES['Brecha'], width=2),
1250
+ name='Indicadores'
1251
+ ))
1252
+
1253
+ fig.update_layout(
1254
+ polar=dict(
1255
+ radialaxis=dict(
1256
+ visible=True,
1257
+ range=[0, 100],
1258
+ tickfont=dict(size=12),
1259
+ angularaxis=dict(
1260
+ rotation=90,
1261
+ direction='clockwise',
1262
+ tickfont=dict(size=14))
1263
+ ),
1264
+ title=dict(text='Radar de Indicadores Laborales', x=0.5, font=dict(size=20)),
1265
+ showlegend=False,
1266
+ height=500
1267
+ )
1268
+
1269
+ return fig
1270
 
1271
+ def analisis_comparativo_desempleo():
1272
+ datos = []
1273
+
1274
+ for ciudad, data in cities_data.items():
1275
  nombre = normalizar_nombres_ciudades(ciudad)
1276
+ df = procesar_dataframe(data['desempleo_trimestral'],
1277
+ ["Trimestre", "Total", "Hombres", "Mujeres"])
1278
  if not df.empty:
1279
+ ultimo_valor = df['Total'].iloc[-1]
1280
+ datos.append({'Ciudad': nombre, 'Desempleo': ultimo_valor})
1281
+
1282
+ if not datos:
1283
+ return go.Figure()
1284
 
1285
+ df_comparativo = pd.DataFrame(datos).sort_values('Desempleo', ascending=False)
 
 
 
 
 
1286
 
1287
+ fig = px.bar(df_comparativo,
1288
+ x='Ciudad',
1289
+ y='Desempleo',
1290
+ color='Desempleo',
1291
+ color_continuous_scale='Bluered',
1292
+ text_auto='.1f%',
1293
+ title='Comparaci贸n de Tasa de Desempleo entre Ciudades')
1294
+
1295
+ fig.update_layout(
1296
+ xaxis_title='',
1297
+ yaxis_title='Tasa de Desempleo (%)',
1298
+ coloraxis_showscale=False,
1299
+ xaxis={'categoryorder':'total descending'},
1300
+ height=600
1301
+ )
1302
+
1303
+ return fig
1304
 
1305
+ def generar_analisis_global():
1306
+ figs = []
1307
+
1308
+ # Gr谩fico comparativo de desempleo
1309
+ figs.append(analisis_comparativo_desempleo())
1310
+
1311
+ # Gr谩fico de tendencia de ingresos
1312
+ datos_ingresos = []
1313
+ for ciudad, data in cities_data.items():
1314
  nombre = normalizar_nombres_ciudades(ciudad)
1315
+ df = procesar_dataframe(data['ingresos_periodo'],
1316
+ ["Periodo", "Total", "Hombres", "Mujeres"])
 
1317
  if not df.empty:
1318
+ df['Ciudad'] = nombre
1319
+ datos_ingresos.append(df)
1320
 
1321
+ if datos_ingresos:
1322
+ df_ingresos = pd.concat(datos_ingresos)
1323
+ fig_ingresos = px.line(df_ingresos,
1324
+ x='Periodo',
1325
+ y='Total',
1326
+ color='Ciudad',
1327
+ title='Evoluci贸n de Ingresos por Ciudad',
1328
+ markers=True)
1329
+ fig_ingresos.update_layout(height=600)
1330
+ figs.append(fig_ingresos)
1331
+ else:
1332
+ figs.append(go.Figure())
1333
 
1334
+ # Gr谩fico de brecha salarial
1335
+ datos_brecha = []
1336
+ for ciudad, data in cities_data.items():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1337
  nombre = normalizar_nombres_ciudades(ciudad)
1338
+ df = procesar_dataframe(data['ingresos_periodo'],
1339
+ ["Periodo", "Total", "Hombres", "Mujeres"])
1340
+ if not df.empty:
1341
+ df['Brecha'] = (df['Hombres'] - df['Mujeres']) / df['Hombres'].replace(0, np.nan) * 100
1342
+ df['Ciudad'] = nombre
1343
+ datos_brecha.append(df)
 
1344
 
1345
+ if datos_brecha:
1346
+ df_brecha = pd.concat(datos_brecha)
1347
+ fig_brecha = px.bar(df_brecha,
1348
+ x='Periodo',
1349
+ y='Brecha',
1350
+ color='Ciudad',
1351
+ barmode='group',
1352
+ title='Evoluci贸n de Brecha Salarial por Ciudad',
1353
+ text_auto='.1f%')
1354
+ fig_brecha.update_layout(height=600)
1355
+ figs.append(fig_brecha)
1356
+ else:
1357
+ figs.append(go.Figure())
1358
 
1359
+ return figs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1360
 
1361
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"),
1362
+ css=".gradio-container {background-color: white}") as app:
 
 
 
 
 
 
1363
 
1364
+ gr.Markdown("# 馃搳 Dashboard Anal铆tico del Mercado Laboral")
 
 
 
1365
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1366
  with gr.Row():
1367
+ ciudad = gr.Dropdown(
1368
  list(cities_data.keys()),
1369
+ label="Seleccionar Ciudad",
1370
  value="Chimbote",
1371
+ interactive=True
1372
  )
1373
+
1374
+ with gr.Tab("An谩lisis Ciudad"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1375
  with gr.Row():
1376
+ desempleo_plot = Plot(label="Tasa de Desempleo")
1377
+ ingresos_plot = Plot(label="Ingresos Promedio")
1378
  with gr.Row():
1379
+ informalidad_plot = Plot(label="Tasa de Informalidad")
1380
+ actividad_plot = Plot(label="Tasa de Actividad")
 
 
 
1381
  with gr.Row():
1382
+ radar_plot = Plot(label="Radar de Indicadores")
1383
+ brecha_plot = Plot(label="Brecha Salarial")
1384
+
1385
+ with gr.Tab("An谩lisis Comparativo"):
1386
  with gr.Row():
1387
+ global_desempleo = Plot(label="Ranking de Desempleo")
1388
  with gr.Row():
1389
+ global_ingresos = Plot(label="Comparativa de Ingresos")
1390
+ global_brecha = Plot(label="Evoluci贸n Brecha Salarial")
1391
+
1392
+ @app.change(inputs=ciudad, outputs=[desempleo_plot, ingresos_plot,
1393
+ informalidad_plot, actividad_plot,
1394
+ radar_plot, brecha_plot])
1395
+ def actualizar_graficos(ciudad):
1396
+ data = cities_data[ciudad]
1397
+
1398
+ dfs = {
1399
+ 'desempleo': procesar_dataframe(data['desempleo_trimestral'],
1400
+ ["Trimestre", "Total", "Hombres", "Mujeres"]),
1401
+ 'ingresos': procesar_dataframe(data['ingresos_periodo'],
1402
+ ["Periodo", "Total", "Hombres", "Mujeres"]),
1403
+ 'informal': procesar_dataframe(data['informal_periodo'],
1404
+ ["Periodo", "Total", "Hombres", "Mujeres"]),
1405
+ 'actividad': procesar_dataframe(data['actividad_trimestral'],
1406
+ ["Trimestre", "Total", "Hombres", "Mujeres"])
1407
+ }
1408
+
1409
+ return [
1410
+ crear_grafico_lineas(dfs['desempleo'], "Tasa de Desempleo", "%", ".1f"),
1411
+ crear_grafico_lineas(dfs['ingresos'], "Ingresos Promedio", "Soles", ".0f"),
1412
+ crear_grafico_lineas(dfs['informal'], "Tasa de Informalidad", "%", ".1f"),
1413
+ crear_grafico_lineas(dfs['actividad'], "Tasa de Actividad", "%", ".1f"),
1414
+ crear_radar_plot(dfs),
1415
+ crear_grafico_lineas(dfs['ingresos'].assign(
1416
+ Brecha=lambda x: (x['Hombres'] - x['Mujeres']) / x['Hombres'].replace(0, np.nan) * 100
1417
+ ), "Brecha Salarial", "%", ".1f")
1418
  ]
1419
+
1420
+ @app.click(inputs=None, outputs=[global_desempleo, global_ingresos, global_brecha])
1421
+ def actualizar_analisis_global():
1422
+ return generar_analisis_global()
 
 
 
1423
 
1424
  app.launch(debug=True)