Saltar al contenido
Volver al inicio 100 Días de Código en Python — Día 79: El Premio Nobel en datos — mapas, sunburst y la edad de los laureados

100 Días de Código en Python — Día 79: El Premio Nobel en datos — mapas, sunburst y la edad de los laureados

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 79 (nivel avanzado).

Este tutorial adapta pedagógicamente el Día 79. Las secciones 📝 Contenido original del curso provienen de las lecciones 583 a 590; el cuaderno de inicio y el archivo nobel_prize_data.csv están en los recursos del curso (Nobel Prize Analysis Start.zip). En varias lecciones parte del código aparece solo en imágenes; aquí se reconstruye y se verifica. Los retos 🟢🟡🟠🔴, la sección una base de datos, varias historias, el proyecto extra sobre América Latina en el Nobel y la etapa 4 del Laboratorio de Datos Abiertos son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.

Nota sobre las verificaciones: el 27 de septiembre de 2026, con Python 3.14, pandas 3.0.6, plotly 7.1.0, seaborn 0.13.2, Matplotlib 3.11.2 y statsmodels 0.15.0, y con una copia pública de nobel_prize_data.csv (publicada en GitHub por otro estudiante del mismo curso), se ejecutó todo el código. Los resultados coinciden con el curso: 962 filas y 16 columnas, de 1901 a 2020; 6,2 % de mujeres; 6 laureados repetidos; Economía desde 1969; Estados Unidos, Reino Unido y Alemania a la cabeza; John Goodenough, de 97 años, el mayor. Hay tres observaciones importantes: cada fila es un laureado, no un premio (hay 962 filas pero 602 premios distintos), así que los gráficos cuentan laureados; la línea central de un diagrama de caja es la mediana, no la media; y lowess=True necesita el paquete statsmodels (sin él: RuntimeError: lowess=True requires statsmodels).


Tabla de contenidos

  1. Antes de empezar: el testamento de Alfred Nobel
  2. Explorar y limpiar el conjunto de datos
  3. Hacer preguntas: categorías, mujeres y repetidores
  4. Tendencias en el tiempo con Matplotlib
  5. Países: ranking, mapa coroplético y acumulado
  6. Instituciones y ciudades: gráfico sunburst
  7. La edad de los laureados
  8. 📖 Una base de datos, varias historias
  9. 🚀 Proyecto del día: la historia del Nobel en 8 gráficos
  10. 🌟 Proyecto extra: América Latina en el Nobel
  11. 🏗️ Laboratorio de Datos Abiertos — Etapa 4
  12. 🧩 Comprueba lo aprendido
  13. 🤖 Programar con apoyo de IA
  14. 🛠️ Correcciones técnicas al material original
  15. 📋 Matriz de trazabilidad
  16. 📦 Evidencias de aprendizaje del Día 79

0. Antes de empezar: el testamento de Alfred Nobel

(Fuente: 583-day-79-goals-what-you-will-make-by-the-end-of-the-day.md)

📝 Contenido original del curso

El 27 de noviembre de 1895, Alfred Nobel firmó su testamento en París. Al abrirse tras su muerte causó controversia: Nobel dejaba casi toda su fortuna para crear premios "a quienes, durante el año anterior, hayan conferido el mayor beneficio a la humanidad". Cada año se premia a científicos y escritores en química, literatura, física, fisiología o medicina, economía y paz.

Este proyecto integra muchas herramientas de los días anteriores y agrega otras nuevas:

  • un mapa coroplético para mostrar datos sobre un mapa;
  • barras de plotly con segmentos de los datos;
  • gráficos sunburst;
  • .lmplot() de seaborn con líneas de ajuste por categoría (row, hue, lowess);
  • cómo la misma información cuenta historias distintas según cómo se mire (cajas contra series de tiempo);
  • histogramas de seaborn para ver la distribución.

Preparación: descarga el ZIP, sube el cuaderno a Google Drive, ábrelo con Colab y agrega nobel_prize_data.csv.

Prerequisitos: plotly (Día 76), dos ejes y media móvil (Días 74 y 75), merge y groupby (Día 74), regplot (Día 78), np.arange (Día 77). En tu computador: pip install plotly seaborn statsmodels.


1. Explorar y limpiar el conjunto de datos

(Fuente: 584-update-packages-in-google-colab-y-explore-and-clean-the-dataset.md)

🎯 Objetivo de aprendizaje

Explicar por qué faltan datos (no solo cuántos) y convertir un texto como "1/3" en un número.

📝 Contenido original del curso

En Colab, actualizar plotly: el plotly preinstalado estaba desactualizado y causaba problemas con el sunburst. Se descomenta la celda con pip install, se ejecuta y se vuelve a comentar.

Reto 1: ¿forma?, ¿columnas y qué contienen?, ¿primer y último año? Reto 2: ¿duplicados?, ¿NaN?, ¿en qué columnas y cuántos?, ¿por qué? Reto 3: convierte birth_date a fecha y agrega share_pct con la parte del premio como decimal.

Solución: 962 filas y 16 columnas, de 1901 a 2020. Columnas destacadas: birth_date (texto), motivation, prize_share (fracción), laureate_type (individuo u organización), birth_country (incluye países que ya no existen), birth_country_current (nombre actual), ISO (código de 3 letras), organization_name, organization_city.

Python
print(f'Any duplicates? {df_data.duplicated().values.any()}')     # False
print(f'Any NaN values among the data? {df_data.isna().values.any()}')   # True
df_data.isna().sum()

col_subset = ['year', 'category', 'laureate_type',
              'birth_date', 'full_name', 'organization_name']
df_data.loc[df_data.birth_date.isna()][col_subset]

Sin fecha de nacimiento aparecen organizaciones (la ONU, la Cruz Roja): tiene sentido. Y como su nombre está en full_name, organization_name queda vacío. Además, muchos premios de Literatura y Paz fueron para personas sin afiliación a una universidad o instituto.

Python
df_data.birth_date = pd.to_datetime(df_data.birth_date)

separated_values = df_data.prize_share.str.split('/', expand=True)
numerator = pd.to_numeric(separated_values[0])
denomenator = pd.to_numeric(separated_values[1])
df_data['share_pct'] = numerator / denomenator

📖 Explicación conceptual (verificada)

Columna NaN ¿Por qué?
birth_date, birth_country, birth_country_current, sex, ISO 28 Son organizaciones: no nacen ni tienen sexo
birth_city 31 Las 28 organizaciones y 3 personas sin ciudad registrada
organization_name, organization_city 255 Organizaciones y personas sin afiliación (sobre todo Literatura y Paz)
motivation 88 Premios sin motivación registrada en la fuente

Faltante no es lo mismo que error. Un NaN puede significar "no aplica" (una organización no tiene fecha de nacimiento) o "no se sabe". No se deben eliminar esas filas a ciegas: borrar las 255 filas sin organización quitaría a casi todos los ganadores de Literatura y Paz.

share_pct: los valores posibles son 1, 1/2, 1/3 y 1/4 (verificado: 352, 321, 219 y 70 laureados).

🛠️ Nota técnica (verificado): un error en los datos. laureate_type marca 32 filas como Organization, pero solo 28 no tienen fecha de nacimiento. Las otras 4 son personas mal clasificadas: Le Duc Tho (1973), la Madre Teresa (1979), el 14.º Dalái Lama (1989) y Aung San Suu Kyi (1991), todas del premio de Paz. Así se detecta: df_data[(df_data.laureate_type == 'Organization') & df_data.sex.notna()]. Lección: contrasta columnas entre sí; una sola columna puede tener errores que otra delata.

🛠️ Nota técnica: hoy Colab trae versiones recientes de plotly; en general no hace falta actualizarlo. Si alguna vez lo necesitas, %pip install --upgrade plotly y luego reinicia la sesión (Entorno de ejecución → Reiniciar), porque un paquete ya importado no se recarga solo.

🛠️ Nota técnica: el curso escribe denomenator; la palabra correcta en inglés es denominator. Funciona igual (es solo un nombre de variable), pero conviene escribir bien los nombres.

⚠️ Errores frecuentes

  • dropna() sin pensar: pasarías de 962 a menos de 700 filas y perderías categorías enteras.
  • pd.to_numeric('1/3'): ValueError. Hay que separar numerador y denominador primero.
  • Tratar sex como si incluyera a las organizaciones: los porcentajes cambian según el denominador (sección 2).

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: ¿cuántas organizaciones han ganado realmente? ¿En qué categoría? Corrige laureate_type de las 4 personas mal clasificadas y comprueba que ahora coincide con las 28 filas sin fecha de nacimiento.
  • 🟡 Reto 2: calcula share_pct en una sola línea con .map({'1/1': 1, '1/2': 0.5, ...}). ¿Qué ventaja y qué riesgo tiene frente a split?

2. Hacer preguntas: categorías, mujeres y repetidores

(Fuente: 585-plotly-bar-y-donut-charts-analyse-prize-categories-y-women-winning-prizes.md)

🎯 Objetivo de aprendizaje

Formular preguntas propias sobre unos datos y responderlas con conteos, filtros y gráficos segmentados.

📝 Contenido original del curso

Reto 1: formula 3 preguntas. Buena parte de la ciencia de datos es hacerse preguntas; es lo más difícil de enseñar porque requiere creatividad. A veces los datos no pueden responderlas, "y está bien: es parte del descubrimiento". Ejemplos: "¿Qué porcentaje de laureados fueron mujeres?", "¿Cuántos premios hubo en cada categoría?".

Reto 2: dona de hombres contra mujeres. Solo un 6,2 % fueron mujeres.

Python
biology = df_data.sex.value_counts()
fig = px.pie(labels=biology.index, values=biology.values,
             title="Percentage of Male vs. Female Winners",
             names=biology.index, hole=0.4)
fig.update_traces(textposition='inside', textfont_size=15, textinfo='percent')
fig.show()

Reto 3: las 3 primeras mujeres laureadas (una es, claro, Marie Curie):

Python
df_data[df_data.sex == 'Female'].sort_values('year', ascending=True)[:3]

Reto 4: ¿alguien ganó más de una vez? Buscando duplicados en full_name:

Python
is_winner = df_data.duplicated(subset=['full_name'], keep=False)
multiple_winners = df_data[is_winner]
print(f'There are {multiple_winners.full_name.nunique()}'
      ' winners who were awarded the prize more than once.')   # 6

Solo 4 son personas. Marie Curie ganó en Física y en Química; Linus Pauling, en Química y luego en Paz por promover el desarme nuclear; la Cruz Roja Internacional ganó el de Paz 3 veces, las dos primeras durante las guerras mundiales.

Reto 5: ¿cuántas categorías? (6). Barras con la escala Aggrnyl, sin eje de color:

Python
prizes_per_category = df_data.category.value_counts()
v_bar = px.bar(x=prizes_per_category.index, y=prizes_per_category.values,
               color=prizes_per_category.values, color_continuous_scale='Aggrnyl',
               title='Number of Prizes Awarded per Category')
v_bar.update_layout(xaxis_title='Nobel Prize Category', coloraxis_showscale=False,
                    yaxis_title='Number of Prizes')
v_bar.show()

Reto 6: ¿por qué tan pocos de Economía? Es mucho más nuevo: desde 1969 (Física, desde 1901).

Reto 7: hombres y mujeres por categoría:

Python
cat_men_women = df_data.groupby(['category', 'sex'],
                                as_index=False).agg({'prize': pd.Series.count})
cat_men_women.sort_values('prize', ascending=False, inplace=True)

v_bar_split = px.bar(x=cat_men_women.category, y=cat_men_women.prize,
                     color=cat_men_women.sex,
                     title='Number of Prizes Awarded per Category split by Men and Women')
v_bar_split.update_layout(xaxis_title='Nobel Prize Category', yaxis_title='Number of Prizes')
v_bar_split.show()

El desequilibrio es muy grande en Física, Economía y Química; las mujeres están algo más representadas en Medicina, Literatura y Paz. Dividir las barras así da una imagen mucho más detallada.

📖 Resultados verificados

Categoría Mujeres Hombres
Chemistry 7 179
Economics 2 84
Literature 16 101
Medicine 12 210
Peace 17 90
Physics 4 212

Primeras mujeres: Marie Curie (Física, 1903), Bertha von Suttner (Paz, 1905) y Selma Lagerlöf (Literatura, 1909). Repetidores: Marie Curie, Linus Pauling, John Bardeen, Frederick Sanger, el Comité Internacional de la Cruz Roja (3 veces) y el ACNUR (Alto Comisionado de las Naciones Unidas para los Refugiados, 2 veces).

🛠️ Nota técnica (verificado): ¿premios o laureados? Cada fila es un laureado. Un premio compartido por tres personas aparece 3 veces. Hay 962 filas pero solo 602 premios distintos (combinaciones de año y categoría). Por eso los gráficos del curso titulados "Number of Prizes" cuentan en realidad laureados. Para contar premios: df_data.groupby('category').prize.nunique() (Física: 114 premios, 216 laureados).

🛠️ Nota técnica (verificado): el denominador. El 6,2 % es 58 mujeres de 934 personas (se excluyen las 28 organizaciones, que no tienen sexo). Sobre las 962 filas sería 6,0 %. Ninguna cifra es "la correcta": hay que decir cuál es el denominador.

⚠️ Errores frecuentes

  • Llamar a una variable biology para contar sexos: funciona, pero un nombre como conteo_sexo comunica mejor.
  • Buscar repetidores con duplicated() sin keep=False: solo verías la segunda aparición de cada uno.
  • Comparar Economía con Física por el total sin notar que Economía empezó 68 años después.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: calcula el porcentaje de mujeres por década. ¿Está aumentando?
  • 🟡 Reto 2: grafica el número de premios (no laureados) por categoría y compáralo con el gráfico del curso.
  • 🔴 Reto 3 — Pensamiento computacional: para comparar Economía con las demás categorías de forma justa, calcula laureados por año desde que existe cada categoría. Diseña el cálculo y explica tu decisión.

3. Tendencias en el tiempo con Matplotlib

(Fuente: 586-using-matplotlib-to-visualise-trends-over-time.md)

🎯 Objetivo de aprendizaje

Combinar dispersión, media móvil y un segundo eje invertido para mostrar la relación entre dos tendencias.

📝 Contenido original del curso

Reto 1: ¿se otorgan más premios ahora? Cuenta los premios por año, calcula la media móvil de 5 años, superponla a un gráfico de dispersión, pon una marca cada 5 años de 1900 a 2020 (con NumPy) y usa los colores dodgerblue y crimson. ¿Influyeron las guerras mundiales?

Python
prize_per_year = df_data.groupby(by='year').count().prize
moving_average = prize_per_year.rolling(window=5).mean()

plt.figure(figsize=(16, 8), dpi=200)
plt.title('Number of Nobel Prizes Awarded per Year', fontsize=18)
plt.yticks(fontsize=14)
plt.xticks(ticks=np.arange(1900, 2021, step=5), fontsize=14, rotation=45)

ax = plt.gca()
ax.set_xlim(1900, 2020)
ax.scatter(x=prize_per_year.index, y=prize_per_year.values,
           c='dodgerblue', alpha=0.7, s=100)
ax.plot(prize_per_year.index, moving_average.values, c='crimson', linewidth=3)
plt.show()

Reto 2: ¿se comparten más los premios? Promedio anual de share_pct y su media móvil de 5 años, en un segundo eje, invertido:

Python
yearly_avg_share = df_data.groupby(by='year').agg({'share_pct': pd.Series.mean})
share_moving_average = yearly_avg_share.rolling(window=5).mean()

ax1 = plt.gca()
ax2 = ax1.twinx()           # segundo eje y
ax2.invert_yaxis()          # se puede invertir
# ... scatter y plot en ax1 como antes ...
ax2.plot(prize_per_year.index, share_moving_average.values, c='grey', linewidth=3)

Hay una clara tendencia al alza en el número de premios, porque se comparten cada vez más. Desde 1969 hay más por la llegada de Economía. Durante las guerras mundiales se otorgaron muy pocos: en lugar de ceros, se ven puntos azules que faltan.

📖 Explicación conceptual (verificada)

  • Años faltantes (verificado): 1940, 1941 y 1942 no tienen ninguna fila; durante la Primera Guerra Mundial hubo pocos laureados (1914: 3; 1916: 1; 1918: 2). groupby solo crea filas para los años que existen en los datos: un año sin premios no aparece como 0, sino que no aparece.
  • La media móvil de 5 años llega a 12,4 laureados por año en 2020, frente a 6 en 1901. La parte promedio del premio baja de 0,82 (1905) a 0,49 (2020).
  • ¿Por qué invertir el segundo eje? Porque las dos tendencias van en direcciones opuestas: más laureados ↔ partes más pequeñas. Al invertirlo, ambas líneas suben juntas y la relación "salta a la vista". Es un recurso legítimo solo si se indica claramente (etiqueta del eje): si no, el lector creería que la parte del premio aumenta.

🛠️ Nota técnica: el reto escribe dogerblue; el nombre del color es dodgerblue (la solución lo usa bien). Con el nombre mal escrito, Matplotlib lanza ValueError indicando que no es un color válido. Además, los gráficos con dos ejes conservan el título "Number of Nobel Prizes Awarded per Year": conviene cambiarlo y etiquetar ambos ejes.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: agrega etiquetas a los dos ejes, una leyenda y una anotación en 1969 ("Primer premio de Economía").
  • 🟡 Reto 2: reindexa la serie con todos los años (prize_per_year.reindex(range(1901, 2021), fill_value=0)) y compara el gráfico. ¿Qué versión es más honesta para mostrar las guerras?

4. Países: ranking, mapa coroplético y acumulado

(Fuente: 587-a-choropleth-map-and-the-countries-with-the-most-prizes.md)

🎯 Objetivo de aprendizaje

Elegir la columna adecuada para un análisis geográfico, crear un mapa coroplético y un gráfico de líneas acumulado.

📝 Contenido original del curso

Reto 1: top20_countries con el total de premios. ¿birth_country, birth_country_current u organization_country? ¿Qué problemas tiene cada una?

birth_country incluye países que ya no existen (la Unión Soviética, Checoslovaquia): es mejor birth_country_current, el país que hoy controla la ciudad de nacimiento. Pero eso no es la nacionalidad: hay quienes nacieron en el extranjero, emigraron o cambiaron de ciudadanía. Hay que ser claros con los supuestos.

Python
top_countries = df_data.groupby(['birth_country_current'],
                                as_index=False).agg({'prize': pd.Series.count})
top_countries.sort_values(by='prize', inplace=True)
top20_countries = top_countries[-20:]

h_bar = px.bar(x=top20_countries.prize, y=top20_countries.birth_country_current,
               orientation='h', color=top20_countries.prize,
               color_continuous_scale='Viridis', title='Top 20 Countries by Number of Prizes')
h_bar.update_layout(xaxis_title='Number of Prizes', yaxis_title='Country',
                    coloraxis_showscale=False)
h_bar.show()

Reto 2: un mapa coroplético (se abre en otra pestaña) con los códigos ISO de 3 letras (escala matter):

Python
df_countries = df_data.groupby(['birth_country_current', 'ISO'],
                               as_index=False).agg({'prize': pd.Series.count})

world_map = px.choropleth(df_countries, locations='ISO', color='prize',
                          hover_name='birth_country_current',
                          color_continuous_scale=px.colors.sequential.matter)
world_map.update_layout(coloraxis_showscale=True)
world_map.show()

Reto 3: divide las barras por categoría. Primero se cuenta por país y categoría; luego se une con top20_countries para tener el total y controlar el orden:

Python
cat_country = df_data.groupby(['birth_country_current', 'category'],
                              as_index=False).agg({'prize': pd.Series.count})
merged_df = pd.merge(cat_country, top20_countries, on='birth_country_current')
merged_df.columns = ['birth_country_current', 'category', 'cat_prize', 'total_prize']
merged_df.sort_values(by='total_prize', inplace=True)

cat_cntry_bar = px.bar(x=merged_df.cat_prize, y=merged_df.birth_country_current,
                       color=merged_df.category, orientation='h',
                       title='Top 20 Countries by Number of Prizes and Category')
cat_cntry_bar.show()

Estados Unidos tiene una proporción increíble de los premios de Economía; Japón y Alemania, muy pocos o ninguno. Estados Unidos tiene más premios solo en Física o solo en Medicina que Francia en total. Alemania ganó más en Física que el Reino Unido, y Francia más en Paz y Literatura que Alemania.

Reto 4: premios acumulados por país en el tiempo:

Python
prize_by_year = df_data.groupby(by=['birth_country_current', 'year'], as_index=False).count()
prize_by_year = prize_by_year.sort_values('year')[['year', 'birth_country_current', 'prize']]

cumulative_prizes = prize_by_year.groupby(by=['birth_country_current',
                                              'year']).sum().groupby(level=[0]).cumsum()
cumulative_prizes.reset_index(inplace=True)

l_chart = px.line(cumulative_prizes, x='year', y='prize',
                  color='birth_country_current', hover_name='birth_country_current')
l_chart.show()

Estados Unidos despegó tras la Segunda Guerra Mundial, que devastó Europa; antes, el Nobel era "un asunto europeo". En los últimos 40 años hay muchos más países representados. El Reino Unido y Alemania intercambiaron posiciones en los años 70 y 90, y Suecia se mantuvo quinta durante décadas: ¿un poco de "sesgo local"?

📖 Resultados verificados

País (birth_country_current) Laureados Química Economía Literatura Medicina Paz Física
Estados Unidos 281 55 49 10 78 19 70
Reino Unido 105 27 8 7 28 11 24
Alemania 84 26 1 8 18 5 26
Francia 57 10 4 11 12 10 10
Japón 27 7 0 3 5 1 11

Respuestas a las preguntas del reto 3: Alemania y Japón son más débiles que Estados Unidos en Economía; Alemania supera al Reino Unido en Física (26 contra 24); Francia supera a Alemania en Paz, Literatura y también en Economía (4 contra 1); Australia tiene la mayoría en Medicina (7 de 10); la mitad de los premios de los Países Bajos es de Física (9 de 18); Estados Unidos no supera en Economía a toda Francia (49 contra 57), pero sí en Física (70) y en Medicina (78).

¿Cuándo tomó la delantera Estados Unidos? Según el acumulado verificado, el país con más laureados fue Francia o Alemania hasta 1951, año en que Estados Unidos pasó al primer lugar y ya no lo dejó.

Cómo funciona el acumulado: groupby(level=[0]).cumsum() suma, dentro de cada país, los valores año tras año. Como solo hay filas en los años en que un país ganó, la línea salta de un año con premio al siguiente.

⚠️ Errores frecuentes

  • Mapa vacío o con países grises: el coroplético necesita códigos ISO alfa-3 correctos (COL, USA). Un nombre de país en locations no funciona si no indicas locationmode='country names'.
  • merge que pierde países: con how='inner' (por defecto), un país que no está en el top 20 desaparece; aquí es lo que se busca.
  • Cambiar columns sin revisar el orden: merged_df.columns = [...] renombra por posición; si el orden cambia, los nombres quedan cruzados. Es más seguro rename(columns={'prize_x': 'cat_prize', 'prize_y': 'total_prize'}).

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: repite el ranking con organization_country (dónde se hizo la investigación). ¿Qué países suben? ¿Qué te dice eso sobre la "fuga de cerebros"?
  • 🟡 Reto 2: haz el mapa por millón de habitantes con un archivo de población. ¿Qué país lidera ahora?
  • 🟠 Reto 3: anima el mapa con animation_frame='decade' usando los datos acumulados por década.

5. Instituciones y ciudades: gráfico sunburst

(Fuente: 588-create-sunburst-charts-for-a-detailed-regional-breakdown-of-research-locations.md)

📝 Contenido original del curso

Reto 1: las 20 instituciones con más laureados. ¿Cuántos tienen la Universidad de Chicago y Harvard? Reto 2: las 20 ciudades de investigación. ¿Cuál es el mayor centro de descubrimientos? ¿Y en Europa? Reto 3: las 20 ciudades de nacimiento (escala Plasma). ¿Qué porcentaje de los premios de Estados Unidos corresponde a nacidos en Nueva York? ¿Cuántos nacieron en Londres, París y Viena? ¿Cuántas de las 5 primeras están en Estados Unidos? Reto 4: un sunburst país → ciudad → organización. ¿Qué notas en Alemania y Francia?

Python
top20_orgs = df_data.organization_name.value_counts()[:20]
top20_orgs.sort_values(ascending=True, inplace=True)
org_bar = px.bar(x=top20_orgs.values, y=top20_orgs.index, orientation='h',
                 color=top20_orgs.values, color_continuous_scale=px.colors.sequential.haline,
                 title='Top 20 Research Institutions by Number of Prizes')
org_bar.update_layout(xaxis_title='Number of Prizes', yaxis_title='Institution',
                      coloraxis_showscale=False)
org_bar.show()

country_city_org = df_data.groupby(by=['organization_country', 'organization_city',
                                       'organization_name'],
                                   as_index=False).agg({'prize': pd.Series.count})

burst = px.sunburst(country_city_org,
                    path=['organization_country', 'organization_city', 'organization_name'],
                    values='prize', title='Where do Discoveries Take Place?')
burst.show()

Cambridge (Massachusetts) y Nueva York lideran la investigación. Una ciudad muy poblada tiene más probabilidad de ser cuna de un laureado (Nueva York, París, Londres), aunque Viena y Budapest, menos pobladas, produjeron muchos. Las ciudades donde se investiga (Cambridge, Stanford, Berkeley) no son las de nacimiento. El sunburst muestra la concentración: en Francia casi todo está en París; en Alemania los descubrimientos están mucho más repartidos; el Reino Unido está dominado por Cambridge y Londres.

📖 Resultados verificados

  • Instituciones: University of California (40), Harvard (29), Stanford (23), MIT (21), University of Chicago (20).
  • Ciudades de investigación: Cambridge, MA (50), New York, NY (45), Cambridge del Reino Unido (31), Londres (27), París (25). En Europa gana Cambridge.
  • Ciudades de nacimiento: Nueva York (53), París (26), Londres (19), Viena (14), Chicago (12). Entre las 5 primeras, 2 están en Estados Unidos. Nacidos en Nueva York: 18,9 % de los 281 laureados nacidos en Estados Unidos.
  • Francia: 25 de 38 laureados afiliados a instituciones francesas trabajaban en París; Alemania: 23 ciudades distintas.

🛠️ Nota técnica: la solución del sunburst llama a update_layout(xaxis_title=..., yaxis_title=..., coloraxis_showscale=False), que no tiene efecto: un sunburst no tiene ejes. Sobra, aunque no produce error. Y ojo: "Brooklyn, NY" aparece aparte de "New York, NY"; si los sumas, Nueva York crece aún más. Decidir cómo agrupar lugares es parte del análisis.

🔎 ¿Cómo se lee un sunburst?

Es un gráfico circular por niveles: el anillo interior son los países; el siguiente, sus ciudades; el exterior, las organizaciones. El ángulo de cada porción es proporcional a values. Al hacer clic en un país, el gráfico "entra" en él. Es ideal para jerarquías; con más de 3 niveles se vuelve ilegible.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: crea un sunburst categoría → sexo → país de nacimiento.
  • 🟡 Reto 2: unifica "Brooklyn, NY" y "New York, NY" y recalcula el ranking.

6. La edad de los laureados

(Fuente: 589-unearthing-patterns-in-the-laureate-age-at-the-time-of-the-award.md y 590-learning-points-y-summary.md)

🎯 Objetivo de aprendizaje

Describir una distribución con un histograma y estadísticas, y comparar la visión "de conjunto" (cajas) con la visión "en el tiempo" (lmplot con lowess).

📝 Contenido original del curso

Reto 1: la edad en el año del premio, en winning_age:

Python
birth_years = df_data.birth_date.dt.year
df_data['winning_age'] = df_data.year - birth_years

Reto 2: el mayor y el menor laureado:

Python
display(df_data.nlargest(n=1, columns='winning_age'))
display(df_data.nsmallest(n=1, columns='winning_age'))

John Goodenough tenía 97 años. Nació de padres estadounidenses en Alemania: nuestro análisis por país cuenta un premio "alemán" extra, aunque es estadounidense. "Nunca es tarde para ganar un Nobel".

Reto 3: estadísticas descriptivas e histograma (prueba 10, 20, 30 y 50 bins):

Python
plt.figure(figsize=(8, 4), dpi=200)
sns.histplot(data=df_data, x=df_data.winning_age, bins=30)
plt.xlabel('Age')
plt.title('Distribution of Age on Receipt of Prize')
plt.show()

Reto 4: ¿los laureados son cada vez mayores? regplot con lowess=True:

Python
plt.figure(figsize=(8, 4), dpi=200)
with sns.axes_style("whitegrid"):
    sns.regplot(data=df_data, x='year', y='winning_age', lowess=True,
                scatter_kws={'alpha': 0.4}, line_kws={'color': 'black'})
plt.show()

lowess traza una regresión local: como una media móvil, da una forma no lineal. De 1900 a 1950 los laureados tenían unos 55 años; hoy, cerca de 70. En los últimos 10 años la dispersión aumentó: más laureados muy jóvenes y muy viejos.

Reto 5: edades por categoría con sns.boxplot (muestra "la media, los cuartiles, el máximo y el mínimo"). ¿De verdad los ganadores de Paz son mayores que los de Física?

Reto 6: lmplot con una fila por categoría (row) y luego todas juntas (hue):

Python
with sns.axes_style('whitegrid'):
    sns.lmplot(data=df_data, x='year', y='winning_age', row='category',
               lowess=True, aspect=2,
               scatter_kws={'alpha': 0.6}, line_kws={'color': 'black'})
plt.show()

with sns.axes_style("whitegrid"):
    sns.lmplot(data=df_data, x='year', y='winning_age', hue='category',
               lowess=True, aspect=2,
               scatter_kws={'alpha': 0.5}, line_kws={'linewidth': 5})
plt.show()

Física, Química y Medicina envejecen con el tiempo; el efecto más fuerte está en Física (de menos de 50 a más de 70). Economía es más estable. Paz muestra la tendencia opuesta: sus ganadores son más jóvenes. Las cajas del conjunto y las líneas en el tiempo cuentan historias muy distintas.

Resumen del día: repaso de NaN, conversiones, dona y barras de plotly, media móvil, value_counts, groupby, merge, sort_values y agg; nuevo: mapa coroplético, barras segmentadas, sunburst, lmplot con row, hue y lowess, histogramas y la lección de que los mismos datos pueden mostrar historias distintas.

📖 Resultados verificados

Estadística de winning_age Valor
Laureados con edad (personas) 934
Promedio 59,9 años
Mediana 60
75 % tiene menos de… 69 años
Mínimo / máximo 17 (Malala Yousafzai, Paz, 2014) / 97 (John Goodenough, Química, 2019)
Categoría Edad mediana Tendencia lowess (primer dato → último)
Physics 55 46 → 73
Chemistry 58 48 → 71
Medicine 57 54 → 69
Literature 67 61 → 70
Economics 67 68 → 69 (estable)
Peace 62 66 → 57 (baja)

🛠️ Nota técnica (verificado): lowess=True necesita el paquete statsmodels. En Colab ya está; en tu computador, sin él: RuntimeError: lowess=True requires statsmodels, an optional dependency, to be installed. Solución: pip install statsmodels.

🛠️ Nota técnica: la línea central de un diagrama de caja es la mediana, no la media (Día 76, sección 8). En sns.boxplot puedes mostrar también la media con showmeans=True. Con Matplotlib 3.11, seaborn 0.13.2 muestra una advertencia (MatplotlibDeprecationWarning: vert: bool was deprecated): viene de seaborn, no de tu código, y el gráfico funciona.

🛠️ Nota técnica (verificado): año del premio − año de nacimiento no es la edad exacta: quien cumple años después de la ceremonia (10 de diciembre) queda con 1 año de más. Pasa con 55 de los 934 laureados. Para un análisis general la aproximación es razonable; si necesitaras la edad exacta, restarías fechas completas.

⚠️ Errores frecuentes

  • Histograma con pocos o demasiados bins: con 5 no ves la forma; con 200 ves ruido. Prueba varios y elige el que muestre la forma sin exagerar.
  • lmplot dentro de plt.figure(...): lmplot crea su propia figura (es una FacetGrid); el tamaño se controla con height y aspect.
  • Calcular la edad sin convertir la fecha: df_data.birth_date.dt.year falla si birth_date sigue siendo texto (AttributeError: Can only use .dt accessor with datetimelike values).

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: ¿quiénes ganaron antes de los 30 años? ¿En qué categorías?
  • 🟡 Reto 2: compara la edad mediana de hombres y mujeres por categoría.
  • 🟠 Reto 3: agrega la media al diagrama de caja con showmeans=True. ¿En qué categoría se separan más media y mediana? ¿Por qué?

7. 📖 Una base de datos, varias historias

(Sección creada para esta adaptación.)

El curso muestra que la caja dice "los de Paz son mayores" y el lmplot dice "los de Paz son cada vez más jóvenes". Las dos cosas son ciertas: la caja resume 120 años a la vez; la línea muestra el cambio. Antes de concluir, pregúntate siempre:

Pregunta En el Nobel
¿Qué representa cada fila? Un laureado, no un premio
¿Qué falta y por qué? Organizaciones sin edad; años de guerra sin premios
¿Qué columna mide lo que quiero? País de nacimiento ≠ nacionalidad ≠ país donde investigó
¿Estoy resumiendo o viendo el cambio? Cajas contra lmplot
¿Quién eligió los datos? Un comité sueco y noruego: los premios reflejan sus criterios y los de su época

Representación: que solo el 6,2 % de los laureados sean mujeres o que haya muy pocos laureados de África y América Latina no mide el talento de esas poblaciones: refleja quién tuvo acceso a la educación, a los laboratorios y a las redes científicas, y quién fue nominado. Los datos describen el pasado; no dicen "quién puede" ganar.


8. 🚀 Proyecto del día: la historia del Nobel en 8 gráficos

(Integra las lecciones 584 a 590.)

Problema

La biblioteca del colegio prepara una exposición sobre ciencia y quiere una infografía interactiva sobre el Premio Nobel.

Contexto

Una exposición la verán estudiantes de todos los grados: los gráficos deben ser claros y las conclusiones, cuidadosas.

Objetivo

Un cuaderno con el análisis completo del curso y una selección de 8 gráficos guardados en HTML con títulos en español.

Requisitos funcionales

  1. Limpieza explicada (tabla de NaN y sus causas), birth_date convertida, share_pct y winning_age.
  2. Dona por sexo y barras por categoría segmentadas por sexo.
  3. Laureados por año con media móvil y parte promedio del premio en un segundo eje.
  4. Top 20 de países, mapa coroplético y acumulado por país.
  5. Sunburst de lugares de investigación.
  6. Histograma de edades, lmplot por categoría y cajas por categoría.
  7. Una conclusión por gráfico, indicando si cuenta laureados o premios.

Restricciones técnicas

  • Cada gráfico guardado con write_html (plotly) o savefig (Matplotlib y seaborn).
  • Ninguna conclusión sobre "talento" o "capacidad" de un grupo (sección 7).

Planificación

  1. Comprender los datos. 2. Entradas: el CSV. 3. Procesos: limpiar → preguntar → agrupar → graficar → interpretar. 4. Salidas: 8 gráficos y textos. 5. Dividir por lección. 6. Elegir los 8 gráficos más claros. 7. Implementar. 8. Probar. 9. Corregir. 10. Montar la exposición.

Construcción paso a paso

  • Versión 1: exploración, NaN y conversiones.
  • Versión 2: categorías y sexo.
  • Versión 3: tendencias en el tiempo.
  • Versión 4: países y mapa.
  • Versión 5: instituciones y sunburst.
  • Versión final: edades y selección de los 8 gráficos.

Pruebas

Comprobación Valor esperado (verificado)
Forma y años (962, 16); 1901–2020
Porcentaje de mujeres (sobre personas) 6,2 %
Repetidores 6
Años sin laureados 1940, 1941, 1942
Premios distintos 602
País con más laureados Estados Unidos (281)
Edad mínima / máxima 17 / 97

🧪 Prueba y depuración

Python
df_data['share_pct'] = pd.to_numeric(df_data.prize_share)
df_data['winning_age'] = df_data.year - df_data.birth_date.dt.year
top20 = df_data.birth_country.value_counts()[:20]
world_map = px.choropleth(df_data, locations='birth_country_current', color='prize')
sns.regplot(data=df_data, x='year', y='winning_age', lowess=True)

Encuentra cinco problemas: to_numeric no entiende "1/2" (ValueError); birth_date no se ha convertido a fecha (AttributeError con .dt); birth_country incluye países que ya no existen; el mapa usa nombres en locations (sin locationmode) y colorea por la columna de texto prize en vez de un conteo; y lowess=True falla si no está instalado statsmodels.

⭐ Extensión del proyecto

  • Reto 1: una línea de tiempo interactiva con px.scatter (año contra categoría) donde cada punto sea un laureado y el color sea el sexo.
  • Reto 2: analiza las palabras más frecuentes en motivation por categoría (pista: str.split + explode + value_counts).

9. 🌟 Proyecto extra: América Latina en el Nobel

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

En los datos hay 19 laureados nacidos en países de América Latina y el Caribe (verificado), entre ellos dos colombianos: Gabriel García Márquez (Literatura, 1982) y Juan Manuel Santos (Paz, 2016).

Requisitos

  1. Define qué países cuentan como "América Latina y el Caribe" y justifica tu lista.
  2. Filtra los laureados con isin. ¿Cuántos son? ¿En qué categorías?
  3. Crea un mapa coroplético limitado al continente (scope='south america' o ajustando la geografía con fig.update_geos).
  4. Crea una línea de tiempo de los premios de la región.
  5. Compara birth_country_current con organization_country para los premios científicos. ¿Dónde investigaron? Escribe un párrafo sobre la migración de científicos.
  6. Investiga (fuentes confiables) a una o un laureado de la región y agrega una ficha con su aporte.

Decisiones que debes tomar (y justificar)

  • ¿Incluyes a quien nació en la región pero desarrolló su carrera en otro país (por ejemplo, Peter Medawar, nacido en Brasil)?
  • ¿Qué conclusión no se puede sacar de que la región tenga pocos premios científicos?

Casos de prueba

  • Tu filtro encuentra a García Márquez y a Santos.
  • El mapa usa los códigos ISO de la columna ISO.

10. 🏗️ Laboratorio de Datos Abiertos — Etapa 4

(Proyecto integrador creado para esta adaptación; etapas anteriores en los Días 76 a 78.)

Etapa 4 (hoy):

  1. Describe la distribución de tu variable principal: describe(), histograma con varios bins y una frase sobre su forma (¿simétrica?, ¿sesgada?, ¿con valores extremos?).
  2. Compárala entre categorías con cajas y con lmplot (con hue o row) si tienes una variable de tiempo. ¿Cuentan la misma historia?
  3. Si tus datos tienen municipios o departamentos, crea un mapa coroplético (plotly acepta un archivo GeoJSON de los departamentos de Colombia con geojson= y featureidkey=); si no, un sunburst con una jerarquía de tus datos.
  4. Completa en laboratorio.ipynb la tabla de la sección 7: qué representa cada fila, qué falta, qué mide cada columna.

Producto de la etapa: sección 5 de laboratorio.ipynb.


11. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Por qué birth_country_current es mejor que birth_country para un mapa? ¿Qué problema sigue teniendo?
  2. ¿Qué diferencia hay entre contar laureados y contar premios?
  3. ¿Qué es un mapa coroplético y qué dato necesita para ubicar cada país?
  4. ¿Qué hace lowess=True? ¿Qué paquete necesita?
  5. ¿Cómo es posible que la caja y el lmplot de Paz digan cosas "opuestas"?

Análisis de código

Python
df_data.groupby(['birth_country_current', 'year']).size().groupby(level=0).cumsum()

¿Qué calcula? ¿Qué forma tiene el índice del resultado?

Predicción

Si un premio de Física de un año se reparte entre 3 personas, ¿cuántas filas aparecen y cuánto vale share_pct en cada una? ¿Cuánto suma share_pct para ese premio?

Depuración

"Mi coroplético aparece completamente gris". Da dos causas posibles.

Modificación

Cambia el histograma de edades para mostrar una curva de densidad (kde=True) y separa por sexo con hue='sex'.

Creación

Escribe ranking(df, columna, n) que devuelva un gráfico de barras horizontales de plotly con los n valores más frecuentes de cualquier columna, ordenados de mayor a menor (arriba el mayor).


12. 🤖 Programar con apoyo de IA

Ciclo de trabajo: formula el problema → inténtalo tú → consulta a la IA → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.

Actividad 1 — Comprobar datos. Pregunta a una IA cuántas mujeres han ganado el Nobel hasta 2020 y quién es la persona más joven en ganarlo. Compara con los datos (58 y Malala Yousafzai) y con la página oficial de la Fundación Nobel (nobelprize.org). Si hay diferencias, ¿de dónde vienen (fechas de corte, organizaciones, premios repetidos)?

Actividad 2 — Revisar un gráfico. Pide a una IA el código de un mapa coroplético de los premios por país. ¿Usa códigos ISO? ¿Aclara que cuenta laureados por país de nacimiento? Compara con la documentación de plotly.express.choropleth.

Actividad 3 — Sesgo en las conclusiones. Muestra a una IA la tabla de mujeres por categoría y pídele una conclusión. ¿Confunde representación con capacidad? Reescribe la conclusión siguiendo la sección 7.


13. 🛠️ Correcciones técnicas al material original

Archivo Afirmación o comportamiento del curso Observación Tratamiento
585–587 "Number of Prizes" en los gráficos Cuentan laureados: 962 filas, 602 premios (verificado) Nota técnica de la sección 2
589 La caja muestra "la media" Muestra la mediana; showmeans=True agrega la media Nota técnica de la sección 6
589 regplot / lmplot con lowess=True Requiere statsmodels (RuntimeError sin él, verificado) Nota técnica de la sección 6
589 winning_age = año − año de nacimiento Aproximación: 55 de 934 quedan con 1 año de más (verificado) Nota técnica de la sección 6
584 Actualizar plotly en Colab Hoy normalmente innecesario; si se actualiza, reiniciar la sesión Nota técnica de la sección 1
586 Color dogerblue en el reto Es dodgerblue Nota técnica de la sección 3
587 Francia supera a Alemania en Paz y Literatura Cierto, y también en Economía (4 contra 1, verificado) Sección 4
588 update_layout con títulos de ejes en el sunburst No tiene efecto (no hay ejes) Nota técnica de la sección 5
585 6,2 % de mujeres Sobre 934 personas; sobre 962 filas sería 6,0 % Nota técnica de la sección 2
584 laureate_type distingue personas de organizaciones 4 personas del premio de Paz están marcadas como Organization (verificado) Nota técnica de la sección 1
584 denomenator; "starburst chart"; "wards" (por wars); "Massachusets" Erratas: denominator, sunburst, wars, Massachusetts —

14. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Testamento de Nobel, categorías, qué se aprende, cuaderno y CSV 583 Sección 0 Preparar el cuaderno — Sección 8
Actualizar plotly en Colab; exploración; duplicados; NaN y sus causas; to_datetime; share_pct 584 Sección 1 Retos 1–3 Organizaciones; map Secciones 8 y 10
Formular preguntas; dona por sexo; primeras mujeres; repetidores; categorías con Aggrnyl; Economía; barras por sexo 585 Sección 2 Retos 1–7 Mujeres por década; premios contra laureados Secciones 8 y 9
Premios por año, media móvil, np.arange para marcas, dos ejes, invert_yaxis, guerras 586 Sección 3 Retos 1 y 2 Anotaciones; reindex Sección 8
Columna de país, top 20, coroplético con ISO, barras por país y categoría, merge, acumulado con cumsum 587 Sección 4 Retos 1–4 País de la organización; per cápita; animación Secciones 8, 9 y 10
Top instituciones, ciudades de investigación y de nacimiento, sunburst 588 Sección 5 Retos 1–4 Sunburst por categoría; unificar ciudades Secciones 8 y 10
winning_age, nlargest/nsmallest, describe, histplot, regplot con lowess, boxplot, lmplot con row y hue 589 Secciones 6 y 7 Retos 1–6 Menores de 30; sexo; showmeans Secciones 8 y 10
Resumen del día 590 Sección 6 — — Sección 8
(No existe en el curso original) — Sección 7 — Una base de datos, varias historias Secciones 8 y 10
(No existe en el curso original) — — — América Latina en el Nobel Sección 9
(No existe en el curso original) — — — Laboratorio de Datos Abiertos, etapa 4 Sección 10

15. 📦 Evidencias de aprendizaje del Día 79

  • Tus 3 preguntas iniciales y el cuaderno con el análisis completo.
  • Los 8 gráficos de la exposición, con sus conclusiones.
  • El análisis de América Latina en el Nobel (proyecto extra).
  • La etapa 4 del Laboratorio de Datos Abiertos.
  • Cuaderno de errores: to_numeric con fracciones, .dt sobre texto, dropna que borra categorías enteras, laureados contra premios, denominador de los porcentajes, columna de país equivocada, coroplético sin ISO, columns renombradas por posición, lowess sin statsmodels, media contra mediana.