W3docs

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 numpy

Se 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 de show() ou savefig().
  • Quando ncols=1 e nrows=1 (o padrão), axes é um único objeto Axes, 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:

Layoutfigsize 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 suptitle

Ou use fig.subplots_adjust(top=0.90) em vez disso.

Referência Rápida

TarefaCódigo
Grade 1×2fig, (ax1, ax2) = plt.subplots(1, 2)
Grade 2×2fig, ax = plt.subplots(2, 2)
Acessar célulaax[row, col]
Iterar todas as célulasfor ax in axes.flat:
Compartilhar eixo xplt.subplots(2, 1, sharex=True)
Compartilhar eixo yplt.subplots(1, 2, sharey=True)
Sempre array 2-Dplt.subplots(..., squeeze=False)
Layout personalizadogs = GridSpec(rows, cols); fig.add_subplot(gs[r, c])
Expandir colunasfig.add_subplot(gs[0, :])
Proporções de alturaGridSpec(2, 1, height_ratios=[3, 1])
Título da figuraplt.suptitle('Title')
Salvar figuraplt.savefig('out.png', dpi=150, bbox_inches='tight')

Capítulos Relacionados

Was this page helpful?