Pandas y visualización: explorar un dataset real
Los 17 bloques de código de la teoría, en el mismo orden. Cópialos y córrelos en tu Python o en Colab; en la ficha Teoría está la explicación de cada uno.
De arreglos a tablas: Series y DataFrame
import numpy as np
import pandas as pd
# Un DataFrame se puede armar con un diccionario: una lista por columna
curso = pd.DataFrame({
"nombre": ["Ana", "Luis", "Sara", "Iván", "Rosa"],
"edad": [15, 16, 15, 17, 16],
"ciudad": ["La Paz", "Sucre", "Oruro", "La Paz", "Tarija"],
"nota": [78, 65, 91, 70, 84],
})
print(curso)
print()
# Una columna es una Series: un arreglo con nombre e índice
notas = curso["nota"]
print(type(notas).__name__, notas.shape)
print(notas.to_numpy())
print("Promedio:", notas.mean())Cargar el dataset y mirarlo
import seaborn as sns
# Descarga el CSV la primera vez: necesita internet
pinguinos = sns.load_dataset("penguins")
pinguinos = pinguinos.rename(columns={
"species": "especie",
"island": "isla",
"bill_length_mm": "pico_largo_mm",
"bill_depth_mm": "pico_alto_mm",
"flipper_length_mm": "aleta_mm",
"body_mass_g": "masa_g",
"sex": "sexo",
})
# map cambia cada valor según el diccionario
pinguinos["sexo"] = pinguinos["sexo"].map(
{"Male": "macho", "Female": "hembra"})
print(pinguinos.shape)
print(pinguinos.head())pinguinos.info()print(pinguinos.describe().round(1))
print()
print(pinguinos["especie"].value_counts())Elegir filas y columnas
print(pinguinos.loc[0, "especie"])
print(pinguinos.loc[0:2, ["especie", "masa_g"]])
print(pinguinos.iloc[0:2, 0:3])
# Filtrar con una condición
pesados = pinguinos[pinguinos["masa_g"] > 5500]
print(len(pesados), "pingüinos pesan más de 5,5 kg:")
print(pesados["especie"].value_counts())
# Varias condiciones: & (y), | (o), cada una entre paréntesis
dream = pinguinos[(pinguinos["isla"] == "Dream")
& (pinguinos["aleta_mm"] > 200)]
print(len(dream), "pingüinos de Dream con aleta de más de 200 mm")Valores faltantes
print(pinguinos.isna().sum())
medidas = ["pico_largo_mm", "pico_alto_mm", "aleta_mm", "masa_g"]
# any(axis=1): True en las filas con al menos un faltante
sin_medidas = pinguinos[pinguinos[medidas].isna().any(axis=1)]
print(sin_medidas[["especie", "isla"] + medidas])pinguinos = pinguinos.dropna(subset=medidas)
print(pinguinos.shape)
print("Sin sexo anotado:", pinguinos["sexo"].isna().sum())Agrupar y resumir: groupby
por_especie = pinguinos.groupby("especie")
print(por_especie["masa_g"].mean().round(0))
print()
# agg aplica varios resúmenes a la vez
print(por_especie[["aleta_mm", "masa_g"]].agg(["mean", "std"]).round(1))
print()
# Cuántos pingüinos hay de cada especie en cada isla
print(pinguinos.groupby(["especie", "isla"]).size())Unir tablas: merge
especies = pd.DataFrame({
"especie": ["Adelie", "Chinstrap", "Gentoo"],
"nombre_comun": ["pingüino de Adelia", "pingüino barbijo",
"pingüino papúa"],
"cientifico": ["Pygoscelis adeliae", "Pygoscelis antarcticus",
"Pygoscelis papua"],
})
# how="left": quedan todas las filas de la tabla de la izquierda
completa = pinguinos.merge(especies, on="especie", how="left")
print(completa.shape)
print(completa[["especie", "nombre_comun", "masa_g"]]
.sample(4, random_state=0))Graficar: la figura y los ejes
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.hist(pinguinos["aleta_mm"], bins=25, color="C0")
ax.set_xlabel("Largo de la aleta (mm)")
ax.set_ylabel("Cantidad de pingüinos")
ax.set_title("Las aletas de 342 pingüinos")
plt.show()fig, ejes = plt.subplots(3, 1, figsize=(4.8, 4.8), sharex=True,
sharey=True)
intervalos = range(170, 236, 3) # los mismos en los tres
for ax, (especie, grupo) in zip(ejes, pinguinos.groupby("especie")):
color = "C1" if especie == "Gentoo" else "C0"
ax.hist(grupo["aleta_mm"], bins=intervalos, color=color)
ax.text(0.02, 0.75, especie, transform=ax.transAxes)
ejes[-1].set_xlabel("Largo de la aleta (mm)")
fig.supylabel("Cantidad de pingüinos", fontsize=15)
plt.show()Dispersión: la relación entre dos medidas
colores = {"Adelie": "C0", "Chinstrap": "C2", "Gentoo": "C1"}
ax = sns.scatterplot(data=pinguinos, x="pico_largo_mm",
y="pico_alto_mm", hue="especie",
style="especie", palette=colores)
ax.set_xlabel("Largo del pico (mm)")
ax.set_ylabel("Alto del pico (mm)")
ax.set_title("Cada especie forma su propia nube")
# Un poco de espacio arriba para la leyenda, en una sola fila
ax.set_ylim(13, 24)
ax.legend(title=None, fontsize=13, ncols=3, loc="upper left",
columnspacing=0.8, handletextpad=0.1)
plt.show()Diagrama de caja y valores atípicos
def atipicos(serie):
"""True donde el valor cae fuera de los bigotes (regla 1,5·RIC)."""
q1, q3 = serie.quantile(0.25), serie.quantile(0.75)
ric = q3 - q1
return (serie < q1 - 1.5 * ric) | (serie > q3 + 1.5 * ric)
print("Atípicos con todos juntos:", atipicos(pinguinos["masa_g"]).sum())
# transform aplica la función a cada especie por separado
es_atipico = pinguinos.groupby("especie")["masa_g"].transform(atipicos)
print(pinguinos.loc[es_atipico, ["especie", "isla", "masa_g", "sexo"]])
orden = ["Adelie", "Chinstrap", "Gentoo"]
ax = sns.boxplot(data=pinguinos, x="especie", y="masa_g", order=orden,
color="C3", linecolor="C0", saturation=1,
showfliers=False)
# Los atípicos, dibujados aparte para resaltarlos
sns.stripplot(data=pinguinos[es_atipico], x="especie", y="masa_g",
order=orden, color="C1", size=9, jitter=False, ax=ax)
ax.set_xlabel("")
ax.set_ylabel("Masa (g)")
ax.set_title("Masa por especie")
plt.show()Correlación y mapa de calor
from matplotlib.colors import LinearSegmentedColormap
corr = pinguinos[medidas].corr()
print(corr.round(2))
# \n parte la etiqueta en dos líneas
nombres = ["Pico\nlargo", "Pico\nalto", "Aleta", "Masa"]
blanco_tinta = LinearSegmentedColormap.from_list("bt", ["white", "C0"])
fig, ax = plt.subplots(figsize=(4.8, 4.2))
sns.heatmap(corr.abs(), annot=corr, fmt=".2f", cmap=blanco_tinta,
vmin=0, vmax=1, square=True, cbar=False,
xticklabels=nombres, yticklabels=nombres, ax=ax)
ax.tick_params(axis="both", labelrotation=0)
ax.set_title("Correlación entre las medidas")
plt.show()Barras y líneas
conteo = pd.crosstab(pinguinos["isla"], pinguinos["especie"])
print(conteo)
ax = conteo.plot.bar(stacked=True, color=["C0", "C2", "C1"], rot=0)
ax.set_xlabel("Isla")
ax.set_ylabel("Cantidad de pingüinos")
ax.set_title("¿Quién vive en cada isla?")
ax.legend(title=None, fontsize=13)
plt.show()vuelos = sns.load_dataset("flights")
print(vuelos.head(3))
# "1949" + "-" + "Jan" → fecha; %Y es el año y %b, el mes abreviado
texto = vuelos["year"].astype(str) + "-" + vuelos["month"].astype(str)
vuelos["fecha"] = pd.to_datetime(texto, format="%Y-%b")
print(vuelos["fecha"].dtype)
mayor = vuelos.loc[vuelos["passengers"].idxmax()]
print("Mes con más pasajeros:", mayor["fecha"].strftime("%m/%Y"),
"con", mayor["passengers"], "mil")
fig, ax = plt.subplots()
ax.plot(vuelos["fecha"], vuelos["passengers"], color="C0")
ax.scatter(mayor["fecha"], mayor["passengers"], color="C1", s=60,
zorder=3)
ax.set_ylabel("Pasajeros (miles)")
ax.set_title("Pasajeros de vuelos por mes")
plt.show()El cuarteto de Anscombe
anscombe = sns.load_dataset("anscombe")
resumen = anscombe.groupby("dataset").agg(
media_x=("x", "mean"), media_y=("y", "mean"),
desv_x=("x", "std"), desv_y=("y", "std"))
resumen["correlacion"] = [g["x"].corr(g["y"])
for _, g in anscombe.groupby("dataset")]
print(resumen.round(2))
fig, ejes = plt.subplots(2, 2, figsize=(4.8, 4.8), sharex=True,
sharey=True)
x_recta = np.array([3, 20])
for ax, (nombre, g) in zip(ejes.flat, anscombe.groupby("dataset")):
ax.scatter(g["x"], g["y"], color="C0", s=30)
# La recta que mejor se ajusta (la vas a calcular en el tema 6)
pendiente, corte = np.polyfit(g["x"], g["y"], 1)
ax.plot(x_recta, pendiente * x_recta + corte, color="C2",
linewidth=1.6)
ax.set_title(nombre)
plt.show()