0%
Matplotlib: Crea Visualizaciones Profesionales en Python

Matplotlib: Crea Visualizaciones Profesionales en Python

Guía completa de Matplotlib para crear curvas, nubes de puntos, barras e histogramas en Python con la interfaz orientada a objetos, subplots y exportación.

I

InSkillCoach

· min

Matplotlib: Crea Visualizaciones Profesionales en Python

Matplotlib es la biblioteca de visualización histórica e imprescindible del ecosistema Python. Permite producir todos los tipos de gráficos clásicos (curvas, nubes de puntos, barras, histogramas) con un control total sobre cada elemento de la figura, y exportar el resultado en formatos adecuados tanto para la web como para la impresión. Esta guía presenta la anatomía de una figura, la interfaz orientada a objetos recomendada, los principales tipos de gráficos y las técnicas de personalización que marcan la diferencia entre un gráfico improvisado y una visualización profesional.

Anatomía de una figura: Figure y Axes

Basta con entender dos objetos para desbloquear toda la biblioteca:

  • La Figure es el contenedor global: la «hoja de papel» que sostiene todo lo demás (tamaño, resolución, fondo).
  • Los Axes representan una zona de trazado: un sistema de ejes con sus graduaciones, sus etiquetas y sus curvas. Una figura puede contener uno o varios Axes.

Dentro de un Axes se encuentran los ejes x e y (xaxis, yaxis), las graduaciones (ticks), las etiquetas (labels), el título, la leyenda y los artistas (líneas, puntos, barras).

import matplotlib.pyplot as plt
import numpy as np

# Una figura que contiene un único Axes
fig, ax = plt.subplots(figsize=(8, 5))

print(type(fig))   # <class 'matplotlib.figure.Figure'>
print(type(ax))    # <class 'matplotlib.axes._axes.Axes'>

Interfaz pyplot vs interfaz orientada a objetos

Matplotlib ofrece dos estilos de escritura:

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

# Estilo pyplot (a la MATLAB): un estado global implícito
plt.plot(x, np.sin(x))
plt.title("Sinus")
plt.xlabel("x")
plt.show()

# Estilo orientado a objetos: se manipulan explícitamente fig y ax
fig, ax = plt.subplots()
ax.plot(x, np.sin(x))
ax.set_title("Sinus")
ax.set_xlabel("x")
plt.show()

Recomendación: adopta la interfaz orientada a objetos. El estilo pyplot es aceptable para un trazado rápido en una línea, pero en cuanto una figura contiene varios subgráficos o se construye dentro de una función, el estado global de pyplot se convierte en fuente de errores. La interfaz orientada a objetos hace explícito qué Axes recibe cada comando, y es el estilo utilizado en la documentación oficial moderna. Todos los ejemplos de esta guía la utilizan.

Curvas con plot

x = np.linspace(0, 10, 200)

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(x, np.sin(x), label="sin(x)", color="tab:blue", linewidth=2)
ax.plot(x, np.cos(x), label="cos(x)", color="tab:orange",
        linestyle="--", linewidth=2)
ax.plot(x[::20], np.sin(x[::20]), "o", color="tab:blue",
        markersize=6, label="points mesurés")

ax.set_title("Fonctions trigonométriques")
ax.set_xlabel("x (radians)")
ax.set_ylabel("Amplitude")
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()

Los estilos de línea más habituales: "-" (continua), "--" (guiones), ":" (punteada), "-." (mixta). Los marcadores: "o" (círculo), "s" (cuadrado), "^" (triángulo), "+", "x".

Nubes de puntos con scatter

scatter permite variar el tamaño y el color de cada punto, lo que codifica hasta cuatro variables en un mismo gráfico.

rng = np.random.default_rng(42)
x = rng.random(80)
y = 2 * x + rng.normal(0, 0.2, 80)
tailles = rng.integers(20, 200, 80)
couleurs = y - 2 * x                     # desviación respecto al modelo

fig, ax = plt.subplots(figsize=(8, 5))
nuage = ax.scatter(x, y, s=tailles, c=couleurs, cmap="viridis",
                   alpha=0.7, edgecolors="black", linewidths=0.5)

fig.colorbar(nuage, ax=ax, label="Résidu")
ax.set_title("Nuage de points avec taille et couleur variables")
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()

Diagramas de barras

langages = ["Python", "SQL", "JavaScript", "Rust"]
popularite = [48, 35, 30, 12]

fig, ax = plt.subplots(figsize=(8, 5))
barres = ax.bar(langages, popularite, color="tab:blue", edgecolor="black")

# Etiquetar cada barra con su valor
ax.bar_label(barres, fmt="%d %%")

ax.set_title("Popularité des langages (exemple)")
ax.set_ylabel("Part des répondants (%)")
ax.set_ylim(0, 55)
plt.show()

# Barras horizontales: más legibles cuando las categorías tienen nombres largos
fig, ax = plt.subplots(figsize=(8, 4))
ax.barh(langages, popularite, color="tab:green")
ax.set_xlabel("Part des répondants (%)")
plt.show()

Para barras agrupadas, desplaza las posiciones con un desfase numérico:

positions = np.arange(len(langages))
largeur = 0.35
annee_1 = [45, 33, 32, 8]
annee_2 = [48, 35, 30, 12]

fig, ax = plt.subplots(figsize=(8, 5))
ax.bar(positions - largeur / 2, annee_1, largeur, label="2025")
ax.bar(positions + largeur / 2, annee_2, largeur, label="2026")
ax.set_xticks(positions)
ax.set_xticklabels(langages)
ax.legend()
plt.show()

Histogramas

El histograma visualiza la distribución de una variable continua.

rng = np.random.default_rng(0)
donnees = rng.normal(loc=170, scale=8, size=1000)   # alturas simuladas

fig, ax = plt.subplots(figsize=(8, 5))
valeurs, bords, patches = ax.hist(donnees, bins=30, color="tab:blue",
                                  edgecolor="white", alpha=0.85)

ax.axvline(donnees.mean(), color="red", linestyle="--",
           label=f"Moyenne = {donnees.mean():.1f} cm")
ax.set_title("Distribution des tailles")
ax.set_xlabel("Taille (cm)")
ax.set_ylabel("Effectif")
ax.legend()
plt.show()

# density=True normaliza para comparar con una densidad de probabilidad
# bins acepta un entero, una lista de bordes, o "auto"

Gráficos de sectores

Úsalos con moderación (a partir de 5 categorías, es preferible recurrir a barras horizontales):

parts = [45, 25, 20, 10]
etiquettes = ["Cloud", "Data", "Web", "Autres"]

fig, ax = plt.subplots(figsize=(6, 6))
ax.pie(parts, labels=etiquettes, autopct="%1.1f%%",
       startangle=90, explode=(0.05, 0, 0, 0),
       colors=["tab:blue", "tab:orange", "tab:green", "tab:gray"])
ax.set_title("Répartition du chiffre d'affaires")
plt.show()
# autopct muestra los porcentajes, explode separa la primera porción

Subplots y cuadrículas de gráficos

plt.subplots(filas, columnas) crea una cuadrícula de Axes en una sola llamada.

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

fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# axes es un array NumPy 2D de objetos Axes

axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title("sin(x)")

axes[0, 1].plot(x, np.cos(x), color="tab:orange")
axes[0, 1].set_title("cos(x)")

axes[1, 0].plot(x, np.sin(2 * x), color="tab:green")
axes[1, 0].set_title("sin(2x)")

axes[1, 1].plot(x, np.exp(-x) * np.sin(5 * x), color="tab:red")
axes[1, 1].set_title("Oscillation amortie")

fig.suptitle("Grille de quatre graphiques", fontsize=14)
fig.tight_layout()      # ajusta los espaciados para evitar solapamientos
plt.show()

# Ejes compartidos: práctico para comparar series
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(8, 6))

Para diseños irregulares (un gráfico ancho arriba y dos pequeños abajo), explora fig.add_gridspec o plt.subplot_mosaic.

Personalización: títulos, leyendas, colores, estilos

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(x, np.sin(x), label="signal")

# Títulos y etiquetas con control tipográfico
ax.set_title("Titre principal", fontsize=14, fontweight="bold")
ax.set_xlabel("Temps (s)", fontsize=11)
ax.set_ylabel("Amplitude (V)", fontsize=11)

# Leyenda: posición y aspecto
ax.legend(loc="upper right", frameon=True, shadow=False)

# Límites y graduaciones
ax.set_xlim(0, 2 * np.pi)
ax.set_xticks([0, np.pi, 2 * np.pi])
ax.set_xticklabels(["0", "π", "2π"])

# Cuadrícula discreta
ax.grid(True, linestyle=":", alpha=0.5)

# Suprimir los bordes superfluos (estilo limpio)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.show()

Las hojas de estilo permiten cambiar la apariencia global en una sola línea:

print(plt.style.available[:5])
# ['Solarize_Light2', '_classic_test_patch', '_mpl-gallery', '_mpl-gallery-nogrid', 'bmh']

plt.style.use("seaborn-v0_8-whitegrid")   # estilo sobrio adecuado para informes

# O localmente, sin modificar el estado global:
with plt.style.context("ggplot"):
    fig, ax = plt.subplots()
    ax.plot(x, np.sin(x))

Para los colores, es preferible la paleta con nombres tab: (tab:blue, tab:orange, …): está diseñada para ser distinguible y legible, también para personas con daltonismo en gran medida.

Anotaciones

x = np.linspace(0, 10, 200)
y = np.exp(-x / 3) * np.sin(2 * x)

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(x, y)

# Texto simple posicionado en coordenadas de los datos
ax.text(6, 0.4, "Zone amortie", fontsize=11, color="gray")

# Anotación con flecha hacia un punto concreto
i_max = y.argmax()
ax.annotate(f"Maximum ({x[i_max]:.2f}, {y[i_max]:.2f})",
            xy=(x[i_max], y[i_max]),          # punta de la flecha
            xytext=(3, 0.8),                  # posición del texto
            arrowprops=dict(arrowstyle="->", color="black"))

# Líneas de referencia
ax.axhline(0, color="black", linewidth=0.8)
ax.axvline(x[i_max], color="red", linestyle=":", alpha=0.6)
plt.show()

Escalas logarítmicas

Imprescindibles en cuanto los datos abarcan varios órdenes de magnitud.

n = np.logspace(1, 6, 50)          # de 10 a 1 000 000
temps_lineaire = n * 1e-6
temps_quadratique = n ** 2 * 1e-9

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(n, temps_lineaire, label="O(n)")
ax.plot(n, temps_quadratique, label="O(n²)")

ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlabel("Taille de l'entrée n")
ax.set_ylabel("Temps (s)")
ax.set_title("Complexités comparées (échelle log-log)")
ax.legend()
ax.grid(True, which="both", alpha=0.3)   # cuadrícula en graduaciones mayores y menores
plt.show()

# Atajos: ax.semilogy(...), ax.semilogx(...), ax.loglog(...)

Guardado con savefig

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(x, np.sin(x))

# PNG de alta resolución para la web o las diapositivas
fig.savefig("graphique.png", dpi=200, bbox_inches="tight")

# SVG o PDF: vectoriales, perfectos para la impresión y los artículos
fig.savefig("graphique.svg", bbox_inches="tight")
fig.savefig("graphique.pdf", bbox_inches="tight")

# Fondo transparente para integrarlo en diapositivas
fig.savefig("graphique_transparent.png", dpi=200, transparent=True)

Hay dos parámetros que conviene recordar: dpi controla la resolución de los formatos de mapa de bits (de 150 a 300 para un resultado nítido), y bbox_inches="tight" elimina los márgenes blancos sobrantes. Llama a savefig antes de plt.show(), ya que show puede vaciar la figura en algunos entornos.

Integración con Pandas

Los DataFrames exponen un método .plot que delega en Matplotlib y devuelve un objeto Axes, que después puedes personalizar con normalidad.

import pandas as pd

df = pd.DataFrame({
    "mois": pd.date_range("2026-01-01", periods=6, freq="MS"),
    "ventes": [120, 135, 150, 145, 170, 190],
    "couts": [100, 110, 118, 120, 130, 140],
}).set_index("mois")

# El DataFrame traza directamente sobre un Axes existente
fig, ax = plt.subplots(figsize=(8, 5))
df.plot(ax=ax, marker="o")

ax.set_title("Ventes et coûts mensuels")
ax.set_ylabel("k€")
ax.legend(["Ventes", "Coûts"])
plt.show()

# Otros atajos: df.plot.bar(), df.plot.hist(), df.plot.scatter(x=..., y=...)

Unas palabras sobre Seaborn

Seaborn es una capa superior de Matplotlib orientada a la estadística: produce en una línea gráficos que requerirían decenas de líneas de Matplotlib puro (diagramas de caja por categoría, matrices de correlación, regresiones con intervalo de confianza).

import seaborn as sns

tips = sns.load_dataset("tips")

fig, ax = plt.subplots(figsize=(8, 5))
sns.boxplot(data=tips, x="day", y="total_bill", hue="sex", ax=ax)
ax.set_title("Distribution des additions par jour")
plt.show()

Como Seaborn devuelve objetos Matplotlib, todo lo que has aprendido en esta guía (Axes, títulos, savefig) sigue siendo aplicable. En la práctica, muchos analistas utilizan Seaborn para la exploración rápida y Matplotlib puro para las figuras finales con un control más fino.

Buenas prácticas

  • Utiliza la interfaz orientada a objetos (fig, ax = plt.subplots()) en todas partes: es explícita, componible y evita los errores de estado global.
  • Etiqueta todo: un gráfico sin título, sin unidades en los ejes y sin leyenda resulta inutilizable fuera de su contexto de creación.
  • Llama a fig.tight_layout() (o crea la figura con layout="constrained") para evitar que los textos se solapen.
  • Elige el tipo de gráfico adecuado: curva para una evolución, barras para categorías, histograma para una distribución, nube de puntos para una relación.
  • Exporta en formato vectorial (SVG, PDF) para la impresión y los documentos, y en PNG con dpi>=150 para la web.
  • Limita la carga visual: suprime los bordes innecesarios, atenúa la cuadrícula con alpha y reserva los colores vivos para la información importante.
  • Fija la semilla aleatoria en los ejemplos y demostraciones para obtener figuras reproducibles.

Conclusión

Matplotlib se basa en un modelo sencillo, la Figure que contiene Axes, que la interfaz orientada a objetos permite manipular con precisión. Con los pocos tipos de gráficos presentados aquí (curvas, nubes de puntos, barras, histogramas), los subplots y las herramientas de personalización, cubres prácticamente todas las necesidades de visualización en análisis de datos. La integración nativa con Pandas facilita el paso de los datos al gráfico, y Seaborn acelera la exploración estadística. Para profundizar, la galería oficial de Matplotlib es un recurso muy valioso: cada figura va acompañada de su código fuente completo.

InSkillCoach

Acerca de InSkillCoach

Experto en formación y tecnologías

Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.

Certificaciones:

  • AWS Certified Solutions Architect – Professional
  • Certificaciones de Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
817
131

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!