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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +363 -197
app.py CHANGED
@@ -1,11 +1,11 @@
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 datetime import datetime
7
-
8
- #DATA
 
9
 
10
  cities_data = {
11
  'Abancay': {
@@ -1151,14 +1151,17 @@ cities_data = {
1151
  }
1152
 
1153
 
1154
- #DATA
1155
-
 
 
1156
 
1157
  COLORES = {
1158
  'Total': '#2C3E50',
1159
  'Hombres': '#3498DB',
1160
  'Mujeres': '#E74C3C',
1161
- 'Brecha': '#8E44AD'
 
1162
  }
1163
 
1164
  def normalizar_nombres_ciudades(nombre):
@@ -1173,225 +1176,388 @@ def normalizar_nombres_ciudades(nombre):
1173
  'Pura': 'Piura',
1174
  'Posalipa': 'Pucallpa',
1175
  'Tagapito': 'Talara',
1176
- 'Juliana': 'Juliaca',
1177
- 'Chimbote': 'Chimbote',
1178
- 'Arequipa': 'Arequipa',
1179
- 'Trujillo': 'Trujillo'
1180
  }
1181
  return correcciones.get(nombre, nombre)
1182
 
1183
- def procesar_dataframe(df, cols):
1184
- return pd.DataFrame(df, columns=cols).apply(pd.to_numeric, errors='coerce', axis=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1185
 
1186
- def crear_grafico_lineas(df, titulo, eje_y, formato=None):
1187
- fig = go.Figure()
1188
-
1189
- for col in ['Total', 'Hombres', 'Mujeres']:
1190
- if col in df.columns:
1191
- fig.add_trace(go.Scatter(
1192
- x=df['Periodo'],
1193
- y=df[col],
1194
- name=col,
1195
- mode='lines+markers',
1196
- line=dict(color=COLORES[col], width=3),
1197
- marker=dict(size=8),
1198
- hovertemplate=f'<b>{col}</b>: %{{y:{formato or ".2f"}}}<extra></extra>'
1199
- ))
1200
-
1201
- fig.update_layout(
1202
- title=dict(text=titulo, x=0.5, font=dict(size=20)),
1203
- yaxis_title=eje_y,
1204
- xaxis_title='Periodo',
1205
- hovermode='x unified',
1206
- template='plotly_white',
1207
- legend=dict(
1208
- orientation="h",
1209
- yanchor="bottom",
1210
- y=1.02,
1211
- xanchor="right",
1212
- x=1
1213
- )
1214
- )
1215
-
1216
- if formato:
1217
- fig.update_layout(yaxis_tickformat=formato)
1218
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1219
  return fig
1220
 
1221
- def analisis_comparativo_desempleo():
1222
- datos = []
1223
-
1224
- for ciudad, data in cities_data.items():
 
 
 
 
 
 
 
 
 
1225
  nombre = normalizar_nombres_ciudades(ciudad)
1226
- df = pd.DataFrame(data['desempleo_trimestral'],
1227
- columns=["Trimestre", "Total", "Hombres", "Mujeres"])
1228
- ultimo_valor = df['Total'].iloc[-1] if not df.empty else None
1229
- if ultimo_valor:
1230
- datos.append({'Ciudad': nombre, 'Desempleo': ultimo_valor})
1231
 
1232
- df_comparativo = pd.DataFrame(datos).sort_values('Desempleo', ascending=False)
 
 
 
 
 
1233
 
1234
- fig = px.bar(df_comparativo,
1235
- x='Ciudad',
1236
- y='Desempleo',
1237
- color='Desempleo',
1238
- color_continuous_scale='Bluered',
1239
- text_auto='.1f%',
1240
- title='Comparaci贸n de Tasa de Desempleo entre Ciudades')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1241
 
1242
- fig.update_layout(
1243
- xaxis_title='',
1244
- yaxis_title='Tasa de Desempleo (%)',
1245
- coloraxis_showscale=False,
1246
- xaxis={'categoryorder':'total descending'},
1247
- hoverlabel=dict(bgcolor="white", font_size=12),
1248
- height=600
1249
- )
1250
 
1251
- fig.update_traces(textfont_size=12, textangle=0, textposition="outside")
1252
- return fig
 
 
 
 
 
 
 
 
 
 
 
1253
 
1254
- def crear_radar_plot(dfs):
1255
- metricas = {
1256
- 'Desempleo': dfs['desempleo']['Total'].iloc[-1],
1257
- 'Informalidad': dfs['informal']['Total'].iloc[-1],
1258
- 'Brecha Salarial': ((dfs['ingresos']['Hombres'].iloc[-1] - dfs['ingresos']['Mujeres'].iloc[-1]) /
1259
- dfs['ingresos']['Hombres'].iloc[-1]) * 100,
1260
- 'Actividad': dfs['actividad']['Total'].iloc[-1]
1261
- }
1262
-
1263
- fig = go.Figure()
 
 
1264
 
1265
- fig.add_trace(go.Scatterpolar(
1266
- r=list(metricas.values()) + [metricas['Desempleo']],
1267
- theta=list(metricas.keys()) + ['Desempleo'],
1268
- fill='toself',
1269
- fillcolor='rgba(142, 68, 173, 0.2)',
1270
- line=dict(color=COLORES['Brecha'], width=2),
1271
- name='Indicadores'
1272
- ))
1273
 
1274
- fig.update_layout(
1275
- polar=dict(
1276
- radialaxis=dict(
1277
- visible=True,
1278
- range=[0, 100],
1279
- tickfont=dict(size=12),
1280
- tickformat='.0f%'
1281
- ),
1282
- angularaxis=dict(
1283
- rotation=90,
1284
- direction='clockwise',
1285
- tickfont=dict(size=14)
1286
- )
1287
- ),
1288
- title=dict(text='Radar de Indicadores Laborales', x=0.5, font=dict(size=20)),
1289
- showlegend=False,
1290
- height=600
1291
- )
1292
-
1293
- return fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1294
 
1295
- def generar_analisis_global():
1296
  figs = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1297
 
1298
- # Gr谩fico comparativo de desempleo
1299
- figs.append(analisis_comparativo_desempleo())
1300
-
1301
- # Gr谩fico de tendencia de ingresos
1302
- datos_ingresos = []
1303
- for ciudad, data in cities_data.items():
1304
- nombre = normalizar_nombres_ciudades(ciudad)
1305
- df = pd.DataFrame(data['ingresos_periodo'],
1306
- columns=["Periodo", "Total", "Hombres", "Mujeres"])
1307
- df['Ciudad'] = nombre
1308
- datos_ingresos.append(df)
1309
-
1310
- df_ingresos = pd.concat(datos_ingresos)
1311
- fig_ingresos = px.line(df_ingresos,
1312
- x='Periodo',
1313
- y='Total',
1314
- color='Ciudad',
1315
- title='Evoluci贸n de Ingresos por Ciudad',
1316
- markers=True)
1317
- fig_ingresos.update_layout(height=600)
1318
- figs.append(fig_ingresos)
1319
-
1320
- # Gr谩fico de brecha salarial
1321
- datos_brecha = []
1322
- for ciudad, data in cities_data.items():
1323
- nombre = normalizar_nombres_ciudades(ciudad)
1324
- df = pd.DataFrame(data['ingresos_periodo'],
1325
- columns=["Periodo", "Total", "Hombres", "Mujeres"])
1326
- df['Brecha'] = (df['Hombres'] - df['Mujeres']) / df['Hombres'] * 100
1327
- df['Ciudad'] = nombre
1328
- datos_brecha.append(df)
1329
 
1330
- df_brecha = pd.concat(datos_brecha)
1331
- fig_brecha = px.bar(df_brecha,
1332
- x='Periodo',
1333
- y='Brecha',
1334
- color='Ciudad',
1335
- barmode='group',
1336
- title='Evoluci贸n de Brecha Salarial por Ciudad',
1337
- text_auto='.1f%')
1338
- fig_brecha.update_layout(height=600)
 
1339
  figs.append(fig_brecha)
1340
-
1341
  return figs
1342
 
1343
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), css=".gradio-container {background-color: white}") as app:
1344
- gr.Markdown("# 馃搳 Dashboard Interactivo de Mercado Laboral")
1345
-
 
1346
  with gr.Row():
1347
- ciudad = gr.Dropdown(
1348
  list(cities_data.keys()),
1349
- label="Seleccionar Ciudad",
1350
  value="Chimbote",
1351
- interactive=True
1352
  )
1353
-
1354
- with gr.Tab("An谩lisis por Ciudad"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1355
  with gr.Row():
1356
- desempleo_plot = gr.Plotly(label="Tasa de Desempleo")
1357
- ingresos_plot = gr.Plotly(label="Ingresos Promedio")
1358
  with gr.Row():
1359
- informalidad_plot = gr.Plotly(label="Tasa de Informalidad")
1360
- actividad_plot = gr.Plotly(label="Tasa de Actividad")
1361
  with gr.Row():
1362
- radar_plot = gr.Plotly(label="Radar de Indicadores")
1363
- brecha_plot = gr.Plotly(label="Brecha Salarial")
1364
-
1365
- with gr.Tab("An谩lisis Comparativo"):
1366
  with gr.Row():
1367
- global_desempleo = gr.Plotly(label="Comparativa de Desempleo")
 
1368
  with gr.Row():
1369
- global_ingresos = gr.Plotly(label="Evoluci贸n de Ingresos")
1370
- global_brecha = gr.Plotly(label="Evoluci贸n de Brecha Salarial")
1371
-
1372
- @app.change(inputs=ciudad, outputs=[desempleo_plot, ingresos_plot, informalidad_plot, actividad_plot, radar_plot, brecha_plot])
1373
- def actualizar_graficos(ciudad):
1374
- data = cities_data[ciudad]
1375
-
1376
- dfs = {
1377
- 'desempleo': procesar_dataframe(data['desempleo_trimestral'], ["Trimestre", "Total", "Hombres", "Mujeres"]),
1378
- 'ingresos': procesar_dataframe(data['ingresos_periodo'], ["Periodo", "Total", "Hombres", "Mujeres"]),
1379
- 'informal': procesar_dataframe(data['informal_periodo'], ["Periodo", "Total", "Hombres", "Mujeres"]),
1380
- 'actividad': procesar_dataframe(data['actividad_trimestral'], ["Trimestre", "Total", "Hombres", "Mujeres"])
1381
- }
1382
-
1383
- return [
1384
- crear_grafico_lineas(dfs['desempleo'], "Tasa de Desempleo", "Porcentaje (%)", ".1f"),
1385
- crear_grafico_lineas(dfs['ingresos'], "Ingresos Promedio", "Soles", ".0f"),
1386
- crear_grafico_lineas(dfs['informal'], "Tasa de Informalidad", "Porcentaje (%)", ".1f"),
1387
- crear_grafico_lineas(dfs['actividad'], "Tasa de Actividad", "Porcentaje (%)", ".1f"),
1388
- crear_radar_plot(dfs),
1389
- crear_grafico_lineas(dfs['ingresos'].assign(Brecha=lambda x: (x['Hombres'] - x['Mujeres']) / x['Hombres'] * 100),
1390
- "Brecha Salarial de G茅nero", "Porcentaje (%)", ".1f")
 
 
 
 
 
 
 
1391
  ]
1392
-
1393
- @app.click(inputs=None, outputs=[global_desempleo, global_ingresos, global_brecha])
1394
- def actualizar_analisis_global():
1395
- return generar_analisis_global()
 
 
 
1396
 
1397
  app.launch(debug=True)
 
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
  }
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
  '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)