Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1175,7 +1175,15 @@ def normalizar_nombres_ciudades(nombre):
|
|
1175 |
'Juliana': 'Juliaca',
|
1176 |
'Chimbote': 'Chimbote',
|
1177 |
'Arequipa': 'Arequipa',
|
1178 |
-
'Trujillo': 'Trujillo'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1179 |
}
|
1180 |
return correcciones.get(nombre, nombre)
|
1181 |
|
@@ -1195,10 +1203,7 @@ def crear_grafico_lineas(df, titulo, eje_y, formato=None):
|
|
1195 |
y=df[columna],
|
1196 |
name=columna,
|
1197 |
mode='lines+markers',
|
1198 |
-
line=dict(
|
1199 |
-
color=COLORES.get(columna, '#000000'),
|
1200 |
-
width=3
|
1201 |
-
),
|
1202 |
marker=dict(size=8),
|
1203 |
hovertemplate=f'<b>{columna}</b>: %{{y:{formato or ".2f"}}}<extra></extra>'
|
1204 |
))
|
@@ -1209,13 +1214,7 @@ def crear_grafico_lineas(df, titulo, eje_y, formato=None):
|
|
1209 |
xaxis_title='Periodo',
|
1210 |
hovermode='x unified',
|
1211 |
template='plotly_white',
|
1212 |
-
legend=dict(
|
1213 |
-
orientation="h",
|
1214 |
-
yanchor="bottom",
|
1215 |
-
y=1.02,
|
1216 |
-
xanchor="right",
|
1217 |
-
x=1
|
1218 |
-
),
|
1219 |
height=500
|
1220 |
)
|
1221 |
|
@@ -1224,53 +1223,81 @@ def crear_grafico_lineas(df, titulo, eje_y, formato=None):
|
|
1224 |
|
1225 |
return fig
|
1226 |
|
1227 |
-
def
|
1228 |
-
|
1229 |
-
|
1230 |
-
try:
|
1231 |
-
metricas['Desempleo'] = dfs['desempleo']['Total'].iloc[-1]
|
1232 |
-
metricas['Informalidad'] = dfs['informal']['Total'].iloc[-1]
|
1233 |
-
metricas['Actividad'] = dfs['actividad']['Total'].iloc[-1]
|
1234 |
-
metricas['Brecha Salarial'] = ((dfs['ingresos']['Hombres'].iloc[-1] -
|
1235 |
-
dfs['ingresos']['Mujeres'].iloc[-1]) /
|
1236 |
-
dfs['ingresos']['Hombres'].iloc[-1]) * 100
|
1237 |
-
except (KeyError, IndexError, ZeroDivisionError) as e:
|
1238 |
-
print(f"Error al generar radar plot: {str(e)}")
|
1239 |
-
return go.Figure()
|
1240 |
|
1241 |
-
|
1242 |
-
|
|
|
1243 |
|
1244 |
-
|
|
|
|
|
|
|
1245 |
|
1246 |
-
fig.
|
1247 |
-
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
1251 |
-
|
1252 |
-
|
1253 |
-
|
|
|
1254 |
|
1255 |
fig.update_layout(
|
1256 |
-
|
1257 |
-
|
1258 |
-
|
1259 |
-
|
1260 |
-
|
1261 |
-
|
1262 |
-
|
1263 |
)
|
1264 |
|
|
|
1265 |
return fig
|
1266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1267 |
def analisis_comparativo_desempleo():
|
1268 |
datos = []
|
1269 |
-
|
1270 |
for ciudad, data in cities_data.items():
|
1271 |
nombre = normalizar_nombres_ciudades(ciudad)
|
1272 |
-
df = procesar_dataframe(data['desempleo_trimestral'],
|
1273 |
-
["Trimestre", "Total", "Hombres", "Mujeres"])
|
1274 |
if not df.empty:
|
1275 |
ultimo_valor = df['Total'].iloc[-1]
|
1276 |
datos.append({'Ciudad': nombre, 'Desempleo': ultimo_valor})
|
@@ -1279,66 +1306,57 @@ def analisis_comparativo_desempleo():
|
|
1279 |
return go.Figure()
|
1280 |
|
1281 |
df_comparativo = pd.DataFrame(datos).sort_values('Desempleo', ascending=False)
|
1282 |
-
|
1283 |
fig = px.bar(df_comparativo,
|
1284 |
x='Ciudad',
|
1285 |
y='Desempleo',
|
1286 |
color='Desempleo',
|
1287 |
color_continuous_scale='Bluered',
|
1288 |
text_auto='.1f%',
|
1289 |
-
title='Comparaci贸n de Tasa de Desempleo entre Ciudades'
|
|
|
1290 |
|
1291 |
fig.update_layout(
|
1292 |
xaxis_title='',
|
1293 |
yaxis_title='Tasa de Desempleo (%)',
|
1294 |
coloraxis_showscale=False,
|
1295 |
xaxis={'categoryorder':'total descending'},
|
1296 |
-
|
1297 |
)
|
1298 |
-
|
1299 |
return fig
|
1300 |
|
1301 |
def generar_analisis_global():
|
1302 |
figs = []
|
1303 |
|
1304 |
-
#
|
1305 |
figs.append(analisis_comparativo_desempleo())
|
1306 |
|
1307 |
-
#
|
1308 |
datos_ingresos = []
|
1309 |
for ciudad, data in cities_data.items():
|
1310 |
nombre = normalizar_nombres_ciudades(ciudad)
|
1311 |
-
df = procesar_dataframe(data['ingresos_periodo'],
|
1312 |
-
["Periodo", "Total", "Hombres", "Mujeres"])
|
1313 |
if not df.empty:
|
1314 |
df['Ciudad'] = nombre
|
1315 |
datos_ingresos.append(df)
|
1316 |
|
1317 |
if datos_ingresos:
|
1318 |
df_ingresos = pd.concat(datos_ingresos)
|
1319 |
-
fig_ingresos =
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
|
1325 |
-
height=600)
|
1326 |
-
fig_ingresos.update_layout(
|
1327 |
-
xaxis_title='Periodo',
|
1328 |
-
yaxis_title='Ingresos (Soles)',
|
1329 |
-
legend=dict(title='Ciudades'),
|
1330 |
-
margin=dict(l=50, r=50, t=80, b=50)
|
1331 |
)
|
1332 |
figs.append(fig_ingresos)
|
1333 |
else:
|
1334 |
figs.append(go.Figure())
|
1335 |
|
1336 |
-
#
|
1337 |
datos_brecha = []
|
1338 |
for ciudad, data in cities_data.items():
|
1339 |
nombre = normalizar_nombres_ciudades(ciudad)
|
1340 |
-
df = procesar_dataframe(data['ingresos_periodo'],
|
1341 |
-
["Periodo", "Total", "Hombres", "Mujeres"])
|
1342 |
if not df.empty:
|
1343 |
df['Brecha'] = (df['Hombres'] - df['Mujeres']) / df['Hombres'].replace(0, np.nan) * 100
|
1344 |
df['Ciudad'] = nombre
|
@@ -1346,23 +1364,12 @@ def generar_analisis_global():
|
|
1346 |
|
1347 |
if datos_brecha:
|
1348 |
df_brecha = pd.concat(datos_brecha)
|
1349 |
-
fig_brecha =
|
1350 |
-
|
1351 |
-
|
1352 |
-
|
1353 |
-
|
1354 |
-
|
1355 |
-
height=600,
|
1356 |
-
color_continuous_scale='Viridis')
|
1357 |
-
|
1358 |
-
fig_brecha.update_layout(
|
1359 |
-
xaxis_title='Ciudad',
|
1360 |
-
yaxis_title='Brecha Salarial (%)',
|
1361 |
-
coloraxis_showscale=False,
|
1362 |
-
xaxis={'categoryorder':'total descending'},
|
1363 |
-
margin=dict(l=50, r=50, t=80, b=150),
|
1364 |
-
uniformtext_minsize=8,
|
1365 |
-
uniformtext_mode='hide'
|
1366 |
)
|
1367 |
figs.append(fig_brecha)
|
1368 |
else:
|
@@ -1394,6 +1401,7 @@ def actualizar_graficos(ciudad):
|
|
1394 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), css="""
|
1395 |
.gradio-container {background-color: #f8f9fa}
|
1396 |
.plot-container {margin: 20px 0}
|
|
|
1397 |
""") as app:
|
1398 |
|
1399 |
gr.Markdown("# 馃搳 Dashboard Anal铆tico del Mercado Laboral Peruano")
|
@@ -1407,34 +1415,36 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), css="""
|
|
1407 |
)
|
1408 |
|
1409 |
with gr.Tab("An谩lisis por Ciudad"):
|
|
|
1410 |
with gr.Row():
|
1411 |
-
|
1412 |
-
|
1413 |
-
|
1414 |
-
with gr.Row():
|
1415 |
-
informalidad_plot = Plot(label="Tasa de Informalidad")
|
1416 |
-
with gr.Row():
|
1417 |
-
actividad_plot = Plot(label="Tasa de Actividad")
|
1418 |
-
with gr.Row():
|
1419 |
-
radar_plot = Plot(label="Radar de Indicadores")
|
1420 |
with gr.Row():
|
1421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1422 |
|
1423 |
with gr.Tab("An谩lisis Comparativo"):
|
1424 |
with gr.Row():
|
1425 |
global_desempleo = Plot(label="Ranking de Desempleo")
|
1426 |
with gr.Row():
|
1427 |
-
global_ingresos = Plot(label="Evoluci贸n de Ingresos")
|
1428 |
with gr.Row():
|
1429 |
-
global_brecha = Plot(label="
|
1430 |
with gr.Row():
|
1431 |
-
global_btn = gr.Button("Actualizar
|
1432 |
|
1433 |
-
# Eventos
|
1434 |
ciudad.change(
|
1435 |
fn=actualizar_graficos,
|
1436 |
inputs=ciudad,
|
1437 |
-
outputs=[desempleo_plot, ingresos_plot, informalidad_plot, actividad_plot,
|
1438 |
)
|
1439 |
|
1440 |
global_btn.click(
|
|
|
1175 |
'Juliana': 'Juliaca',
|
1176 |
'Chimbote': 'Chimbote',
|
1177 |
'Arequipa': 'Arequipa',
|
1178 |
+
'Trujillo': 'Trujillo',
|
1179 |
+
'Cervo de Pasco': 'Cerro de Pasco',
|
1180 |
+
'Lima Metropolitana y la Provincia Constitucional del Calleo': 'Lima-Callao',
|
1181 |
+
'Moyaegua': 'Moquegua',
|
1182 |
+
'Mayobamba': 'Moyobamba',
|
1183 |
+
'Hueva': 'Huancavelica',
|
1184 |
+
'Pluto J煤nior': 'Piura',
|
1185 |
+
'Guido Mianzano': 'Cajamarca',
|
1186 |
+
'Guyo Morales': 'Ayacucho'
|
1187 |
}
|
1188 |
return correcciones.get(nombre, nombre)
|
1189 |
|
|
|
1203 |
y=df[columna],
|
1204 |
name=columna,
|
1205 |
mode='lines+markers',
|
1206 |
+
line=dict(color=COLORES.get(columna, '#000000'), width=3),
|
|
|
|
|
|
|
1207 |
marker=dict(size=8),
|
1208 |
hovertemplate=f'<b>{columna}</b>: %{{y:{formato or ".2f"}}}<extra></extra>'
|
1209 |
))
|
|
|
1214 |
xaxis_title='Periodo',
|
1215 |
hovermode='x unified',
|
1216 |
template='plotly_white',
|
1217 |
+
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
|
|
|
|
|
|
|
|
|
|
|
|
|
1218 |
height=500
|
1219 |
)
|
1220 |
|
|
|
1223 |
|
1224 |
return fig
|
1225 |
|
1226 |
+
def crear_grafico_barras_anual(df, titulo, eje_y, columna_metricas, palette=px.colors.sequential.Viridis):
|
1227 |
+
# Ordenar datos de mayor a menor
|
1228 |
+
df = df.sort_values(by=columna_metricas, ascending=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1229 |
|
1230 |
+
# Resto de la funci贸n se mantiene igual
|
1231 |
+
df['A帽o'] = df['Periodo'].str.split('-').str[0]
|
1232 |
+
df = df.groupby(['Ciudad', 'A帽o'], as_index=False)[columna_metricas].mean()
|
1233 |
|
1234 |
+
# Crear categor铆as ordenadas
|
1235 |
+
orden_ciudades = df.groupby('Ciudad')[columna_metricas].mean().sort_values(ascending=False).index
|
1236 |
+
df['Ciudad'] = pd.Categorical(df['Ciudad'], categories=orden_ciudades, ordered=True)
|
1237 |
+
df = df.sort_values('Ciudad')
|
1238 |
|
1239 |
+
fig = px.bar(df,
|
1240 |
+
x='Ciudad',
|
1241 |
+
y=columna_metricas,
|
1242 |
+
color='A帽o',
|
1243 |
+
barmode='group',
|
1244 |
+
title=f'<b>{titulo}</b>',
|
1245 |
+
labels={columna_metricas: eje_y},
|
1246 |
+
color_discrete_sequence=palette,
|
1247 |
+
height=600)
|
1248 |
|
1249 |
fig.update_layout(
|
1250 |
+
xaxis_title='Ciudad',
|
1251 |
+
yaxis_title=eje_y,
|
1252 |
+
legend_title='A帽o',
|
1253 |
+
xaxis={'categoryorder': 'total descending'},
|
1254 |
+
uniformtext_minsize=8,
|
1255 |
+
margin=dict(b=150),
|
1256 |
+
hovermode='x unified'
|
1257 |
)
|
1258 |
|
1259 |
+
fig.update_xaxes(tickangle=45)
|
1260 |
return fig
|
1261 |
|
1262 |
+
def crear_radar_plot(dfs):
|
1263 |
+
try:
|
1264 |
+
metricas = {
|
1265 |
+
'Desempleo': dfs['desempleo']['Total'].iloc[-1],
|
1266 |
+
'Informalidad': dfs['informal']['Total'].iloc[-1],
|
1267 |
+
'Actividad': dfs['actividad']['Total'].iloc[-1],
|
1268 |
+
'Brecha Salarial': ((dfs['ingresos']['Hombres'].iloc[-1] - dfs['ingresos']['Mujeres'].iloc[-1]) /
|
1269 |
+
dfs['ingresos']['Hombres'].iloc[-1]) * 100
|
1270 |
+
}
|
1271 |
+
|
1272 |
+
fig = go.Figure()
|
1273 |
+
fig.add_trace(go.Scatterpolar(
|
1274 |
+
r=list(metricas.values()) + [list(metricas.values())[0]],
|
1275 |
+
theta=list(metricas.keys()) + [list(metricas.keys())[0]],
|
1276 |
+
fill='toself',
|
1277 |
+
fillcolor='rgba(142, 68, 173, 0.2)',
|
1278 |
+
line=dict(color=COLORES['Brecha'], width=2)
|
1279 |
+
))
|
1280 |
+
|
1281 |
+
fig.update_layout(
|
1282 |
+
polar=dict(
|
1283 |
+
radialaxis=dict(visible=True, range=[0, 100]),
|
1284 |
+
angularaxis=dict(rotation=90, direction='clockwise')
|
1285 |
+
),
|
1286 |
+
title=dict(text='Radar de Indicadores Laborales', x=0.5, font=dict(size=20)),
|
1287 |
+
showlegend=False,
|
1288 |
+
height=500
|
1289 |
+
)
|
1290 |
+
return fig
|
1291 |
+
|
1292 |
+
except Exception as e:
|
1293 |
+
print(f"Error en radar plot: {str(e)}")
|
1294 |
+
return go.Figure()
|
1295 |
+
|
1296 |
def analisis_comparativo_desempleo():
|
1297 |
datos = []
|
|
|
1298 |
for ciudad, data in cities_data.items():
|
1299 |
nombre = normalizar_nombres_ciudades(ciudad)
|
1300 |
+
df = procesar_dataframe(data['desempleo_trimestral'], ["Trimestre", "Total", "Hombres", "Mujeres"])
|
|
|
1301 |
if not df.empty:
|
1302 |
ultimo_valor = df['Total'].iloc[-1]
|
1303 |
datos.append({'Ciudad': nombre, 'Desempleo': ultimo_valor})
|
|
|
1306 |
return go.Figure()
|
1307 |
|
1308 |
df_comparativo = pd.DataFrame(datos).sort_values('Desempleo', ascending=False)
|
|
|
1309 |
fig = px.bar(df_comparativo,
|
1310 |
x='Ciudad',
|
1311 |
y='Desempleo',
|
1312 |
color='Desempleo',
|
1313 |
color_continuous_scale='Bluered',
|
1314 |
text_auto='.1f%',
|
1315 |
+
title='<b>Comparaci贸n de Tasa de Desempleo entre Ciudades</b>',
|
1316 |
+
height=600)
|
1317 |
|
1318 |
fig.update_layout(
|
1319 |
xaxis_title='',
|
1320 |
yaxis_title='Tasa de Desempleo (%)',
|
1321 |
coloraxis_showscale=False,
|
1322 |
xaxis={'categoryorder':'total descending'},
|
1323 |
+
margin=dict(b=150)
|
1324 |
)
|
|
|
1325 |
return fig
|
1326 |
|
1327 |
def generar_analisis_global():
|
1328 |
figs = []
|
1329 |
|
1330 |
+
# 1. Ranking de Desempleo
|
1331 |
figs.append(analisis_comparativo_desempleo())
|
1332 |
|
1333 |
+
# 2. Evoluci贸n de Ingresos
|
1334 |
datos_ingresos = []
|
1335 |
for ciudad, data in cities_data.items():
|
1336 |
nombre = normalizar_nombres_ciudades(ciudad)
|
1337 |
+
df = procesar_dataframe(data['ingresos_periodo'], ["Periodo", "Total", "Hombres", "Mujeres"])
|
|
|
1338 |
if not df.empty:
|
1339 |
df['Ciudad'] = nombre
|
1340 |
datos_ingresos.append(df)
|
1341 |
|
1342 |
if datos_ingresos:
|
1343 |
df_ingresos = pd.concat(datos_ingresos)
|
1344 |
+
fig_ingresos = crear_grafico_barras_anual(
|
1345 |
+
df_ingresos,
|
1346 |
+
'Evoluci贸n Anual de Ingresos Promedio por Ciudad',
|
1347 |
+
'Ingresos (Soles)',
|
1348 |
+
'Total',
|
1349 |
+
px.colors.sequential.Viridis
|
|
|
|
|
|
|
|
|
|
|
|
|
1350 |
)
|
1351 |
figs.append(fig_ingresos)
|
1352 |
else:
|
1353 |
figs.append(go.Figure())
|
1354 |
|
1355 |
+
# 3. Evoluci贸n de Brecha Salarial
|
1356 |
datos_brecha = []
|
1357 |
for ciudad, data in cities_data.items():
|
1358 |
nombre = normalizar_nombres_ciudades(ciudad)
|
1359 |
+
df = procesar_dataframe(data['ingresos_periodo'], ["Periodo", "Total", "Hombres", "Mujeres"])
|
|
|
1360 |
if not df.empty:
|
1361 |
df['Brecha'] = (df['Hombres'] - df['Mujeres']) / df['Hombres'].replace(0, np.nan) * 100
|
1362 |
df['Ciudad'] = nombre
|
|
|
1364 |
|
1365 |
if datos_brecha:
|
1366 |
df_brecha = pd.concat(datos_brecha)
|
1367 |
+
fig_brecha = crear_grafico_barras_anual(
|
1368 |
+
df_brecha,
|
1369 |
+
'Evoluci贸n Anual de la Brecha Salarial por Ciudad',
|
1370 |
+
'Brecha Salarial (%)',
|
1371 |
+
'Brecha',
|
1372 |
+
px.colors.diverging.RdYlGn
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1373 |
)
|
1374 |
figs.append(fig_brecha)
|
1375 |
else:
|
|
|
1401 |
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), css="""
|
1402 |
.gradio-container {background-color: #f8f9fa}
|
1403 |
.plot-container {margin: 20px 0}
|
1404 |
+
.column-container {gap: 15px}
|
1405 |
""") as app:
|
1406 |
|
1407 |
gr.Markdown("# 馃搳 Dashboard Anal铆tico del Mercado Laboral Peruano")
|
|
|
1415 |
)
|
1416 |
|
1417 |
with gr.Tab("An谩lisis por Ciudad"):
|
1418 |
+
# Primera fila: Radar plot
|
1419 |
with gr.Row():
|
1420 |
+
radar_plot = Plot(label="Radar de Indicadores Laborales")
|
1421 |
+
|
1422 |
+
# Segunda fila: 2 columnas para los dem谩s gr谩ficos
|
|
|
|
|
|
|
|
|
|
|
|
|
1423 |
with gr.Row():
|
1424 |
+
with gr.Column(elem_classes="column-container"):
|
1425 |
+
desempleo_plot = Plot(label="Tasa de Desempleo")
|
1426 |
+
ingresos_plot = Plot(label="Ingresos Promedio")
|
1427 |
+
informalidad_plot = Plot(label="Tasa de Informalidad")
|
1428 |
+
|
1429 |
+
with gr.Column(elem_classes="column-container"):
|
1430 |
+
actividad_plot = Plot(label="Tasa de Actividad")
|
1431 |
+
brecha_plot = Plot(label="Brecha Salarial")
|
1432 |
|
1433 |
with gr.Tab("An谩lisis Comparativo"):
|
1434 |
with gr.Row():
|
1435 |
global_desempleo = Plot(label="Ranking de Desempleo")
|
1436 |
with gr.Row():
|
1437 |
+
global_ingresos = Plot(label="Evoluci贸n Anual de Ingresos")
|
1438 |
with gr.Row():
|
1439 |
+
global_brecha = Plot(label="Evoluci贸n Anual de Brecha Salarial")
|
1440 |
with gr.Row():
|
1441 |
+
global_btn = gr.Button("Actualizar An谩lisis", variant="primary")
|
1442 |
|
1443 |
+
# Eventos (se mantienen igual)
|
1444 |
ciudad.change(
|
1445 |
fn=actualizar_graficos,
|
1446 |
inputs=ciudad,
|
1447 |
+
outputs=[radar_plot, desempleo_plot, ingresos_plot, informalidad_plot, actividad_plot, brecha_plot]
|
1448 |
)
|
1449 |
|
1450 |
global_btn.click(
|