ConectaODSco commited on
Commit
b312763
·
verified ·
1 Parent(s): 6d6337d

Update src/visualization/visualizaciones_ods.py

Browse files
src/visualization/visualizaciones_ods.py CHANGED
@@ -1073,63 +1073,204 @@ def viz_22_composicion_score_mixto(w_sim=0.5, w_rank=0.3, w_freq=0.2):
1073
 
1074
  return fig
1075
 
1076
- def tab_viz23(df_texto, columna, max_palabras=100):
1077
- """Visualización 23: Nube de Palabras - Paloma de Paz"""
1078
- # if not DATOS_RELACIONES_CARGADOS:
1079
- # return None, ""
1080
-
1081
- # try:
1082
- # # Seleccionar columna con más texto
1083
- # # Probar primero con descripciones de ODS, luego metas, luego indicadores
1084
- # if 'OBJETIVO' in df_ods_rel.columns and df_ods_rel['OBJETIVO'].notna().sum() > 0:
1085
- # df_texto = df_ods_rel
1086
- # columna = 'OBJETIVO'
1087
- # tipo = 'Objetivos (ODS)'
1088
- # elif 'META' in df_metas_rel.columns and df_metas_rel['META'].notna().sum() > 0:
1089
- # df_texto = df_metas_rel
1090
- # columna = 'META'
1091
- # tipo = 'Metas'
1092
- # elif 'INDICADOR' in df_indicadores_rel.columns and df_indicadores_rel['INDICADOR'].notna().sum() > 0:
1093
- # df_texto = df_indicadores_rel
1094
- # columna = 'INDICADOR'
1095
- # tipo = 'Indicadores'
1096
- # else:
1097
- # return None, "⚠️ No se encontraron columnas de texto para analizar."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1098
 
 
 
 
 
 
 
 
 
 
 
1099
  # Generar nube de palabras
1100
- # filepath = viz_23_nube_palabras_paloma(df_texto, columna, max_palabras=100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1101
 
1102
- # Si falla, intentar versión simple
1103
- # if not os.path.exists(filepath):
1104
- filepath = viz_23_nube_palabras_simple(df_texto, columna, max_palabras=100)
1105
 
1106
- explicacion = f"""
1107
- ## 🕊️ Nube de Palabras - Paloma de Paz
 
 
 
1108
 
1109
- ### ¿Qué muestra?
1110
- Las **palabras más frecuentes** encontradas en el documento
 
 
 
 
 
 
 
 
 
 
1111
 
1112
- ### Interpretación:
1113
- - **Tamaño grande**: Palabras que aparecen con más frecuencia
1114
- - **Tamaño pequeño**: Palabras menos frecuentes
1115
- - **Posición**: Aleatoria dentro de la forma de paloma
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1116
 
1117
- ### 🔍 Análisis:
1118
- - Total de registros analizados: {len(df_texto)}
1119
- - Palabras únicas mostradas: ~100
1120
- - Stopwords excluidas: sí (artículos, preposiciones, etc.)
1121
 
1122
- ### 💡 Uso:
1123
- Identifica rápidamente los **temas centrales** y **conceptos clave**
1124
- que dominan el conjunto de textos analizados.
 
 
1125
 
1126
- ### 🎨 Simbología:
1127
- La paloma de paz representa el espíritu de los Objetivos de Desarrollo
1128
- Sostenible: paz, bienestar y desarrollo para todos.
1129
- """
1130
 
1131
- return filepath, explicacion
1132
-
1133
  def viz_24_header_conecta_ods(logo_path=None):
1134
  """
1135
  Visualización 24: Header ConectaODS
 
1073
 
1074
  return fig
1075
 
1076
+ def viz_23_nube_palabras_paloma(df, columna_texto, max_palabras=100):
1077
+ """
1078
+ Visualización 23: Nube de Palabras en Forma de Paloma de Paz
1079
+
1080
+ Genera una nube de palabras con la forma de una paloma
1081
+ mostrando las palabras más frecuentes de una columna de texto
1082
+
1083
+ Args:
1084
+ df: DataFrame con los datos
1085
+ columna_texto: Nombre de la columna con texto
1086
+ max_palabras: Máximo de palabras a mostrar
1087
+ """
1088
+ from wordcloud import WordCloud
1089
+ import matplotlib.pyplot as plt
1090
+ import numpy as np
1091
+ from PIL import Image
1092
+ import os
1093
+ import tempfile
1094
+
1095
+ # Combinar todo el texto de la columna
1096
+ texto_completo = ' '.join(df[columna_texto].dropna().astype(str))
1097
+
1098
+ # Palabras a excluir (stopwords en español)
1099
+ stopwords_es = {
1100
+ 'el', 'la', 'de', 'que', 'y', 'a', 'en', 'un', 'ser', 'se', 'no', 'haber',
1101
+ 'por', 'con', 'su', 'para', 'como', 'estar', 'tener', 'le', 'lo', 'todo',
1102
+ 'pero', 'más', 'hacer', 'o', 'poder', 'decir', 'este', 'ir', 'otro', 'ese',
1103
+ 'la', 'si', 'me', 'ya', 'ver', 'porque', 'dar', 'cuando', 'él', 'muy',
1104
+ 'sin', 'vez', 'mucho', 'saber', 'qué', 'sobre', 'mi', 'alguno', 'mismo',
1105
+ 'yo', 'también', 'hasta', 'año', 'dos', 'querer', 'entre', 'así', 'primero',
1106
+ 'desde', 'grande', 'eso', 'ni', 'nos', 'llegar', 'pasar', 'tiempo', 'ella',
1107
+ 'del', 'al', 'los', 'las', 'una', 'unos', 'unas', 'ante', 'bajo', 'cabe',
1108
+ 'donde', 'durante', 'mediante', 'salvo', 'según', 'excepto', 'hacia', 'mediante'
1109
+ }
1110
+
1111
+ # Crear máscara en forma de paloma de paz
1112
+ # Imagen SVG de paloma simplificada como array numpy
1113
+ def crear_mascara_paloma(width=800, height=600):
1114
+ """Crea una máscara en forma de paloma de paz"""
1115
+ from PIL import Image, ImageDraw
1116
+
1117
+ # Crear imagen en blanco
1118
+ img = Image.new('RGB', (width, height), 'white')
1119
+ draw = ImageDraw.Draw(img)
1120
+
1121
+ # Coordenadas de la paloma (simplificada)
1122
+ # Cuerpo
1123
+ cuerpo = [(width//2, height//2 + 50),
1124
+ (width//2 + 100, height//2 + 80),
1125
+ (width//2 + 80, height//2 + 120),
1126
+ (width//2 - 20, height//2 + 100)]
1127
+
1128
+ # Cabeza
1129
+ cabeza_center = (width//2 - 30, height//2 - 20)
1130
+ cabeza_radius = 40
1131
+
1132
+ # Ala izquierda (extendida)
1133
+ ala_izq = [(width//2, height//2 + 50),
1134
+ (width//2 - 150, height//2 - 80),
1135
+ (width//2 - 200, height//2 - 100),
1136
+ (width//2 - 180, height//2),
1137
+ (width//2 - 100, height//2 + 20)]
1138
+
1139
+ # Ala derecha (extendida)
1140
+ ala_der = [(width//2, height//2 + 50),
1141
+ (width//2 + 150, height//2 - 50),
1142
+ (width//2 + 220, height//2 - 80),
1143
+ (width//2 + 200, height//2 + 10),
1144
+ (width//2 + 120, height//2 + 30)]
1145
+
1146
+ # Cola
1147
+ cola = [(width//2 + 80, height//2 + 120),
1148
+ (width//2 + 120, height//2 + 180),
1149
+ (width//2 + 100, height//2 + 200),
1150
+ (width//2 + 60, height//2 + 170)]
1151
+
1152
+ # Rama de olivo (en el pico)
1153
+ rama = [(width//2 - 70, height//2 - 10),
1154
+ (width//2 - 120, height//2 - 30),
1155
+ (width//2 - 140, height//2 - 25)]
1156
+
1157
+ # Dibujar formas en negro (área donde irán las palabras)
1158
+ draw.ellipse([cabeza_center[0]-cabeza_radius, cabeza_center[1]-cabeza_radius,
1159
+ cabeza_center[0]+cabeza_radius, cabeza_center[1]+cabeza_radius],
1160
+ fill='black')
1161
+ draw.polygon(cuerpo, fill='black')
1162
+ draw.polygon(ala_izq, fill='black')
1163
+ draw.polygon(ala_der, fill='black')
1164
+ draw.polygon(cola, fill='black')
1165
+ draw.line(rama, fill='black', width=8)
1166
 
1167
+ # Ojo (punto blanco)
1168
+ draw.ellipse([cabeza_center[0]-5, cabeza_center[1]-5,
1169
+ cabeza_center[0]+5, cabeza_center[1]+5],
1170
+ fill='white')
1171
+
1172
+ return np.array(img)
1173
+
1174
+ # Crear máscara
1175
+ mascara_paloma = crear_mascara_paloma()
1176
+
1177
  # Generar nube de palabras
1178
+ wordcloud = WordCloud(
1179
+ width=800,
1180
+ height=600,
1181
+ background_color='white',
1182
+ stopwords=stopwords_es,
1183
+ max_words=max_palabras,
1184
+ mask=mascara_paloma,
1185
+ contour_width=2,
1186
+ contour_color='#2E5090',
1187
+ colormap='Blues',
1188
+ relative_scaling=0.5,
1189
+ min_font_size=8
1190
+ ).generate(texto_completo)
1191
+
1192
+ # Crear figura
1193
+ fig, ax = plt.subplots(figsize=(12, 9), facecolor='white')
1194
+ ax.imshow(wordcloud, interpolation='bilinear')
1195
+ ax.axis('off')
1196
+ ax.set_title('Nube de Palabras - Paloma de Paz\nPalabras más frecuentes en análisis ODS',
1197
+ fontsize=18, color='#2E5090', fontweight='bold', pad=20)
1198
 
1199
+ plt.tight_layout()
 
 
1200
 
1201
+ # Guardar en archivo temporal
1202
+ temp_dir = tempfile.gettempdir()
1203
+ filepath = os.path.join(temp_dir, 'nube_palabras_paloma.png')
1204
+ fig.savefig(filepath, format='png', dpi=150, bbox_inches='tight', facecolor='white')
1205
+ plt.close(fig)
1206
 
1207
+ return filepath
1208
+
1209
+
1210
+ def viz_23_nube_palabras_simple(df, columna_texto, max_palabras=100, forma='paloma'):
1211
+ """
1212
+ Versión alternativa con forma circular/elíptica
1213
+ (más simple, no requiere crear máscara compleja)
1214
+ """
1215
+ from wordcloud import WordCloud
1216
+ import matplotlib.pyplot as plt
1217
+ import tempfile
1218
+ import os
1219
 
1220
+ # Combinar texto
1221
+ texto_completo = ' '.join(df[columna_texto].dropna().astype(str))
1222
+
1223
+ # Stopwords español
1224
+ stopwords_es = {
1225
+ 'el', 'la', 'de', 'que', 'y', 'a', 'en', 'un', 'ser', 'se', 'no', 'haber',
1226
+ 'por', 'con', 'su', 'para', 'como', 'estar', 'tener', 'le', 'lo', 'todo',
1227
+ 'pero', 'más', 'hacer', 'o', 'poder', 'decir', 'este', 'ir', 'otro', 'ese',
1228
+ 'si', 'me', 'ya', 'ver', 'porque', 'dar', 'cuando', 'muy', 'sin', 'vez',
1229
+ 'mucho', 'saber', 'sobre', 'mi', 'también', 'hasta', 'año', 'dos', 'entre',
1230
+ 'del', 'al', 'los', 'las', 'una', 'unos', 'unas', 'donde', 'cuando'
1231
+ }
1232
+
1233
+ # Generar nube con forma elíptica (simula ala de paloma)
1234
+ wordcloud = WordCloud(
1235
+ width=1000,
1236
+ height=600,
1237
+ background_color='white',
1238
+ stopwords=stopwords_es,
1239
+ max_words=max_palabras,
1240
+ colormap='Blues',
1241
+ relative_scaling=0.5,
1242
+ min_font_size=10,
1243
+ prefer_horizontal=0.7,
1244
+ collocations=False
1245
+ ).generate(texto_completo)
1246
+
1247
+ # Crear figura con diseño de paloma sugerido
1248
+ fig, ax = plt.subplots(figsize=(14, 8), facecolor='white')
1249
+
1250
+ ax.imshow(wordcloud, interpolation='bilinear')
1251
+ ax.axis('off')
1252
+
1253
+ # Título decorativo
1254
+ ax.text(0.5, 0.98, '🕊️ Nube de Palabras - Análisis ODS',
1255
+ transform=ax.transAxes,
1256
+ fontsize=20, color='#2E5090', fontweight='bold',
1257
+ ha='center', va='top')
1258
+
1259
+ ax.text(0.5, 0.02, 'Palabras más frecuentes en descripciones de ODS',
1260
+ transform=ax.transAxes,
1261
+ fontsize=12, color='#666',
1262
+ ha='center', va='bottom')
1263
 
1264
+ plt.tight_layout()
 
 
 
1265
 
1266
+ # Guardar
1267
+ temp_dir = tempfile.gettempdir()
1268
+ filepath = os.path.join(temp_dir, 'nube_palabras_ods.png')
1269
+ fig.savefig(filepath, format='png', dpi=150, bbox_inches='tight', facecolor='white')
1270
+ plt.close(fig)
1271
 
1272
+ return filepath
 
 
 
1273
 
 
 
1274
  def viz_24_header_conecta_ods(logo_path=None):
1275
  """
1276
  Visualización 24: Header ConectaODS