Matplotlib fill_between: relleno condicional, bandas y arreglos con isin()
Publicado el
Actualizado el

fill_between rellena el área entre dos curvas en y (o entre una curva y una línea base). Es la herramienta habitual para bandas de confianza, resaltados “por encima/por debajo” y regiones destacadas en gráficos de líneas.
Sintaxis rápida
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 200)
y1 = np.sin(x)
y2 = np.cos(x)
fig, ax = plt.subplots()
ax.plot(x, y1, label="sin")
ax.plot(x, y2, label="cos")
ax.fill_between(x, y1, y2, color="0.7", alpha=0.4)
ax.legend()
plt.show()| Objetivo | Patrón |
|---|---|
| Rellenar entre dos series | ax.fill_between(x, y1, y2, alpha=0.3) |
| Rellenar bajo una línea | ax.fill_between(x, 0, y, alpha=0.3) |
| Solo donde se cumple una condición | where=(y1 > y2) (misma longitud que x) |
| Filtro por índice / categoría | where=df.index.isin([...]) — no el in de Python |
| Bordes más suaves en condiciones | interpolate=True |
- Runcell Science: alternativa open source a Claude Science para investigación
- Cómo evitar que Mac se duerma: mantener Codex, Claude Code y agentes de IA corriendo
- OpenClaw vs ZeroClaw vs Pi Agent vs Nanobot: ¿Qué stack de agentes de IA deberías elegir en 2026?
- Cómo Claude Code analiza notebooks de Jupyter en Data Science: lo que sí hace y lo que no
- Claude Code routines: qué son y por qué importan los cron jobs de agentes de IA
- Claude Code Desktop Bypass Permissions: cómo activarlo
- Cómo Construir Dos Agentes en Python con el Protocolo A2A de Google - Tutorial Paso a Paso
- Las 10 principales bibliotecas de visualización de datos en Python que crecen en 2025
Qué hace fill_between
Matplotlib dibuja un polígono de (x, y1) a (x, y2) y lo rellena. x, y1 y y2 deben tener la misma longitud (o y1/y2 pueden ser escalares).
Con esa sola idea cubres la mayoría de tareas de “resalta esta región” sin inventar patches a mano.
Relleno básico entre dos curvas
Este ejemplo rellena todo el área entre seno y coseno:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 300)
y1 = np.sin(x)
y2 = np.cos(x)
fig, ax = plt.subplots(figsize=(7.2, 4.2))
ax.plot(x, y1, label="sin(x)", color="#2563eb", lw=2)
ax.plot(x, y2, label="cos(x)", color="#dc2626", lw=2)
ax.fill_between(x, y1, y2, color="#94a3b8", alpha=0.45)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend(loc="upper right")
plt.show()
Controles útiles:
alpha— deja el relleno semitransparente para que las líneas se leancolor/facecolor— color del rellenolinewidth=0— oculta el borde del polígono si pelea con el estilo de las líneaslabel=— incluye el relleno en la leyenda cuando representa una categoría real
Rellenos condicionales con where=
Pasa un array booleano (misma longitud que x) para pintar solo parte de la banda. Usa dos llamadas si quieres dos colores:
fig, ax = plt.subplots(figsize=(7.2, 4.2))
ax.plot(x, y1, label="sin(x)", color="#2563eb", lw=2)
ax.plot(x, y2, label="cos(x)", color="#dc2626", lw=2)
ax.fill_between(
x, y1, y2,
where=(y1 > y2),
interpolate=True,
color="#22c55e",
alpha=0.35,
label="sin > cos",
)
ax.fill_between(
x, y1, y2,
where=(y1 <= y2),
interpolate=True,
color="#f97316",
alpha=0.30,
label="sin ≤ cos",
)
ax.legend(loc="upper right", ncol=2)
plt.show()
Por qué importa interpolate=True
Sin interpolación, el relleno puede cortar con bordes verticales “a bloques” donde la condición cambia entre puntos de muestreo. interpolate=True estima el cruce para que el resaltado siga las curvas con más limpieza. Prefiérelo en series continuas densas; importa menos en ejes x categóricos dispersos.
Patrón de banda de confianza / error
Un uso muy habitual en la práctica es “media ± incertidumbre”:
t = np.linspace(0, 12, 200)
mean = np.sin(t / 1.5) + 0.15 * t
std = 0.35 + 0.08 * np.abs(np.cos(t))
fig, ax = plt.subplots(figsize=(7.2, 4.2))
ax.plot(t, mean, color="#0f766e", lw=2.2, label="estimate")
ax.fill_between(t, mean - std, mean + std, color="#14b8a6", alpha=0.28, label="±1σ band")
ax.set_xlabel("time")
ax.set_ylabel("value")
ax.legend(loc="upper left")
plt.show()
La misma idea sirve para cuantiles (p10/p90), rangos de pronóstico o envolventes min/max. Dibuja la línea central después o con un z-order más alto si el relleno se impone demasiado.
Diagnóstico: rellenar solo algunas categorías / meses
Gran parte del tráfico de búsqueda llega a esta página tras un intento fallido como:
# Broken ideas
where = plotMonths.index in ["January", "February", "March"] # ValueError / nonsense
where = [m in ["January", "February", "March"] for m in plotMonths.index] # may work, but isin is cleanerQué sale mal
array in listno es elemento a elemento. Los objetos de NumPy/pandas lanzanValueErroro devuelven un único booleano ambiguo.- Necesitas un vector booleano alineado con
x, no un True/False para todo el array.
Arreglo: isin() (o np.isin)
Ejemplo completo y ejecutable con etiquetas de meses categóricos:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"]
df = pd.DataFrame(
{
"A": [12, 14, 13, 15, 18, 17, 16, 19],
"B": [10, 11, 12, 14, 13, 15, 14, 16],
},
index=months,
)
fig, ax = plt.subplots(figsize=(7.2, 4.2))
ax.plot(df.index, df["A"], marker="o", label="series A")
ax.plot(df.index, df["B"], marker="o", label="series B")
ax.fill_between(
np.arange(len(df)),
df["A"],
df["B"],
where=df.index.isin(["Jan", "Feb", "Mar"]),
color="#38bdf8",
alpha=0.35,
label="Q1 highlight",
)
ax.set_xticks(range(len(df)), df.index)
ax.legend(loc="upper left")
plt.show()
Notas:
- Con un DatetimeIndex, prefiere máscaras booleanas como
(df.index.month <= 3)o.isinsobre etiquetas normalizadas. - Cuando el eje x son etiquetas categóricas, pasar posiciones enteras a
fill_between(como arriba) suele ser más fiable que categorías string. - Mantén la longitud de
whereigual al array x que pasas.
Trampas frecuentes
| Trampa | Síntoma | Arreglo |
|---|---|---|
| Longitudes distintas | ValueError: ... not the same size | Alinea x, y1, y2, where |
Usar el in de Python sobre Index/array | ValueError o relleno vacío | index.isin([...]) / np.isin |
Olvidar alpha | El relleno tapa las líneas | alpha=0.2–0.4 |
| Leyenda fuera recortada en PNG | Leyenda cortada | Ver leyenda fuera del gráfico + bbox_inches="tight" |
| Bordes feos en condiciones | Regiones a bloques | interpolate=True |
En gráficos multi-serie, combina rellenos con estilos de línea claros de múltiples gráficos de líneas. Si la figura se siente apretada tras anotaciones y bandas, ajusta el tamaño de figura antes de pelear con la leyenda.
fill_between vs fill_betweenx
fill_between(x, y1, y2)— relleno vertical entre dos valores y a lo largo de x (el más habitual).fill_betweenx(y, x1, x2)— relleno horizontal entre dos valores x a lo largo de y (útil para perfiles verticales, franjas de densidad, rangos estilo tornado).
La misma idea de where= aplica a ambos.
FAQ
Conclusión
ax.fill_between(x, y1, y2) es una API pequeña con mucho impacto visual: comparar series, mostrar incertidumbre y marcar regiones. Usa where= cuando la historia sea condicional, isin/máscaras booleanas en lugar del in de Python para etiquetas, y mantén alpha bajo para que los datos sigan mandando.
Related Guides
- Matplotlib legend outside plot — mantener legibles las etiquetas de series junto a regiones rellenas
- Matplotlib multiple line plots — gráficos multi-serie que combinan bien con rellenos
- Matplotlib figure size — espacio para bandas, etiquetas y leyendas
- Matplotlib savefig cuts off labels — exportar sin recortes
- Matplotlib subplots — paneles para varias series con relleno
- Matplotlib annotations and text — etiquetar las regiones que acabas de rellenar