Subplots no Matplotlib — Guia Completo
Aprenda a criar figuras com múltiplos painéis usando subplots no Matplotlib: plt.subplots(), GridSpec, eixos compartilhados, tamanho e exemplos de salvamento.
Os subplots permitem posicionar vários gráficos dentro de uma única figura do Matplotlib. Em vez de criar janelas separadas para cada gráfico, você os organiza em uma grade — lado a lado, empilhados ou em um layout personalizado — para que o leitor possa comparar dados de relance. Este capítulo abrange desde uma figura simples com dois painéis até layouts de grade flexíveis com eixos compartilhados e expansão por célula.
Antes de começar, instale o Matplotlib se ainda não tiver feito isso:
pip install matplotlib numpySe você é novo na biblioteca, leia primeiro os capítulos de Introdução ao Matplotlib e Primeiros Passos.
A Forma Mais Rápida: plt.subplots()
plt.subplots(nrows, ncols) é o ponto de entrada padrão. Ele retorna uma Figure e um array de objetos Axes.
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))
x = np.linspace(0, 2 * np.pi, 200)
axes[0].plot(x, np.sin(x), color='steelblue')
axes[0].set_title('Sine')
axes[0].set_xlabel('x')
axes[0].set_ylabel('sin(x)')
axes[1].plot(x, np.cos(x), color='darkorange')
axes[1].set_title('Cosine')
axes[1].set_xlabel('x')
axes[1].set_ylabel('cos(x)')
plt.tight_layout()
plt.show()Pontos principais:
figsize=(width, height)define o tamanho da figura em polegadas. O padrão é(6.4, 4.8), que costuma ser pequeno demais para múltiplos painéis.plt.tight_layout()ajusta automaticamente o espaçamento para que títulos e rótulos não se sobreponham. Chame-o logo antes deshow()ousavefig().- Quando
ncols=1enrows=1(o padrão),axesé um único objetoAxes, não um array.
Criando uma Grade 2×2
Passe nrows e ncols para obter um array NumPy 2-D de Axes. Indexe-o com [row, col], onde ambos os índices começam em zero.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 200)
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(10, 8))
# Top-left
ax[0, 0].plot(x, np.sin(x), color='steelblue')
ax[0, 0].set_title('sin(x)')
# Top-right
ax[0, 1].plot(x, np.cos(x), color='darkorange')
ax[0, 1].set_title('cos(x)')
# Bottom-left — clamp tan to avoid ±∞ spikes
y_tan = np.clip(np.tan(x), -10, 10)
ax[1, 0].plot(x, y_tan, color='seagreen')
ax[1, 0].set_title('tan(x) [clipped]')
# Bottom-right
ax[1, 1].plot(x, np.exp(x / np.pi), color='tomato')
ax[1, 1].set_title('exp(x/π)')
plt.suptitle('Trigonometric & Exponential Functions', fontsize=14, y=1.02)
plt.tight_layout()
plt.show()plt.suptitle() adiciona um título geral à figura acima de todos os subplots. O ajuste y=1.02 empurra o título para longe dos títulos dos painéis da linha superior.
Iterando Sobre os Eixos
Para uma grade grande, é mais fácil percorrer o array de eixos achatado do que indexar cada célula manualmente:
import matplotlib.pyplot as plt
import numpy as np
functions = [np.sin, np.cos, np.tan, np.arctan]
names = ['sin', 'cos', 'tan', 'arctan']
colors = ['steelblue', 'darkorange', 'seagreen', 'tomato']
x = np.linspace(-np.pi, np.pi, 300)
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
for ax, fn, name, color in zip(axes.flat, functions, names, colors):
y = fn(x)
y = np.clip(y, -5, 5) # keep tan from dominating the scale
ax.plot(x, y, color=color)
ax.set_title(name)
ax.axhline(0, color='black', linewidth=0.6, linestyle='--')
ax.axvline(0, color='black', linewidth=0.6, linestyle='--')
plt.tight_layout()
plt.show()axes.flat retorna um iterador 1-D independentemente da forma da grade, então o mesmo loop funciona para uma grade 2×2, 3×3 ou qualquer outro layout.
Compartilhando Eixos
Quando os painéis representam a mesma grandeza (mesmo intervalo de x ou mesma escala de y), vincule os eixos para que deslocar ou ampliar um atualize todos:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 4 * np.pi, 400)
# sharex=True links horizontal axes; sharey=True links vertical axes
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6), sharex=True)
ax1.plot(x, np.sin(x), color='steelblue')
ax1.set_ylabel('sin(x)')
ax1.set_title('Shared x-axis example')
ax2.plot(x, np.sin(2 * x), color='darkorange')
ax2.set_ylabel('sin(2x)')
ax2.set_xlabel('x (radians)')
# Hide redundant x-tick labels on the top panel
ax1.tick_params(labelbottom=False)
plt.tight_layout()
plt.show()sharex=True remove os rótulos do eixo x duplicados dos painéis superiores e mantém todos os painéis alinhados. Use sharey=True de forma semelhante quando a escala y deve coincidir entre as colunas.
Desempacotando Eixos Diretamente
Quando o layout é pequeno e conhecido com antecedência, você pode desempacotar o array retornado diretamente em variáveis nomeadas:
import matplotlib.pyplot as plt
import numpy as np
# 1 row, 3 columns → unpack into three named axes
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(13, 4))
x = np.linspace(0, 10, 300)
ax1.plot(x, x ** 0.5, color='steelblue', label='√x')
ax1.set_title('Square Root')
ax1.legend()
ax2.plot(x, np.log1p(x), color='darkorange', label='ln(1+x)')
ax2.set_title('Natural Log')
ax2.legend()
ax3.plot(x, x, color='seagreen', label='x')
ax3.set_title('Linear')
ax3.legend()
plt.tight_layout()
plt.show()Isso é mais limpo do que axes[0], axes[1], axes[2] para layouts curtos. Para uma grade 2-D com duas linhas, aninhe o desempacotamento: (ax1, ax2), (ax3, ax4) = axes.
Layouts Personalizados com GridSpec
plt.subplots() cria apenas grades onde todas as células têm o mesmo tamanho. Para layouts desiguais — um painel de visão geral largo no topo com painéis de detalhe abaixo — use matplotlib.gridspec.GridSpec:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
x = np.linspace(0, 2 * np.pi, 300)
fig = plt.figure(figsize=(10, 8))
gs = gridspec.GridSpec(nrows=2, ncols=3, figure=fig)
# Top row: one wide panel spanning all three columns
ax_top = fig.add_subplot(gs[0, :])
ax_top.plot(x, np.sin(x), color='steelblue', linewidth=2)
ax_top.set_title('Overview — sin(x)')
# Bottom row: three equal panels
ax_bl = fig.add_subplot(gs[1, 0])
ax_bl.plot(x, np.sin(x), color='steelblue')
ax_bl.set_title('sin(x)')
ax_bm = fig.add_subplot(gs[1, 1])
ax_bm.plot(x, np.cos(x), color='darkorange')
ax_bm.set_title('cos(x)')
ax_br = fig.add_subplot(gs[1, 2])
ax_br.plot(x, np.sin(x) * np.cos(x), color='seagreen')
ax_br.set_title('sin(x)·cos(x)')
plt.tight_layout()
plt.show()A sintaxe de fatiamento gs[row, col] espelha o fatiamento do NumPy. gs[0, :] abrange todas as três colunas; gs[1, 0] ocupa apenas a primeira célula na linha 1.
Controlando as Alturas de Linhas e Colunas
GridSpec aceita height_ratios e width_ratios para tornar algumas linhas ou colunas maiores que outras:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
fig = plt.figure(figsize=(9, 7))
gs = gridspec.GridSpec(
2, 2,
height_ratios=[3, 1], # top row is 3× taller than bottom row
width_ratios=[2, 1], # left column is 2× wider than right column
hspace=0.4,
wspace=0.3,
)
x = np.linspace(0, 4 * np.pi, 400)
ax1 = fig.add_subplot(gs[0, 0])
ax1.plot(x, np.sin(x), color='steelblue')
ax1.set_title('Main (tall + wide)')
ax2 = fig.add_subplot(gs[0, 1])
ax2.plot(x, np.cos(x), color='darkorange')
ax2.set_title('Side (tall + narrow)')
ax3 = fig.add_subplot(gs[1, 0])
ax3.plot(x, np.sin(x) ** 2, color='seagreen')
ax3.set_title('Bottom-left (short + wide)')
ax4 = fig.add_subplot(gs[1, 1])
ax4.plot(x, np.cos(x) ** 2, color='tomato')
ax4.set_title('Bottom-right (short + narrow)')
plt.suptitle('Proportional Grid Layout', fontsize=13)
plt.show()hspace e wspace controlam o espaço vertical e horizontal entre os subplots (como uma fração da altura/largura média do subplot).
Definindo um Tamanho de Figura Comum
O parâmetro figsize sempre se refere à figura inteira, não aos subplots individuais. Uma boa regra prática:
| Layout | figsize recomendado |
|---|---|
| 1 linha × 2 colunas | (10, 4) |
| 1 linha × 3 colunas | (13, 4) |
| 2 linhas × 2 colunas | (10, 8) |
| 3 linhas × 3 colunas | (13, 11) |
Você sempre pode ajustar de acordo com seus dados; estes são pontos de partida que deixam espaço para títulos e rótulos.
Adicionando Títulos e Rótulos
Cada objeto Axes tem seu próprio título e rótulos de eixo. A figura como um todo pode ter um supertítulo:
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
x = np.linspace(0, 5, 100)
for ax, power, color in zip(axes, [2, 3], ['steelblue', 'darkorange']):
ax.plot(x, x ** power, color=color)
ax.set_title(f'$x^{power}$') # LaTeX in title
ax.set_xlabel('x')
ax.set_ylabel(f'$x^{power}$')
ax.grid(True, linestyle='--', alpha=0.5)
plt.suptitle('Power Functions', fontsize=14)
plt.tight_layout()
plt.show()Salvando uma Figura com Múltiplos Painéis
Chame plt.savefig() antes de plt.show() (chamar show() primeiro limpa a figura):
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 3, figsize=(13, 4))
x = np.linspace(0, 2 * np.pi, 300)
axes[0].plot(x, np.sin(x), color='steelblue')
axes[0].set_title('sin(x)')
axes[1].plot(x, np.cos(x), color='darkorange')
axes[1].set_title('cos(x)')
axes[2].plot(x, np.sin(x) + np.cos(x), color='seagreen')
axes[2].set_title('sin(x) + cos(x)')
plt.tight_layout()
plt.savefig('trig_panels.png', dpi=150, bbox_inches='tight')
plt.show()bbox_inches='tight' corta qualquer espaço em branco extra ao redor da figura — útil ao incorporar a imagem em um documento ou página web.
Erros Comuns
Esquecer o tight_layout()
Sem tight_layout(), os títulos dos subplots e os rótulos dos eixos de painéis adjacentes frequentemente se sobrepõem. Sempre chame-o antes de show() ou savefig().
Dimensões de índice erradas
plt.subplots(2, 3) retorna um array 2-D; plt.subplots(1, 3) retorna um array 1-D. Tentar usar axes[0, 0] em um array 1-D gera um IndexError. Use axes[0], ou passe squeeze=False para sempre obter um array 2-D:
fig, axes = plt.subplots(1, 3, squeeze=False)
# axes is now shape (1, 3) — always index with [row, col]
axes[0, 0].plot(...)
axes[0, 1].plot(...)
axes[0, 2].plot(...)Supertítulo cortado
plt.suptitle() pode ser cortado pelo tight_layout(). Corrija passando um rect para tight_layout:
plt.tight_layout(rect=[0, 0, 1, 0.95]) # leave 5% at top for suptitleOu use fig.subplots_adjust(top=0.90) em vez disso.
Referência Rápida
| Tarefa | Código |
|---|---|
| Grade 1×2 | fig, (ax1, ax2) = plt.subplots(1, 2) |
| Grade 2×2 | fig, ax = plt.subplots(2, 2) |
| Acessar célula | ax[row, col] |
| Iterar todas as células | for ax in axes.flat: |
| Compartilhar eixo x | plt.subplots(2, 1, sharex=True) |
| Compartilhar eixo y | plt.subplots(1, 2, sharey=True) |
| Sempre array 2-D | plt.subplots(..., squeeze=False) |
| Layout personalizado | gs = GridSpec(rows, cols); fig.add_subplot(gs[r, c]) |
| Expandir colunas | fig.add_subplot(gs[0, :]) |
| Proporções de altura | GridSpec(2, 1, height_ratios=[3, 1]) |
| Título da figura | plt.suptitle('Title') |
| Salvar figura | plt.savefig('out.png', dpi=150, bbox_inches='tight') |
Capítulos Relacionados
- Introdução ao Matplotlib — visão geral da biblioteca e instalação
- Primeiros Passos com Matplotlib — seu primeiro gráfico
- Gráficos de Linha no Matplotlib — plotando dados contínuos
- Rótulos no Matplotlib — rótulos de eixo, títulos e anotações
- Grade no Matplotlib — adicionando e estilizando linhas de grade
- Gráficos de Barras no Matplotlib — comparando categorias
- Gráficos de Dispersão no Matplotlib — relações entre variáveis
- Histogramas no Matplotlib — distribuições de dados