Matplotlib Subplots: Crear Figuras Multipanel con plt.subplots()
Updated on
Las figuras de un solo panel rara vez son suficientes para análisis reales. Necesita comparar distribuciones lado a lado, mostrar un gráfico de dispersión junto a sus residuos, o presentar cuatro métricas en un diseño de dashboard. Sin subplots, crearía figuras separadas que pierden su relación visual cuando se presentan juntas.
La función plt.subplots() de Matplotlib crea figuras multipanel con ejes compartidos, tamaño consistente y layouts flexibles. Esta guía cubre todo, desde cuadrículas básicas hasta layouts asimétricos avanzados.
Subplots Básicos
Fila Única
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].plot(x, np.sin(x))
axes[0].set_title('Seno')
axes[1].plot(x, np.cos(x), color='orange')
axes[1].set_title('Coseno')
axes[2].plot(x, np.tan(x), color='green')
axes[2].set_ylim(-5, 5)
axes[2].set_title('Tangente')
plt.tight_layout()
plt.show()Diseño en Cuadrícula
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
x = np.linspace(0, 10, 100)
axes[0, 0].plot(x, x, 'b-')
axes[0, 0].set_title('Lineal')
axes[0, 1].plot(x, x**2, 'r-')
axes[0, 1].set_title('Cuadrático')
axes[1, 0].plot(x, np.sqrt(x), 'g-')
axes[1, 0].set_title('Raíz Cuadrada')
axes[1, 1].plot(x, np.log(x + 1), 'm-')
axes[1, 1].set_title('Logarítmico')
plt.tight_layout()
plt.show()Ejes Compartidos
Compartir ejes asegura escalas consistentes para comparación:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1.5, 1000)
# Compartir eje X
fig, axes = plt.subplots(2, 1, figsize=(8, 6), sharex=True)
axes[0].hist(data1, bins=30, color='steelblue', alpha=0.7)
axes[0].set_ylabel('Conteo')
axes[0].set_title('Distribución A')
axes[1].hist(data2, bins=30, color='coral', alpha=0.7)
axes[1].set_ylabel('Conteo')
axes[1].set_xlabel('Valor')
axes[1].set_title('Distribución B')
plt.tight_layout()
plt.show()import matplotlib.pyplot as plt
import numpy as np
# Compartir ambos ejes X e Y
fig, axes = plt.subplots(2, 3, figsize=(12, 8), sharex=True, sharey=True)
for i, ax in enumerate(axes.flat):
data = np.random.randn(100)
ax.hist(data, bins=20, color=f'C{i}', alpha=0.7)
ax.set_title(f'Muestra {i+1}')
plt.tight_layout()
plt.show()Control de Espaciado y Layout
tight_layout()
Ajusta automáticamente el espaciado para evitar superposición:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# ... agregar gráficos ...
plt.tight_layout() # Auto-corregir espaciado
plt.show()subplots_adjust()
Control manual del espaciado:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
plt.subplots_adjust(
left=0.1, # Margen izquierdo
right=0.95, # Margen derecho
top=0.92, # Margen superior
bottom=0.08, # Margen inferior
wspace=0.3, # Espacio horizontal entre subplots
hspace=0.4, # Espacio vertical entre subplots
)
plt.show()Agregar un Supertítulo
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
fig.suptitle('Funciones Trigonométricas', fontsize=16, fontweight='bold')
x = np.linspace(0, 2 * np.pi, 100)
for ax, func, name in zip(axes, [np.sin, np.cos, np.tan], ['sin', 'cos', 'tan']):
ax.plot(x, func(x))
ax.set_title(name)
plt.tight_layout()
plt.show()GridSpec para Layouts Asimétricos
Cuando necesita paneles de diferentes tamaños, use GridSpec:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(2, 3, figure=fig)
# Panel grande izquierdo (abarca 2 filas)
ax1 = fig.add_subplot(gs[:, 0])
ax1.plot(np.random.randn(100).cumsum())
ax1.set_title('Serie Temporal (2 filas)')
# Paneles superiores derechos
ax2 = fig.add_subplot(gs[0, 1])
ax2.bar(['A', 'B', 'C'], [3, 7, 5])
ax2.set_title('Gráfico de Barras')
ax3 = fig.add_subplot(gs[0, 2])
ax3.scatter(np.random.randn(50), np.random.randn(50))
ax3.set_title('Dispersión')
# Panel inferior ancho (abarca 2 columnas)
ax4 = fig.add_subplot(gs[1, 1:])
ax4.hist(np.random.randn(500), bins=30, color='coral')
ax4.set_title('Histograma (2 columnas)')
plt.tight_layout()
plt.show()subplot2grid para Layout Basado en Posición
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(12, 8))
# (filas, cols), (fila_inicio, col_inicio), rowspan, colspan
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=2)
ax2 = plt.subplot2grid((3, 3), (0, 2), rowspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 0))
ax4 = plt.subplot2grid((3, 3), (1, 1))
ax5 = plt.subplot2grid((3, 3), (2, 0), colspan=3)
ax1.set_title('Superior Ancho')
ax2.set_title('Derecho Alto')
ax3.set_title('Centro Izquierda')
ax4.set_title('Centro Medio')
ax5.set_title('Inferior Ancho Completo')
plt.tight_layout()
plt.show()Iterar Sobre Subplots
import matplotlib.pyplot as plt
import numpy as np
# Aplanar array de axes para iteración fácil
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
datasets = [np.random.randn(100) for _ in range(6)]
colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c']
for ax, data, color, i in zip(axes.flat, datasets, colors, range(6)):
ax.hist(data, bins=20, color=color, alpha=0.7)
ax.set_title(f'Conjunto de Datos {i+1}')
ax.set_xlabel('Valor')
ax.set_ylabel('Conteo')
plt.tight_layout()
plt.show()Eliminar Subplots Vacíos
Cuando tiene menos gráficos que posiciones en la cuadrícula:
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
data_count = 5 # Solo 5 conjuntos de datos para 6 espacios
for i, ax in enumerate(axes.flat):
if i < data_count:
ax.plot(np.random.randn(50).cumsum())
ax.set_title(f'Gráfico {i+1}')
else:
ax.set_visible(False) # Ocultar subplot vacío
plt.tight_layout()
plt.show()Exploración Interactiva Multipanel
Para exploración visual rápida donde quiere comparar diferentes vistas del mismo conjunto de datos interactivamente, PyGWalker (opens in a new tab) le permite crear dashboards arrastrando y soltando columnas en Jupyter -- sin necesidad de código de subplots:
import pandas as pd
import pygwalker as pyg
df = pd.read_csv('your_data.csv')
walker = pyg.walk(df)FAQ
¿Cómo creo subplots en Matplotlib?
Use fig, axes = plt.subplots(filas, columnas) para crear una figura con una cuadrícula de subplots. Acceda a ejes individuales con axes[fila, columna] para cuadrículas o axes[i] para filas/columnas únicas. Siempre llame a plt.tight_layout() al final para evitar etiquetas superpuestas.
¿Cómo comparto ejes entre subplots?
Pase sharex=True o sharey=True a plt.subplots(). Por ejemplo, fig, axes = plt.subplots(2, 1, sharex=True) hace que ambos subplots compartan la misma escala del eje x. Esto es útil para comparar distribuciones o series temporales.
¿Cómo creo subplots de diferentes tamaños?
Use matplotlib.gridspec.GridSpec para layouts asimétricos. Cree una cuadrícula y use notación de slice para abarcar múltiples celdas: ax = fig.add_subplot(gs[0, :2]) crea un subplot que abarca las dos primeras columnas de la primera fila.
¿Cómo ajusto el espaciado entre subplots?
Llame a plt.tight_layout() para espaciado automático. Para control manual, use plt.subplots_adjust(wspace=0.3, hspace=0.4) donde wspace es el espaciado horizontal y hspace es el espaciado vertical.
¿Cómo agrego un título sobre todos los subplots?
Use fig.suptitle('Mi Título', fontsize=16) para agregar un supertítulo sobre todos los subplots. Puede necesitar ajustar el margen superior con plt.subplots_adjust(top=0.92) o llamar a plt.tight_layout() para evitar superposición.
Conclusión
El plt.subplots() de Matplotlib es la base para figuras multipanel. Úselo con (filas, columnas) para cuadrículas regulares, sharex/sharey para escalas consistentes, tight_layout() para espaciado automático, y GridSpec para layouts asimétricos. Para iteración rápida, aplane el array de axes con axes.flat y recorra. Estos patrones cubren la gran mayoría de las necesidades de visualización multipanel.