FacetGrid para cuadrículas condicionales

Avanzado
Seaborn
Seaborn
Actualizado: 18/04/2026

Qué es FacetGrid

FacetGrid es la clase de bajo nivel que gestiona las cuadrículas de subgráficos en Seaborn. Las funciones figure-level (relplot, displot, catplot) la utilizan internamente para organizar los subgráficos condicionados a variables categóricas. Sin embargo, crear un FacetGrid directamente permite un nivel de personalización mucho mayor, ya que se puede mapear cualquier función de visualización, incluidas las de Matplotlib, SciPy o bibliotecas de terceros.

Mientras que relplot o catplot están limitados a las funciones de Seaborn, FacetGrid permite crear cuadrículas con cualquier función que acepte arrays o DataFrames como entrada.

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

sns.set_theme(style="ticks")

tips = sns.load_dataset("tips")
penguins = sns.load_dataset("penguins")

Creación básica de FacetGrid

# FacetGrid con variable de columna
g = sns.FacetGrid(tips, col="time", height=5, aspect=1.0)
plt.show()

El FacetGrid se crea vacío — define la cuadrícula pero aún no mapea ningún gráfico. Se usa map() o map_dataframe() para poblar los subgráficos.

map(): mapear funciones con arrays

map() recibe una función y los nombres de las columnas que se pasan como arrays posicionales:

g = sns.FacetGrid(tips, col="time", hue="smoker", palette="Set1", height=5)
g.map(sns.scatterplot, "total_bill", "tip", alpha=0.6)
g.add_legend()
g.set_axis_labels("Importe ($)", "Propina ($)")
g.set_titles("{col_name}")
plt.show()

map_dataframe(): mapear funciones con data=

map_dataframe() pasa el subconjunto del DataFrame completo a la función mediante el parámetro data=. Es necesario para funciones que aceptan data=:

g = sns.FacetGrid(penguins.dropna(), col="island", height=4, aspect=1.0)
g.map_dataframe(
    sns.scatterplot,
    x="bill_length_mm",
    y="bill_depth_mm",
    hue="species",
    palette="Set2",
    alpha=0.7
)
g.add_legend(title="Especie")
g.set_titles("Isla: {col_name}")
g.set_axis_labels("Longitud del pico (mm)", "Profundidad del pico (mm)")
plt.show()

Cuadrículas de filas y columnas

g = sns.FacetGrid(
    tips,
    col="time",
    row="smoker",
    hue="sex",
    palette=["#3498db", "#e74c3c"],
    height=4,
    aspect=1.2,
    margin_titles=True   # títulos en los márgenes derecho/superior
)
g.map_dataframe(sns.scatterplot, x="total_bill", y="tip", alpha=0.6)
g.set_titles(col_template="{col_name}", row_template="{row_name}")
g.set_axis_labels("Importe ($)", "Propina ($)")
g.add_legend(title="Género")
g.fig.suptitle("Propina según turno y condición de fumador", y=1.02)
plt.tight_layout()
plt.show()

col_wrap para muchas categorías

diamonds = sns.load_dataset("diamonds").sample(3000, random_state=42)

g = sns.FacetGrid(
    diamonds,
    col="cut",
    col_wrap=3,          # máximo 3 columnas
    height=3.5,
    aspect=1.0
)
g.map_dataframe(
    sns.histplot,
    x="price",
    bins=20,
    color="steelblue",
    alpha=0.7
)
g.set_titles("{col_name}")
g.set_axis_labels("Precio ($)", "Conteo")
g.fig.suptitle("Distribución de precios por tipo de corte", y=1.02)
plt.tight_layout()
plt.show()

Mapear funciones de Matplotlib

La gran ventaja de FacetGrid es que puede mapear cualquier función de Matplotlib:

def plot_scatter_with_regression(x, y, **kwargs):
    """Función personalizada: scatter + línea de regresión con Matplotlib."""
    from scipy import stats
    ax = plt.gca()
    ax.scatter(x, y, **kwargs)
    slope, intercept, r, p, se = stats.linregress(x, y)
    x_line = np.linspace(x.min(), x.max(), 100)
    ax.plot(x_line, slope * x_line + intercept, color="red", linewidth=2)
    ax.set_title(f"r={r:.2f}, p={p:.3f}", fontsize=9)

g = sns.FacetGrid(penguins.dropna(), col="species", height=4, aspect=1.0)
g.map(plot_scatter_with_regression, "bill_length_mm", "body_mass_g",
      alpha=0.5, s=30, color="steelblue")
g.set_axis_labels("Longitud del pico (mm)", "Masa corporal (g)")
g.set_titles("{col_name}")
g.fig.suptitle("Regresión personalizada por especie", y=1.05)
plt.tight_layout()
plt.show()

Personalización post-creación

g = sns.FacetGrid(tips, col="time", height=5, aspect=1.2)
g.map_dataframe(sns.boxplot, x="day", y="total_bill",
                palette="Set2", order=["Thur", "Fri", "Sat", "Sun"])

# Personalizar cada subgráfico individualmente
for ax, titulo in zip(g.axes.flatten(), ["Almuerzo", "Cena"]):
    ax.set_title(titulo, fontsize=14, fontweight="bold")
    ax.set_xlabel("Día de la semana", fontsize=11)
    ax.set_ylabel("Importe total ($)", fontsize=11)
    ax.set_xticklabels(["Jue", "Vie", "Sáb", "Dom"])
    ax.axhline(
        y=tips["total_bill"].mean(),
        color="red",
        linestyle="--",
        alpha=0.5,
        label="Media global"
    )

g.axes[0, 0].legend(loc="upper left")
g.fig.suptitle("Distribución del importe por turno", fontsize=15, y=1.02)
plt.tight_layout()
plt.show()

refline: añadir líneas de referencia

g = sns.FacetGrid(penguins.dropna(), col="species", height=4)
g.map_dataframe(sns.scatterplot, x="flipper_length_mm", y="body_mass_g",
                alpha=0.6, color="steelblue")

# Añadir líneas de referencia a todos los subgráficos
g.refline(x=penguins["flipper_length_mm"].mean(),
           y=penguins["body_mass_g"].mean(),
           color="red", linestyle="--", linewidth=1.5)

g.set_titles("{col_name}")
g.set_axis_labels("Longitud de aleta (mm)", "Masa corporal (g)")
g.fig.suptitle("Masa vs aleta con medias globales de referencia", y=1.05)
plt.tight_layout()
plt.show()

Cuándo usar FacetGrid en lugar de relplot, displot o catplot

Las funciones figure-level de Seaborn son, en realidad, envoltorios de FacetGrid con una función de dibujo fija. Si solo necesitas dibujar un scatterplot, un histplot o un boxplot facetado, casi siempre es más rápido y legible usar relplot, displot o catplot. Sin embargo, hay tres situaciones en las que FacetGrid sigue siendo irreemplazable.

La primera es cuando quieres componer varias capas dentro de cada celda. Por ejemplo, superponer un kdeplot sobre un scatterplot, o añadir una línea de media por encima de un histograma. Con FacetGrid puedes llamar a map o map_dataframe tantas veces como capas necesites, y cada una se dibujará sobre la misma cuadrícula.

La segunda es cuando la función de dibujo no es nativa de Seaborn: rutinas de Matplotlib, funciones propias, ajustes de SciPy o gráficos especializados (streamplots, contornos, imágenes). Como ya hemos visto, basta con aceptar **kwargs y usar plt.gca() dentro de la función.

La tercera es cuando necesitas control fino sobre la estructura de los ejes antes de dibujar: compartir un rango concreto, imponer ticks manuales, ajustar aspect ratio o preparar twin axes. FacetGrid te entrega la figura ya construida mediante g.axes, mientras que las funciones figure-level crean la cuadrícula dentro de la llamada y te limitan la intervención previa.

Buenas prácticas y errores comunes

  • Ordena las categorías explícitamente. Seaborn usa el orden alfabético o el de aparición en el DataFrame, que rara vez es el deseado. Usa col_order=, row_order= y hue_order= para fijar la secuencia, o convierte la columna a pd.Categorical(..., ordered=True) antes de crear el grid.
  • Comparte ejes cuando la comparación importa. Por defecto FacetGrid usa sharex=True, sharey=True, lo que facilita comparar magnitudes entre celdas. Si cada faceta tiene escalas muy distintas, pon sharex=False para evitar que las más pequeñas queden aplastadas.
  • Filtra los valores nulos antes de crear el grid. FacetGrid no los elimina por ti y pueden aparecer facetas vacías o ejes descolocados. Llama a .dropna(subset=[...]) sobre el DataFrame.
  • Cuidado con col_wrap y row a la vez. Son incompatibles: si usas col_wrap, no puedes usar row. Elige una estructura 2D (col + row) o envuelta (col + col_wrap).
  • Evita un número excesivo de facetas. Por encima de 12-16 celdas el gráfico se vuelve ilegible. Si tienes decenas de categorías, considera agrupar las minoritarias en "otros" o usar una interfaz interactiva como Plotly o Streamlit.
  • tight_layout rompe a veces el suptitle. Si el título superior queda cortado, ajusta y=1.03 o reemplaza plt.tight_layout() por g.fig.subplots_adjust(top=0.9) para dejar margen manualmente.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Seaborn es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Seaborn

Explora más contenido relacionado con Seaborn y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Crear cuadrículas de gráficos con sns.FacetGrid(). Mapear funciones de Seaborn y Matplotlib con map() y map_dataframe(). Controlar el número de columnas con col_wrap y el tamaño con height y aspect. Personalizar títulos, etiquetas y leyendas del FacetGrid. Combinar FacetGrid con funciones no nativas de Seaborn.