Spaces:
Running
Running
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
|
| 1077 |
-
"""
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
|
| 1082 |
-
|
| 1083 |
-
|
| 1084 |
-
|
| 1085 |
-
|
| 1086 |
-
|
| 1087 |
-
|
| 1088 |
-
|
| 1089 |
-
|
| 1090 |
-
|
| 1091 |
-
|
| 1092 |
-
|
| 1093 |
-
|
| 1094 |
-
|
| 1095 |
-
#
|
| 1096 |
-
|
| 1097 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1098 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1099 |
# Generar nube de palabras
|
| 1100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1101 |
|
| 1102 |
-
|
| 1103 |
-
# if not os.path.exists(filepath):
|
| 1104 |
-
filepath = viz_23_nube_palabras_simple(df_texto, columna, max_palabras=100)
|
| 1105 |
|
| 1106 |
-
|
| 1107 |
-
|
|
|
|
|
|
|
|
|
|
| 1108 |
|
| 1109 |
-
|
| 1110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1111 |
|
| 1112 |
-
#
|
| 1113 |
-
|
| 1114 |
-
|
| 1115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1116 |
|
| 1117 |
-
|
| 1118 |
-
- Total de registros analizados: {len(df_texto)}
|
| 1119 |
-
- Palabras únicas mostradas: ~100
|
| 1120 |
-
- Stopwords excluidas: sí (artículos, preposiciones, etc.)
|
| 1121 |
|
| 1122 |
-
#
|
| 1123 |
-
|
| 1124 |
-
|
|
|
|
|
|
|
| 1125 |
|
| 1126 |
-
|
| 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
|