Curso de Python · Bloque 09 de 09

Gráficos

Matplotlib, elección de gráficos, paneles, anotación y guardado.

75–105 min Descargar notebook ↓

Audiencia. Personas que ya han visto algo de programación y quieren repasar Python de forma práctica.
Prerrequisitos. NumPy y análisis básico de datos. Requiere matplotlib.
Duración orientativa. 60–90 minutos, incluida la práctica.

Objetivos de aprendizaje

  • Construir figuras con la interfaz orientada a objetos de Matplotlib.
  • Elegir entre línea, dispersión, barras e histograma.
  • Crear subgráficos, etiquetas, leyendas y anotaciones legibles.
  • Guardar figuras y aplicar principios básicos de comunicación visual.

Itinerario

  1. Modelo figura–ejes
  2. Líneas
  3. Dispersión
  4. Barras
  5. Histogramas
  6. Subgráficos
  7. Anotación y guardado
  8. Práctica

Cómo trabajar: ejecuta las celdas en orden, predice el resultado antes de verlo y modifica los ejemplos. Cada bloque termina con un ejercicio, un punto de partida y una solución posible.

1. Figura y ejes

Matplotlib distingue Figure (lienzo completo) y Axes (zona con coordenadas). La interfaz orientada a objetos —fig, ax = plt.subplots()— escala mejor que depender del estado global. tight_layout o constrained_layout evita solapamientos.

Python
import numpy as np
import matplotlib.pyplot as plt

plt.style.use("seaborn-v0_8-whitegrid")
COLOR_PRINCIPAL = "#3366CC"
COLOR_SECUNDARIO = "#E45756"
rng = np.random.default_rng(2026)

fig, ax = plt.subplots(figsize=(6, 2.8), constrained_layout=True)
ax.text(0.5, 0.5, "Figure contiene uno o más Axes", ha="center", va="center", fontsize=13)
ax.set_axis_off()
plt.show()
Resultado
Resultado gráfico de la celda

2. Gráficos de líneas

Una línea muestra evolución sobre una variable ordenada, normalmente tiempo. Incluye unidades, título informativo y leyenda solo cuando haya varias series.

Python
meses = np.arange(1, 13)
ventas_2025 = np.array([20, 22, 21, 25, 28, 30, 29, 31, 35, 34, 38, 42])
ventas_2026 = ventas_2025 * np.array([1.05, 1.02, 1.08, 1.1, 1.07, 1.12, 1.11, 1.08, 1.13, 1.15, 1.12, 1.1])

fig, ax = plt.subplots(figsize=(7, 3.5), constrained_layout=True)
ax.plot(meses, ventas_2025, marker="o", label="2025")
ax.plot(meses, ventas_2026, marker="o", label="2026")
ax.set(title="Ventas mensuales", xlabel="Mes", ylabel="Miles de euros")
ax.set_xticks(meses)
ax.legend(frameon=False)
plt.show()
Resultado
Resultado gráfico de la celda

3. Dispersión

Un diagrama de dispersión muestra relación entre dos variables numéricas. Color y tamaño pueden codificar dimensiones adicionales, pero conviene no sobrecargar. Una barra de color debe indicar la escala.

Python
horas = rng.uniform(1, 12, 45)
experiencia = rng.integers(0, 6, 45)
puntuacion = 45 + 4 * horas + 2 * experiencia + rng.normal(0, 6, 45)

fig, ax = plt.subplots(figsize=(6.5, 3.8), constrained_layout=True)
puntos = ax.scatter(horas, puntuacion, c=experiencia, s=55, cmap="viridis", alpha=0.8, edgecolor="white")
ax.set(title="Práctica y resultado", xlabel="Horas de práctica", ylabel="Puntuación")
fig.colorbar(puntos, ax=ax, label="Años de experiencia")
plt.show()
Resultado
Resultado gráfico de la celda

4. Barras

Las barras comparan magnitudes por categoría. El eje cuantitativo suele comenzar en cero. Ordenar categorías ayuda a encontrar patrones; las barras horizontales funcionan bien con etiquetas largas.

Python
categorias = np.array(["Documentación", "Práctica", "Vídeos", "Tutoría"])
valoraciones = np.array([8.7, 9.2, 7.4, 8.1])
orden = np.argsort(valoraciones)

fig, ax = plt.subplots(figsize=(6.5, 3.5), constrained_layout=True)
barras = ax.barh(categorias[orden], valoraciones[orden], color=COLOR_PRINCIPAL)
ax.bar_label(barras, fmt="%.1f", padding=3)
ax.set(title="Valoración de recursos", xlabel="Puntuación sobre 10", xlim=(0, 10))
plt.show()
Resultado
Resultado gráfico de la celda

5. Histogramas

Un histograma aproxima la distribución de una variable continua. El número de intervalos cambia la lectura: prueba varios y justifica la elección. Para comparar grupos, considera paneles separados o transparencia moderada.

Python
tiempos = rng.lognormal(mean=2.3, sigma=0.35, size=500)

fig, ax = plt.subplots(figsize=(6.5, 3.5), constrained_layout=True)
ax.hist(tiempos, bins="auto", color=COLOR_PRINCIPAL, alpha=0.8, edgecolor="white")
ax.axvline(np.median(tiempos), color=COLOR_SECUNDARIO, linestyle="--", label=f"Mediana: {np.median(tiempos):.1f}")
ax.set(title="Distribución del tiempo de resolución", xlabel="Minutos", ylabel="Frecuencia")
ax.legend(frameon=False)
plt.show()
Resultado
Resultado gráfico de la celda

6. Subgráficos y escalas compartidas

plt.subplots(filas, columnas) devuelve una cuadrícula de ejes. Comparte escalas con sharex o sharey cuando facilite comparaciones. No metas demasiada información en un solo panel.

Python
x = np.linspace(0, 2 * np.pi, 200)

fig, axes = plt.subplots(1, 2, figsize=(9, 3.2), sharey=True, constrained_layout=True)
axes[0].plot(x, np.sin(x), color=COLOR_PRINCIPAL)
axes[0].set(title="Seno", xlabel="x", ylabel="amplitud")
axes[1].plot(x, np.cos(x), color=COLOR_SECUNDARIO)
axes[1].set(title="Coseno", xlabel="x")
fig.suptitle("Funciones periódicas")
plt.show()
Resultado
Resultado gráfico de la celda

7. Matrices y mapas de calor

imshow representa una matriz con color. Usa un mapa secuencial para magnitudes y uno divergente cuando existe un centro significativo. La barra de color comunica la escala.

Python
correlaciones = np.array([
    [1.00, 0.72, -0.18],
    [0.72, 1.00, -0.35],
    [-0.18, -0.35, 1.00],
])
etiquetas = ["Práctica", "Nota", "Errores"]

fig, ax = plt.subplots(figsize=(4.8, 3.8), constrained_layout=True)
imagen = ax.imshow(correlaciones, cmap="coolwarm", vmin=-1, vmax=1)
ax.set_xticks(range(3), etiquetas, rotation=30, ha="right")
ax.set_yticks(range(3), etiquetas)
for i in range(3):
    for j in range(3):
        ax.text(j, i, f"{correlaciones[i, j]:.2f}", ha="center", va="center")
fig.colorbar(imagen, ax=ax, label="Correlación")
ax.set_title("Relaciones entre variables")
plt.show()
Resultado
Resultado gráfico de la celda

8. Anotación y guardado

Anota solo puntos que apoyen el mensaje. Al guardar, elige formato: PNG para mapa de bits, SVG/PDF para vector. dpi afecta a formatos raster. Aquí guardamos en memoria para mantener el notebook autocontenido.

Python
from io import BytesIO

x = np.arange(1, 8)
y = np.array([3, 5, 4, 8, 7, 11, 10])
maximo = int(np.argmax(y))

fig, ax = plt.subplots(figsize=(6, 3.2), constrained_layout=True)
ax.plot(x, y, marker="o", color=COLOR_PRINCIPAL)
ax.annotate(
    "Máximo",
    xy=(x[maximo], y[maximo]),
    xytext=(-35, 25),
    textcoords="offset points",
    arrowprops={"arrowstyle": "->"},
)
ax.set(title="Serie con punto destacado", xlabel="Semana", ylabel="Valor")

buffer_png = BytesIO()
fig.savefig(buffer_png, format="png", dpi=150, bbox_inches="tight")
print(f"PNG generado en memoria: {buffer_png.tell() / 1024:.1f} KiB")
plt.show()
Resultado
PNG generado en memoria: 37.7 KiB
Resultado gráfico de la celda

Principios y errores habituales

  • Elige el gráfico según la pregunta, no por decoración.
  • Incluye unidades y contexto; evita títulos genéricos.
  • No uses un arcoíris ni dependas solo del color para distinguir series.
  • Evita ejes truncados en barras, 3D ornamental y leyendas redundantes.
  • Reduce tinta innecesaria y destaca una conclusión.

Extensión opcional: explora escalas logarítmicas, fechas, estilos propios y animación. Para gráficos estadísticos de alto nivel, investiga Seaborn.

Práctica · Mini panel de rendimiento

Con las series mensuales dadas, crea una figura con dos paneles:

  1. línea de ingresos mensuales;
  2. barras de incidencias mensuales;
  3. título general, unidades, marcadores y colores accesibles;
  4. una anotación en el máximo de ingresos.
Tu turno
import numpy as np
import matplotlib.pyplot as plt

meses = np.arange(1, 7)
ingresos = np.array([21, 24, 23, 29, 31, 28])
incidencias = np.array([14, 11, 13, 8, 6, 9])

# TODO: crea `fig, axes` y dibuja los dos paneles.
Ver una solución posible
Solución
import numpy as np
import matplotlib.pyplot as plt

meses = np.arange(1, 7)
ingresos = np.array([21, 24, 23, 29, 31, 28])
incidencias = np.array([14, 11, 13, 8, 6, 9])
indice_max = int(np.argmax(ingresos))

fig, axes = plt.subplots(1, 2, figsize=(9.5, 3.5), constrained_layout=True)
axes[0].plot(meses, ingresos, marker="o", color=COLOR_PRINCIPAL)
axes[0].annotate(
    "máximo",
    xy=(meses[indice_max], ingresos[indice_max]),
    xytext=(8, -28),
    textcoords="offset points",
    arrowprops={"arrowstyle": "->"},
)
axes[0].set(title="Ingresos", xlabel="Mes", ylabel="Miles de euros", xticks=meses)

axes[1].bar(meses, incidencias, color=COLOR_SECUNDARIO)
axes[1].set(title="Incidencias", xlabel="Mes", ylabel="Número", xticks=meses)
fig.suptitle("Rendimiento del primer semestre")
plt.show()
Resultado
Resultado gráfico de la celda

Comprobación rápida

  • [ ] Distingo figura y ejes y uso la interfaz orientada a objetos.
  • [ ] Sé elegir un gráfico básico según la pregunta.
  • [ ] Puedo etiquetar, anotar y guardar una visualización legible.

← Anterior · Volver al índice