Visualización de Datos en Python con Matplotlib y Seaborn: Guía Práctica (2026)

Guía práctica de visualización de datos en Python con Matplotlib 3.10 y Seaborn 0.13: API orientada a objetos, FacetGrid, paletas accesibles y formatos de exportación para publicación.

Matplotlib y Seaborn: Guía 2026

Actualizado: 29 de mayo de 2026

La visualización de datos en Python con Matplotlib y Seaborn consiste en construir gráficos exploratorios y de publicación combinando la capa de bajo nivel de Matplotlib (control total sobre figuras, ejes y artistas) con la API estadística de alto nivel de Seaborn (gráficos categóricos, distribuciones y relaciones a partir de un DataFrame de pandas). En la práctica, Matplotlib aporta el lienzo y Seaborn los temas estadísticos: juntos cubren el 95% de los casos de uso en ciencia de datos, desde un histograma rápido en Jupyter hasta una figura compuesta para una revista científica.

  • Matplotlib 3.10 (febrero de 2026) introduce constrained_layout como gestor predeterminado y mejora la API orientada a objetos; sigue siendo la base sobre la que se apoyan casi todas las librerías de visualización en Python.
  • Seaborn 0.13.2 añadió la interfaz declarativa seaborn.objects, inspirada en la gramática de gráficos, pero las funciones clásicas (relplot, displot, catplot) siguen siendo las más usadas en producción.
  • Usa siempre la API orientada a objetos (fig, ax = plt.subplots()) en notebooks reutilizables; reserva la API pyplot de estado para exploración rápida.
  • Para datasets superiores a 100k filas, agrega o muestrea antes de graficar: tanto Matplotlib como Seaborn dibujan punto por punto y degradan el rendimiento sin transformación previa.
  • Guarda figuras en SVG o PDF para publicación y en PNG con dpi=300 para presentaciones; nunca exportes a JPEG si tu gráfico contiene texto o líneas finas.

¿Cuál es la diferencia entre Matplotlib y Seaborn?

Matplotlib es una librería de bajo nivel inspirada en MATLAB que dibuja primitivas gráficas (líneas, marcadores, parches, textos) sobre un objeto Figure. Seaborn es una capa de alto nivel construida sobre Matplotlib que toma como entrada un DataFrame de pandas y produce gráficos estadísticos ya tematizados. En otras palabras, Seaborn genera objetos de Matplotlib; no los reemplaza. Esto significa que cualquier ajuste fino (cambiar el ticker del eje, añadir una anotación, modificar la transparencia) se hace con la API de Matplotlib, incluso si el gráfico se creó con sns.boxplot().

En mi experiencia revisando notebooks académicos y de equipos de datos, la mejor heurística es la siguiente: si la pregunta empieza con «¿cómo se distribuye…?» o «¿cómo se relaciona X con Y agrupado por Z?», empieza con Seaborn porque resuelve la agregación, el color por categoría y la leyenda automáticamente. Si la pregunta es «necesito esta figura exacta para la revista», dibuja en Matplotlib desde el inicio para tener control de cada punto. Esa división mental ahorra horas frente a tratar a Seaborn como un sustituto completo.

CaracterísticaMatplotlib 3.10Seaborn 0.13
Nivel de abstracciónBajo (lienzo + primitivas)Alto (gráfico estadístico)
Entrada típicaArrays de NumPyDataFrame de pandas (long-form)
Curva de aprendizajePronunciada (mucha API)Suave (pocas funciones, muchos parámetros)
Personalización extremaTotalLimitada (delega a Matplotlib)
Gráficos estadísticos integradosManualesHistogramas, KDE, regresión, boxplots categóricos listos
Mejor paraFiguras de publicación, dashboardsEDA rápido, informes estadísticos

Instalación y configuración del entorno (2026)

Las versiones actuales a fecha de mayo de 2026 son Matplotlib 3.10 (febrero de 2026) y Seaborn 0.13.2. Ambas requieren Python ≥3.10 y son compatibles con NumPy 2.x. Si trabajas en un entorno gestionado con uv o pip, instala así:

# Entorno virtual nuevo con uv (recomendado en 2026)
uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install "matplotlib>=3.10" "seaborn>=0.13.2" pandas numpy jupyterlab

# Alternativa clásica con pip
python -m pip install --upgrade matplotlib seaborn pandas

En Jupyter, activa el backend interactivo con %matplotlib inline para imágenes estáticas o %matplotlib widget (requiere ipympl) para zoom y pan. Para scripts puros, fija el backend antes de importar pyplot:

import matplotlib
matplotlib.use("Agg")   # backend sin GUI, ideal para CI y servidores
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd

sns.set_theme(style="whitegrid", context="notebook")

La anatomía de una figura: Figure, Axes y Artists

Antes de dibujar nada, conviene entender el modelo mental que Matplotlib heredó de MATLAB pero formalizó orientado a objetos. Una Figure es el contenedor de nivel superior (la hoja de papel). Dentro hay uno o varios Axes, que son el sistema de coordenadas donde realmente se dibuja: cada Axes tiene su eje X, su eje Y, su título, sus ticks y sus etiquetas. Todo lo demás (líneas, marcadores, anotaciones, leyenda) es un Artist que se añade a un Axes.

Esta jerarquía es importante porque casi cualquier error 404 visual («mi gráfico no muestra el título», «la leyenda está duplicada», «el ylim no cambia») se resuelve preguntándote a qué objeto le estás hablando. Si llamas a plt.title("X"), modificas el Axes actual según el estado global de pyplot. Si llamas a ax.set_title("X"), modificas exactamente el Axes que tienes en la variable ax. En cuadernos con varias figuras, la segunda forma es la única segura.

El tutorial oficial de inicio rápido de Matplotlib formaliza esta distinción y recomienda siempre la API orientada a objetos para código que vaya a mantenerse. Es la convención que sigue toda esta guía.

Tu primer gráfico con Matplotlib paso a paso

Bien, vamos al grano. Empecemos con un ejemplo reproducible que muestra la API orientada a objetos completa, incluyendo etiquetas, leyenda y formato de ticks. Vamos a graficar el crecimiento simulado de dos series temporales:

import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(seed=42)
meses = np.arange(1, 25)
serie_a = 100 * np.exp(0.05 * meses) + rng.normal(0, 5, size=meses.size)
serie_b = 100 * np.exp(0.03 * meses) + rng.normal(0, 5, size=meses.size)

fig, ax = plt.subplots(figsize=(8, 4.5), layout="constrained")
ax.plot(meses, serie_a, marker="o", label="Producto A", linewidth=2)
ax.plot(meses, serie_b, marker="s", label="Producto B", linewidth=2)

ax.set_xlabel("Mes")
ax.set_ylabel("Ingresos (miles €)")
ax.set_title("Crecimiento acumulado por producto (24 meses)")
ax.grid(True, alpha=0.3)
ax.legend(loc="upper left", frameon=False)

fig.savefig("crecimiento.png", dpi=150, bbox_inches="tight")
plt.show()

Tres detalles merecen comentario estadístico. Primero, el seed=42 y el uso de default_rng en lugar del antiguo np.random.normal garantiza reproducibilidad y es la API recomendada desde NumPy 1.17 (Harris et al., 2020, Nature). Segundo, layout="constrained" es ahora el gestor de espaciado preferido y reemplaza a tight_layout en figuras con leyendas externas o varios subplots. Tercero, guardar con bbox_inches="tight" evita que la leyenda quede cortada en el PNG.

Seaborn con DataFrames de pandas: gráficos estadísticos

Seaborn brilla cuando trabajas con datos long-form (formato largo, una observación por fila) en un DataFrame. La librería incluye varios datasets de ejemplo para practicar; usaremos penguins, que sustituyó a iris en la documentación oficial por motivos éticos y de diversidad estadística:

import seaborn as sns
import matplotlib.pyplot as plt

sns.set_theme(style="ticks", context="talk")
penguins = sns.load_dataset("penguins").dropna()

g = sns.relplot(
    data=penguins,
    x="bill_length_mm",
    y="flipper_length_mm",
    hue="species",
    style="sex",
    col="island",
    kind="scatter",
    height=4, aspect=0.9,
    palette="colorblind",
)
g.set_axis_labels("Longitud del pico (mm)", "Longitud de la aleta (mm)")
g.set_titles("Isla: {col_name}")
plt.show()

Con cuatro líneas hemos producido tres scatterplots facetados por isla, coloreados por especie y diferenciados por sexo, con paleta accesible para daltónicos. Reproducir esto en Matplotlib puro requeriría unas 40 líneas de código gestionando subplots y leyendas. Esa es la propuesta de valor de Seaborn: gramática estadística resumida en parámetros.

Las tres funciones figure-level que cubren el 90% de los casos son relplot (relaciones: scatter y line), displot (distribuciones: histograma, KDE, ECDF) y catplot (categóricos: box, violin, strip, bar). Cada una devuelve un objeto FacetGrid que puedes seguir personalizando con métodos de Matplotlib accediendo a g.axes.

Paletas de color y estilos accesibles

El color en visualización no es decorativo: codifica información. Una mala paleta puede inducir lecturas erróneas y excluir al ~8% de hombres con algún tipo de discromatopsia. Seaborn integra paletas colorblind-safe que deberías preferir por defecto:

# Paleta cualitativa accesible (Tol y Wong, 2011)
sns.set_palette("colorblind")

# Paleta secuencial (datos ordenados, p. ej. magnitudes)
sns.color_palette("rocket", as_cmap=True)

# Paleta divergente (datos con punto medio significativo, p. ej. correlación)
sns.color_palette("vlag", as_cmap=True)

Como regla práctica: usa cualitativa (colorblind, deep, Set2) para categorías sin orden; secuencial (rocket, mako, viridis) para magnitudes; y divergente (vlag, coolwarm) cuando el cero o la media son significativos. jet y rainbow son perceptualmente desiguales y deberían evitarse —es el consenso de la comunidad desde el artículo clásico de Borland y Taylor (IEEE CG&A, 2007).

Subplots, FacetGrid y figuras compuestas

Para combinar varios paneles en una sola figura, hay tres mecanismos según el origen de los datos. Con Matplotlib puro, usa plt.subplots(nrows, ncols) o el más potente fig.subplot_mosaic, disponible desde 3.3 y que permite layouts irregulares con cadenas ASCII:

fig, axd = plt.subplot_mosaic(
    '''
    AAB
    CCB
    ''',
    figsize=(8, 5),
    layout="constrained",
)
axd["A"].plot(meses, serie_a)
axd["B"].hist(serie_a, bins=15, orientation="horizontal")
axd["C"].plot(meses, serie_b, color="tab:orange")

Para gráficos derivados de un mismo DataFrame, FacetGrid es más declarativo: indica una variable categórica como col o row y Seaborn dibuja un panel por nivel. Si necesitas un grid de pares (todas las variables contra todas), sns.pairplot() resuelve la matriz scatter clásica en una línea, ideal para EDA cuando estás preparando datos para machine learning y quieres detectar correlaciones, outliers y relaciones no lineales antes de modelar.

La nueva interfaz seaborn.objects (gramática de gráficos)

Desde la versión 0.12, Seaborn incluye un submódulo experimental seaborn.objects que reimplementa la gramática de gráficos de Wilkinson (la misma base teórica de ggplot2 en R). El estilo es composicional: empiezas con un Plot, le sumas Marks (qué dibujar) y Stats (qué calcular). Es la dirección de futuro de la librería según el changelog oficial de Seaborn 0.13.

import seaborn.objects as so

(
    so.Plot(penguins, x="body_mass_g", y="bill_depth_mm", color="species")
      .add(so.Dot(alpha=0.6))
      .add(so.Line(), so.PolyFit(order=1))
      .label(x="Masa corporal (g)", y="Profundidad del pico (mm)")
      .theme({"axes.spines.right": False, "axes.spines.top": False})
      .show()
)

La ventaja es que mantienes un único objeto declarativo y compones transformaciones (suavizado, jitter, agregación) como si fueran capas. La desventaja: la API sigue marcada como experimental y algunos marks avanzados aún no están portados. Para producción en 2026 recomiendo seguir usando las funciones clásicas; para exploración y para usuarios que vienen de ggplot2, seaborn.objects reduce la fricción mental notablemente.

Cómo guardar figuras para publicación

La elección del formato de salida importa más de lo que parece. Para presentaciones y web, usa PNG con dpi=150–300; para artículos científicos y documentos vectoriales, usa PDF o SVG porque permiten zoom infinito sin pixelado. Nunca uses JPEG con texto: el algoritmo de compresión genera halos visibles alrededor de las letras y líneas finas.

# Para artículo científico (LaTeX)
fig.savefig("figura1.pdf", bbox_inches="tight", pad_inches=0.02)

# Para web y blogs
fig.savefig("figura1.png", dpi=200, bbox_inches="tight",
            facecolor="white")   # evita fondo transparente en modo oscuro

# Para edición posterior en Inkscape o Illustrator
fig.savefig("figura1.svg", bbox_inches="tight")

Si estás creando varias figuras en un pipeline reproducible, conviene encapsular los parámetros de estilo en un módulo. Esta práctica encaja con el flujo que describimos en nuestra guía de pipelines de scikit-learn: el código de visualización se trata como cualquier otro paso reproducible, con configuraciones versionadas y semillas fijas. Para conjuntos derivados de arrays de NumPy vectorizados, mantén también las dimensiones del array y los nombres de ejes alineados con las etiquetas del gráfico.

Errores comunes y cómo resolverlos

Tras años revisando notebooks ajenos, estos son los cinco fallos que veo una y otra vez:

  1. Mezclar API pyplot y orientada a objetos. Si dentro del mismo bloque usas plt.title() y luego ax.set_title(), una de las llamadas no surte efecto porque apuntan a Axes distintos.
  2. Olvidar plt.close(fig) en bucles. Generar 1000 figuras sin cerrarlas satura la memoria y dispara RuntimeWarning: More than 20 figures have been opened.
  3. Graficar datasets de millones de puntos sin agregar. Para series temporales largas, remuestrea antes (df.resample("D").mean()) o usa datashader para grandes volúmenes.
  4. Usar el formato wide cuando Seaborn espera long. Convierte con df.melt() antes de llamar a relplot o catplot.
  5. Confundir la leyenda con un Artist editable. La leyenda de Seaborn se posiciona con g.legend.set_bbox_to_anchor(...) en FacetGrid, no con plt.legend(loc=...).

Preguntas frecuentes

¿Qué es mejor para empezar, Matplotlib o Seaborn?

Para principiantes con un DataFrame de pandas, Seaborn es más rápido porque resuelve agregación y leyenda automáticamente. Pero antes o después necesitarás Matplotlib para personalizar detalles, así que conviene aprender ambas en paralelo. Empieza con Seaborn para exploración y añade Matplotlib cuando un cliente o revisor te pida un ajuste específico.

¿Cómo instalo Seaborn y Matplotlib en Python?

Con pip: pip install matplotlib seaborn. Con conda: conda install -c conda-forge matplotlib seaborn. En 2026 recomiendo usar uv pip install dentro de un entorno virtual creado con uv venv, ya que la resolución de dependencias es entre 10 y 100 veces más rápida que pip puro.

¿Se puede usar Seaborn sin Matplotlib?

No. Seaborn es una capa construida sobre Matplotlib: cada gráfico de Seaborn devuelve internamente objetos Figure y Axes de Matplotlib. Por eso, al instalar Seaborn con pip, Matplotlib se instala automáticamente como dependencia.

¿Cuál es la diferencia entre relplot, displot y catplot?

Las tres son funciones figure-level: relplot dibuja relaciones entre variables numéricas (scatter, line), displot dibuja distribuciones de una variable (hist, KDE, ECDF) y catplot dibuja gráficos categóricos (box, violin, bar, strip). Todas comparten interfaz: data, x, y, hue, col y row para facetar.

¿Cómo hago que mis gráficos sean accesibles para personas daltónicas?

Usa sns.set_palette("colorblind") como paleta cualitativa por defecto y prefiere paletas perceptualmente uniformes como viridis, rocket o mako para escalas continuas. Adicionalmente, combina color con forma o patrón cuando codifiques categorías, así la información sigue siendo legible incluso en escala de grises o con discromatopsia.

Dr. Elena Vasquez
Sobre el Autor Dr. Elena Vasquez

Data scientist with a PhD in computational statistics. Translates papers into pandas one notebook at a time.