Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import matplotlib.pyplot as plt | |
| from matplotlib.patches import Circle, Ellipse | |
| from scipy.interpolate import make_interp_spline | |
| import numpy as np | |
| import pandas as pd | |
| # Datos de satisfacción por edad para cada región | |
| region_data = { | |
| "United States": [7.0, 6.5, 6.2, 6.0, 6.3, 6.8, 7.3, 7.8, 8.1], | |
| "United Kingdom": [6.8, 6.3, 6.0, 6.1, 6.4, 6.7, 7.2, 7.6, 8.0], | |
| "Latin America and Caribbean": [6.5, 6.1, 6.0, 6.2, 6.4, 6.6, 6.9, 7.2, 7.5], | |
| "China": [5.5, 5.0, 4.8, 4.7, 5.0, 5.5, 6.0, 6.5, 7.0], | |
| "Germany": [6.8, 6.2, 5.8, 5.7, 6.0, 6.3, 6.6, 6.9, 7.2], | |
| "Russia": [5.8, 5.4, 5.1, 4.8, 4.6, 4.8, 5.0, 5.2, 5.5], | |
| "Mundo": [5.58, 5.47, 5.38, 5.32, 5.26, 5.22, 5.20, 5.22, 5.28, 5.36, 5.45, 5.55, 5.65, 5.75], | |
| "Países más felices": [7.4, 6.9, 6.6, 6.5, 6.6, 6.9, 7.3, 7.8, 8.2], | |
| "Países menos felices": [6.8, 6.3, 6.0, 5.9, 6.0, 6.3, 6.7, 7.2, 7.6], | |
| "Países menos felices (extremo)": [6.0, 5.2, 4.8, 4.5, 4.6, 5.0, 5.6, 6.2, 6.7] | |
| } | |
| # Datos PISA 2022 | |
| pisa_data = { | |
| "Singapore": {"Math": 575, "Reading": 543, "Science": 561}, | |
| "Macao (China)": {"Math": 552, "Reading": 510, "Science": 543}, | |
| "Chinese Taipei": {"Math": None, "Reading": 515, "Science": 537}, | |
| "Hong Kong (China)": {"Math": 540, "Reading": 500, "Science": 520}, | |
| "Japan": {"Math": 536, "Reading": 516, "Science": 547}, | |
| "Korea": {"Math": 527, "Reading": 515, "Science": 528}, | |
| "Estonia": {"Math": 510, "Reading": 511, "Science": 526}, | |
| "Switzerland": {"Math": 508, "Reading": 483, "Science": 503}, | |
| "Canada": {"Math": 497, "Reading": 507, "Science": 515}, | |
| "Netherlands": {"Math": 493, "Reading": 459, "Science": 488}, | |
| "Ireland": {"Math": 492, "Reading": 516, "Science": 504}, | |
| "Belgium": {"Math": 489, "Reading": 479, "Science": 491}, | |
| "Denmark": {"Math": 489, "Reading": 489, "Science": 494}, | |
| "United Kingdom": {"Math": 489, "Reading": 494, "Science": 500}, | |
| "Poland": {"Math": 489, "Reading": 489, "Science": 499}, | |
| "Austria": {"Math": 487, "Reading": 480, "Science": 491}, | |
| "Australia": {"Math": 487, "Reading": 498, "Science": 507}, | |
| "Czech Republic": {"Math": 487, "Reading": 489, "Science": 498}, | |
| "Slovenia": {"Math": 485, "Reading": 469, "Science": 500}, | |
| "Finland": {"Math": 484, "Reading": 490, "Science": 511}, | |
| "Latvia": {"Math": 483, "Reading": 475, "Science": 494}, | |
| "Sweden": {"Math": 482, "Reading": 487, "Science": 494}, | |
| "New Zealand": {"Math": 479, "Reading": 501, "Science": 504}, | |
| "Lithuania": {"Math": 475, "Reading": 472, "Science": 484}, | |
| "Germany": {"Math": 475, "Reading": 480, "Science": 492}, | |
| "France": {"Math": 474, "Reading": 474, "Science": 487}, | |
| "Spain": {"Math": 473, "Reading": 474, "Science": 485}, | |
| "Hungary": {"Math": 473, "Reading": 473, "Science": 486}, | |
| "Portugal": {"Math": 472, "Reading": 477, "Science": 484}, | |
| "Italy": {"Math": 471, "Reading": 482, "Science": 477}, | |
| "Norway": {"Math": 468, "Reading": 477, "Science": 478}, | |
| "United States": {"Math": 465, "Reading": 504, "Science": 499}, | |
| "Slovak Republic": {"Math": 464, "Reading": 447, "Science": 462}, | |
| "Croatia": {"Math": 463, "Reading": 475, "Science": 483}, | |
| "Iceland": {"Math": 459, "Reading": 436, "Science": 447}, | |
| "Israel": {"Math": 458, "Reading": 474, "Science": 465}, | |
| "Turkey": {"Math": 453, "Reading": 456, "Science": 476}, | |
| "Ukraine": {"Math": 441, "Reading": 428, "Science": 450}, | |
| "Serbia": {"Math": 440, "Reading": 440, "Science": 447}, | |
| "United Arab Emirates": {"Math": 431, "Reading": 417, "Science": 432}, | |
| "Greece": {"Math": 430, "Reading": 438, "Science": 441}, | |
| "Romania": {"Math": 428, "Reading": 428, "Science": 428}, | |
| "Kazakhstan": {"Math": 425, "Reading": 386, "Science": 423}, | |
| "Mongolia": {"Math": 425, "Reading": 378, "Science": 412}, | |
| "Bulgaria": {"Math": 417, "Reading": 404, "Science": 421}, | |
| "Moldova": {"Math": 414, "Reading": 411, "Science": 417}, | |
| "Qatar": {"Math": 414, "Reading": 419, "Science": 432}, | |
| "Chile": {"Math": 412, "Reading": 448, "Science": 444}, | |
| "Uruguay": {"Math": 409, "Reading": 430, "Science": 435}, | |
| "Malaysia": {"Math": 409, "Reading": 388, "Science": 416}, | |
| "Montenegro": {"Math": 406, "Reading": 405, "Science": 403}, | |
| "Mexico": {"Math": 395, "Reading": 415, "Science": 410}, | |
| "Thailand": {"Math": 394, "Reading": 379, "Science": 409}, | |
| "Peru": {"Math": 391, "Reading": 408, "Science": 408}, | |
| "Georgia": {"Math": 390, "Reading": 374, "Science": 384}, | |
| "Saudi Arabia": {"Math": 389, "Reading": 383, "Science": 390}, | |
| "North Macedonia": {"Math": 389, "Reading": 359, "Science": 380}, | |
| "Costa Rica": {"Math": 385, "Reading": 415, "Science": 411}, | |
| "Colombia": {"Math": 383, "Reading": 409, "Science": 411}, | |
| "Brazil": {"Math": 379, "Reading": 410, "Science": 403}, | |
| "Argentina": {"Math": 378, "Reading": 401, "Science": 406}, | |
| "Jamaica": {"Math": 377, "Reading": 410, "Science": 403}, | |
| "Albania": {"Math": 368, "Reading": 358, "Science": 376}, | |
| "Palestine": {"Math": 366, "Reading": 349, "Science": 369}, | |
| "Indonesia": {"Math": 366, "Reading": 359, "Science": 383}, | |
| "Morocco": {"Math": 365, "Reading": 339, "Science": 365}, | |
| "Uzbekistan": {"Math": 364, "Reading": 336, "Science": 355}, | |
| "Jordan": {"Math": 361, "Reading": 342, "Science": 375}, | |
| "Panama": {"Math": 357, "Reading": 392, "Science": 388}, | |
| "Kosovo": {"Math": 355, "Reading": 342, "Science": 357}, | |
| "Philippines": {"Math": 355, "Reading": 347, "Science": 356}, | |
| "Guatemala": {"Math": 344, "Reading": 374, "Science": 373}, | |
| "El Salvador": {"Math": 343, "Reading": 365, "Science": 373}, | |
| "Dominican Republic": {"Math": 339, "Reading": 351, "Science": 360}, | |
| "Paraguay": {"Math": 338, "Reading": 373, "Science": 368}, | |
| "Cambodia": {"Math": 336, "Reading": 329, "Science": 347} | |
| } | |
| # Edades diferentes para los datos del mundo | |
| world_ages = [20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85] | |
| ages = [16, 26, 36, 46, 56, 66, 76, 86, 96] | |
| # Streamlit App | |
| st.set_page_config(page_title="ïndices de cambio", layout="wide") | |
| st.title("📊 Análisis de curvas de cambios predecibles, ¿o no?") | |
| # Tabs | |
| tab1, tab2 = st.tabs(["Indice de felicidad", "🎓 Indice educativo"]) | |
| with tab1: | |
| st.header("La curva de la vida como una sonrisa") | |
| col1, col2 = st.columns([1, 2]) | |
| with col1: | |
| # Selectbox para región | |
| region = st.selectbox("Selecciona la región:", list(region_data.keys())) | |
| # Determinar rango de edades según la región | |
| if region == "Mundo": | |
| min_age, max_age = 20, 85 | |
| current_ages = world_ages | |
| else: | |
| min_age, max_age = 16, 96 | |
| current_ages = ages | |
| # Slider para la edad | |
| edad = st.slider("Selecciona tu edad:", | |
| min_value=min_age, | |
| max_value=max_age, | |
| step=1) | |
| with col2: | |
| satisfaction = region_data[region] | |
| # Normalizar los datos | |
| if region == "Mundo": | |
| x_norm = np.array([(age - 52.5) / 32.5 for age in current_ages]) | |
| y_norm = np.array([(s - 5.4) / 2.5 - 0.3 for s in satisfaction]) | |
| edad_interp = (edad - 52.5) / 32.5 | |
| else: | |
| x_norm = np.array([(age - 56) / 50 for age in current_ages]) | |
| y_norm = np.array([(s - 6.5) / 4 - 0.3 for s in satisfaction]) | |
| edad_interp = (edad - 56) / 50 | |
| # Suavizar la curva | |
| x_smooth = np.linspace(x_norm.min(), x_norm.max(), 300) | |
| spl = make_interp_spline(x_norm, y_norm, k=3) | |
| y_smooth = spl(x_smooth) | |
| # Encontrar posición de la edad seleccionada | |
| satisf_interp = float(spl(edad_interp)) if x_norm.min() <= edad_interp <= x_norm.max() else None | |
| # Crear gráfica | |
| fig, ax = plt.subplots(figsize=(8, 8)) | |
| ax.set_aspect('equal') | |
| ax.axis('off') | |
| # Cara | |
| cara = Circle((0, 0), 1, facecolor='gold', edgecolor='orange', linewidth=4) | |
| ax.add_patch(cara) | |
| # Sonrisa | |
| ax.plot(x_smooth, y_smooth, color="black", linewidth=3) | |
| # Ojos | |
| ax.plot(-0.3, 0.3, 'o', markersize=15, color='black') | |
| ax.plot(0.3, 0.3, 'o', markersize=15, color='black') | |
| # Lengua para mostrar la edad seleccionada | |
| if satisf_interp is not None: | |
| tongue_main = Ellipse((edad_interp, satisf_interp - 0.06), width=0.18, height=0.12, | |
| facecolor='#ff4444', edgecolor='#cc0000', linewidth=2) | |
| ax.add_patch(tongue_main) | |
| tongue_top = Ellipse((edad_interp, satisf_interp - 0.04), width=0.14, height=0.08, | |
| facecolor='#ff6666', alpha=0.8) | |
| ax.add_patch(tongue_top) | |
| ax.plot([edad_interp, edad_interp], | |
| [satisf_interp - 0.02, satisf_interp - 0.10], | |
| color='#aa0000', linewidth=1, alpha=0.7) | |
| ax.text(edad_interp, satisf_interp - 0.22, f"{edad} años", | |
| color='white', fontsize=10, ha='center', weight='bold', | |
| bbox=dict(boxstyle="round,pad=0.3", facecolor='red', | |
| edgecolor='darkred', linewidth=2)) | |
| ax.set_xlim(-1.2, 1.2) | |
| ax.set_ylim(-1.2, 1.2) | |
| st.pyplot(fig) | |
| # Información adicional | |
| if satisf_interp is not None: | |
| if region == "Mundo": | |
| satisfaccion_real = (satisf_interp + 0.3) * 2.5 + 5.4 | |
| else: | |
| satisfaccion_real = (satisf_interp + 0.3) * 4 + 6.5 | |
| st.info(f"A los {edad} años en {region}, el nivel de satisfacción promedio es: {satisfaccion_real:.2f}/10") | |
| # Comparación entre regiones | |
| st.subheader("Comparación entre regiones") | |
| fig2, ax2 = plt.subplots(figsize=(14, 8)) | |
| countries_data = {k: v for k, v in region_data.items() if k not in ["Mundo"]} | |
| for region_name, values in countries_data.items(): | |
| current_ages_plot = world_ages if region_name == "Mundo" else ages | |
| ax2.plot(current_ages_plot, values, marker='o', label=region_name, linewidth=2, markersize=6) | |
| ax2.plot(world_ages, region_data["Mundo"], marker='s', label="Mundo", | |
| linewidth=3, markersize=8, color='black', linestyle='--') | |
| ax2.set_xlabel('Edad (años)', fontsize=12) | |
| ax2.set_ylabel('Nivel de Satisfacción', fontsize=12) | |
| ax2.set_title('Curva de Satisfacción por Edad - Comparación Global', fontsize=14, fontweight='bold') | |
| ax2.legend(bbox_to_anchor=(1.05, 1), loc='upper left') | |
| ax2.grid(True, alpha=0.3) | |
| plt.tight_layout() | |
| st.pyplot(fig2) | |
| with tab2: | |
| st.header("Indices educativos con base en PISA 2022") | |
| # Preparar datos para los histogramas | |
| subjects = ["Math", "Reading", "Science"] | |
| subject_names = {"Math": "Matemáticas", "Reading": "Lectura", "Science": "Ciencias"} | |
| col1, col2 = st.columns([1, 3]) | |
| with col1: | |
| # Selector de materia | |
| selected_subject = st.selectbox("Selecciona la materia:", | |
| ["Math", "Reading", "Science"], | |
| format_func=lambda x: subject_names[x]) | |
| # Obtener datos válidos para la materia seleccionada | |
| valid_scores = [score[selected_subject] for score in pisa_data.values() | |
| if score[selected_subject] is not None] | |
| if valid_scores: | |
| min_score = min(valid_scores) | |
| max_score = max(valid_scores) | |
| # Opciones de selección | |
| selection_type = st.radio("Seleccionar por:", ["País", "Puntaje"]) | |
| if selection_type == "País": | |
| # Lista de países con datos válidos para la materia seleccionada | |
| valid_countries = [country for country, scores in pisa_data.items() | |
| if scores[selected_subject] is not None] | |
| selected_country = st.selectbox("Selecciona un país:", valid_countries) | |
| selected_score = pisa_data[selected_country][selected_subject] | |
| else: # Selección por puntaje | |
| selected_score = st.slider("Selecciona un puntaje:", | |
| min_value=int(min_score), | |
| max_value=int(max_score), | |
| value=int((min_score + max_score) / 2)) | |
| # Encontrar el país más cercano a este puntaje | |
| closest_country = min(pisa_data.keys(), | |
| key=lambda x: abs(pisa_data[x][selected_subject] - selected_score) | |
| if pisa_data[x][selected_subject] is not None else float('inf')) | |
| selected_country = closest_country | |
| with col2: | |
| # Crear ranking para identificar top y bottom 3 | |
| ranking = [(country, scores[selected_subject]) for country, scores in pisa_data.items() | |
| if scores[selected_subject] is not None] | |
| ranking.sort(key=lambda x: x[1], reverse=True) | |
| # Obtener top 3 y bottom 3 | |
| top_3 = ranking[:3] | |
| bottom_3 = ranking[-3:] | |
| # Configurar estilo moderno | |
| plt.style.use('default') | |
| fig, ax = plt.subplots(figsize=(14, 9)) | |
| fig.patch.set_facecolor('#f8f9fa') | |
| ax.set_facecolor('#ffffff') | |
| # Crear histograma con mejor diseño | |
| n, bins, patches = ax.hist(valid_scores, bins=25, alpha=0.8, | |
| color='#6c7ae0', edgecolor='white', linewidth=1.5) | |
| # Aplicar gradiente de colores | |
| for i, patch in enumerate(patches): | |
| bin_left = bins[i] | |
| bin_right = bins[i + 1] | |
| # Colorear según si contiene países destacados | |
| contains_top_3 = any(bin_left <= score <= bin_right for _, score in top_3) | |
| contains_bottom_3 = any(bin_left <= score <= bin_right for _, score in bottom_3) | |
| if contains_top_3: | |
| patch.set_facecolor('#FFD700') | |
| patch.set_edgecolor('#FF8C00') | |
| patch.set_linewidth(2) | |
| patch.set_alpha(0.9) | |
| elif contains_bottom_3: | |
| patch.set_facecolor('#FF6B6B') | |
| patch.set_edgecolor('#DC143C') | |
| patch.set_linewidth(2) | |
| patch.set_alpha(0.9) | |
| else: | |
| # Gradiente suave para otras barras | |
| intensity = i / len(patches) | |
| patch.set_facecolor(plt.cm.Blues(0.4 + intensity * 0.4)) | |
| patch.set_alpha(0.7) | |
| # Línea vertical para el país seleccionado | |
| ax.axvline(x=selected_score, color='#e74c3c', linestyle='--', linewidth=4, | |
| alpha=0.9, label=f'{selected_country}: {selected_score}') | |
| # Añadir etiquetas para TOP 3 con mejor posicionamiento | |
| for i, (country, score) in enumerate(top_3): | |
| ax.axvline(x=score, color='#f39c12', linestyle='-', alpha=0.8, linewidth=3) | |
| # Calcular posición y para evitar superposición | |
| y_pos = max(n) * (0.95 - i * 0.08) | |
| # Crear etiqueta con fondo | |
| bbox_props = dict(boxstyle="round,pad=0.4", facecolor='#FFD700', | |
| edgecolor='#FF8C00', alpha=0.9, linewidth=2) | |
| ax.annotate(f'🥇 {country}\n{score}' if i == 0 else | |
| f'🥈 {country}\n{score}' if i == 1 else | |
| f'🥉 {country}\n{score}', | |
| xy=(score, 0), xytext=(score, y_pos), | |
| ha='center', va='bottom', fontsize=10, fontweight='bold', | |
| bbox=bbox_props, color='#8B4513', | |
| arrowprops=dict(arrowstyle='->', color='#FF8C00', lw=2)) | |
| # Añadir etiquetas para BOTTOM 3 con mejor diseño | |
| for i, (country, score) in enumerate(bottom_3): | |
| ax.axvline(x=score, color='#c0392b', linestyle='-', alpha=0.8, linewidth=3) | |
| # Calcular posición y para la parte inferior | |
| y_pos = max(n) * (0.35 - i * 0.08) | |
| # Crear etiqueta con fondo | |
| bbox_props = dict(boxstyle="round,pad=0.4", facecolor='#FF6B6B', | |
| edgecolor='#DC143C', alpha=0.9, linewidth=2) | |
| rank = len(ranking) - 2 + i | |
| ax.annotate(f'{rank}° {country}\n{score}', | |
| xy=(score, 0), xytext=(score, y_pos), | |
| ha='center', va='bottom', fontsize=10, fontweight='bold', | |
| bbox=bbox_props, color='white', | |
| arrowprops=dict(arrowstyle='->', color='#DC143C', lw=2)) | |
| # Línea del promedio con mejor estilo | |
| mean_score = np.mean(valid_scores) | |
| ax.axvline(x=mean_score, color='#27ae60', linestyle=':', linewidth=3, | |
| alpha=0.8, label=f'Promedio Global: {mean_score:.1f}') | |
| # Mejorar el diseño general | |
| ax.set_xlabel('Puntaje PISA', fontsize=14, fontweight='bold', color='#2c3e50') | |
| ax.set_ylabel('Número de Países/Regiones', fontsize=14, fontweight='bold', color='#2c3e50') | |
| ax.set_title(f'🌍 Distribución Global PISA 2022 - {subject_names[selected_subject]}', | |
| fontsize=16, fontweight='bold', color='#2c3e50', pad=20) | |
| # Personalizar grid | |
| ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5, color='#bdc3c7') | |
| ax.set_axisbelow(True) | |
| # Mejorar bordes y espinas | |
| for spine in ax.spines.values(): | |
| spine.set_color('#bdc3c7') | |
| spine.set_linewidth(1) | |
| # Crear leyenda personalizada más elegante | |
| from matplotlib.patches import Patch | |
| legend_elements = [ | |
| plt.Line2D([0], [0], color='#e74c3c', linestyle='--', linewidth=4, | |
| label=f'País Seleccionado: {selected_country}'), | |
| plt.Line2D([0], [0], color='#27ae60', linestyle=':', linewidth=3, | |
| label=f'Promedio Global: {mean_score:.1f}'), | |
| Patch(facecolor='#FFD700', edgecolor='#FF8C00', alpha=0.9, | |
| label='🏆 Top 3 Países'), | |
| Patch(facecolor='#FF6B6B', edgecolor='#DC143C', alpha=0.9, | |
| label='📉 Bottom 3 Países'), | |
| Patch(facecolor='#6c7ae0', alpha=0.7, label='Otros Países') | |
| ] | |
| legend = ax.legend(handles=legend_elements, loc='upper center', | |
| fontsize=11, frameon=True, fancybox=True, | |
| shadow=True, framealpha=0.9) | |
| legend.get_frame().set_facecolor('#f8f9fa') | |
| legend.get_frame().set_edgecolor('#bdc3c7') | |
| # Ajustar límites para mejor visualización | |
| ax.set_xlim(min_score - 20, max_score + 20) | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| # Información estadística mejorada | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.metric("🏛️ País Seleccionado", selected_country) | |
| with col2: | |
| st.metric("📊 Puntaje", f"{selected_score}") | |
| with col3: | |
| st.metric("🌍 Promedio Global", f"{np.mean(valid_scores):.1f}") | |
| with col4: | |
| percentile = (sum(1 for score in valid_scores if score < selected_score) / len(valid_scores)) * 100 | |
| st.metric("📈 Percentil", f"{percentile:.1f}%") | |
| # Top 10 y Bottom 10 | |
| st.subheader(f"🏆 Ranking Mundial en {subject_names[selected_subject]}") | |
| col1, col2 = st.columns(2) | |
| # Crear ranking | |
| ranking = [(country, scores[selected_subject]) for country, scores in pisa_data.items() | |
| if scores[selected_subject] is not None] | |
| ranking.sort(key=lambda x: x[1], reverse=True) | |
| with col1: | |
| st.write("**🏆 Top 10 países:**") | |
| top_10 = ranking[:10] | |
| for i, (country, score) in enumerate(top_10, 1): | |
| emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else f"{i}." | |
| st.write(f"{emoji} {country}: {score}") | |
| with col2: | |
| st.write("**📉 Bottom 10 países:**") | |
| bottom_10 = ranking[-10:] | |
| bottom_10.reverse() | |
| for i, (country, score) in enumerate(bottom_10, 1): | |
| st.write(f"{len(ranking) - i + 1}. {country}: {score}") | |
| # Comparación por materias | |
| st.subheader("📚 Comparación por Materias") | |
| if selection_type == "País": | |
| country_scores = pisa_data[selected_country] | |
| valid_subjects = [subj for subj in subjects if country_scores[subj] is not None] | |
| if len(valid_subjects) > 1: | |
| fig, ax = plt.subplots(figsize=(12, 7)) | |
| fig.patch.set_facecolor('#f8f9fa') | |
| ax.set_facecolor('#ffffff') | |
| subject_scores = [country_scores[subj] for subj in valid_subjects] | |
| subject_labels = [subject_names[subj] for subj in valid_subjects] | |
| # Colores modernos para las barras | |
| colors = ['#e74c3c', '#3498db', '#2ecc71'][:len(valid_subjects)] | |
| bars = ax.bar(subject_labels, subject_scores, color=colors, | |
| alpha=0.8, edgecolor='white', linewidth=2) | |
| # Añadir valores en las barras con mejor estilo | |
| for bar, score in zip(bars, subject_scores): | |
| height = bar.get_height() | |
| ax.text(bar.get_x() + bar.get_width()/2., height + 10, | |
| f'{score}', ha='center', va='bottom', | |
| fontweight='bold', fontsize=14, color='#2c3e50') | |
| # Mejorar diseño | |
| ax.set_ylabel('Puntaje PISA', fontsize=14, fontweight='bold', color='#2c3e50') | |
| ax.set_title(f'📊 Rendimiento de {selected_country} por Materia', | |
| fontsize=16, fontweight='bold', color='#2c3e50', pad=20) | |
| ax.set_ylim(0, max(subject_scores) + 60) | |
| # Grid y estilo | |
| ax.grid(True, alpha=0.3, axis='y') | |
| ax.set_axisbelow(True) | |
| for spine in ax.spines.values(): | |
| spine.set_color('#bdc3c7') | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| else: | |
| st.info(f"Solo hay datos disponibles para una materia en {selected_country}") |