HOWTO · Matplotlib
Como criar um gráfico de superfície no Matplotlib
Podemos criar um gráfico de superfície em Matplotlib usando o método plot_surface().
Nesta página
Para criar um gráfico de superfície 3D no Matplotlib, prepare arrays bidimensionais compatíveis X, Y e Z, crie um eixo com projection="3d" e passe os arrays para Axes3D.plot_surface. O exemplo abaixo constrói uma grade de coordenadas a partir de uma função matemática, identifica os eixos, adiciona uma barra de cores e salva a figura. O mesmo fluxo funciona com valores medidos quando as coordenadas e alturas podem ser representadas em uma grade retangular.
Preparando o ambiente
Instale o NumPy para operações com arrays e grades e o Matplotlib para criar gráficos. No terminal, execute python -m pip install numpy matplotlib no ambiente Python que pretende usar. Se você já gerencia pacotes em um ambiente virtual ou notebook, instale as duas bibliotecas nele para que fiquem disponíveis ao interpretador que executará o script.
O exemplo importa o NumPy e matplotlib.pyplot. O suporte a gráficos 3D já vem incluído no Matplotlib; projection="3d" cria o eixo tridimensional, portanto não é necessário importar Axes3D separadamente. O método usado nesse eixo é Axes3D.plot_surface.
Preparando os dados
Um gráfico de superfície atribui uma altura a cada posição em um plano definido por duas coordenadas. Para coordenadas espaçadas regularmente, np.linspace() cria valores de coordenada unidimensionais e np.meshgrid() os expande em grades bidimensionais X e Y. Calcule Z a partir dessas grades ou forneça alturas medidas organizadas nas mesmas posições da grade.
Os três arrays precisam ter dimensões compatíveis: cada Z[i, j] é a altura do par de coordenadas X[i, j], Y[i, j]. As versões atuais do Matplotlib também aceitam vetores de coordenadas unidimensionais x e y e os expandem em relação a um Z bidimensional, mas o próprio array de alturas ainda precisa corresponder às dimensões da grade: um Z calculado em uma grade diferente de X e Y gera um ValueError, como mostra a seção de solução de problemas abaixo com uma execução real. Primeiro, construa as grades de coordenadas; depois, calcule ou remodele as alturas nessas mesmas grades. Se as medições dispersas não formarem uma grade retangular, prepare ou interpole os dados em uma grade antes de usar plot_surface.
Criando o gráfico de superfície
Este exemplo completo usa a função radial suave sin(sqrt(x² + y²)). Ele cria as grades, desenha a superfície, identifica as três dimensões, adiciona uma escala de cores e salva o resultado como surface-plot.png no diretório atual. Execute-o em um ambiente com NumPy e Matplotlib instalados. O exemplo não imprime nada no console; o resultado é uma imagem salva e uma janela de figura do Matplotlib quando o backend ativo oferece suporte à exibição. Os rótulos visíveis dos eixos, o título e a barra de cores definidos no exemplo permanecem em inglês.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
surface = ax.plot_surface(X, Y, Z, cmap="viridis")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Surface Plot")
fig.colorbar(surface, ax=ax, label="Z value")
fig.savefig("surface-plot.png", dpi=150, bbox_inches="tight")
plt.show()
Como o script não imprime texto, não há bloco de saída do console. A barra de cores relaciona as cores da superfície aos respectivos valores; a grade define a geometria, enquanto o mapa de cores controla sua aparência. Em um notebook, o ambiente de plotagem geralmente exibe a figura; em um script, plt.show() solicita que o backend configurado do Matplotlib a exiba.
Personalizando o gráfico de superfície
O exemplo abaixo aplica as personalizações mais comuns à mesma superfície. Ele troca o mapa de cores para plasma, remove as arestas da malha e gira a câmera para uma elevação e um azimute fixos antes de adicionar a barra de cores e salvar a figura como customized-surface-plot.png.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
surface = ax.plot_surface(X, Y, Z, cmap="plasma", edgecolor="none")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Customized Surface Plot")
ax.view_init(elev=30, azim=45)
fig.colorbar(surface, ax=ax, label="Z value")
fig.savefig("customized-surface-plot.png", dpi=150, bbox_inches="tight")
plt.show()
Escolha um mapa de cores que facilite a leitura das variações em Z. Neste exemplo, cmap="plasma" substitui a progressão viridis do primeiro exemplo, e a barra de cores ajuda a interpretar essa correspondência. A opção edgecolor="none" remove as arestas desenhadas da malha, que em uma grade densa podem distrair da superfície contínua; mantenha as arestas padrão quando a própria grade de amostragem deve continuar visível.
Para mudar a câmera, chame ax.view_init(elev=30, azim=45) antes de salvar ou exibir a figura. elev altera a elevação da vista e azim gira a vista ao redor do eixo vertical. Ajuste esses valores até que a forma e os eixos fiquem claros; o melhor ângulo depende dos dados e do que você deseja mostrar. Em comparação com a primeira figura, a câmera girada da segunda figura expõe mais a encosta distante do pico central.
Em uma grade densa, plot_surface aceita controles de amostragem de linhas e colunas, como rcount e ccount. Valores menores desenham menos linhas e colunas amostradas e podem acelerar a renderização de uma superfície grande, mas reduzem o nível de detalhe visível. Mantenha amostras suficientes para preservar as variações importantes dos dados. Salve uma imagem para compartilhar com fig.savefig("surface-plot.png", dpi=150, bbox_inches="tight"); faça isso antes de plt.show() para manter a compatibilidade com backends de exibição que fecham a figura depois de mostrá-la. Use outro nome de arquivo ou diretório se quiser guardar várias versões.
Comparação com um gráfico de estrutura de arame
Um gráfico de estrutura de arame desenha apenas as linhas da grade da mesma superfície, sem faces preenchidas nem mapa de cores. Axes3D.plot_wireframe aceita as mesmas grades X, Y e Z, então você pode alternar entre as duas visualizações sem preparar novos dados. A estrutura de arame ajuda quando a própria estrutura da grade importa, quando uma superfície preenchida esconderia o lado distante da forma, ou como uma verificação estrutural rápida de dados desconhecidos antes de gerar uma superfície completa.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2, color="tab:blue")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Wireframe Plot")
fig.savefig("wireframe-plot.png", dpi=150, bbox_inches="tight")
plt.show()
Os argumentos rstride e cstride pulam linhas e colunas para que as linhas permaneçam legíveis; o exemplo desenha uma a cada duas linhas e colunas de uma grade de 50 por 50. Diferentemente de plot_surface, uma estrutura de arame não tem cores de face, portanto não há nada para uma barra de cores explicar. Para uma apresentação final, a superfície preenchida costuma comunicar melhor as diferenças de altura, enquanto a estrutura de arame mostra com mais clareza como a própria grade é amostrada.
Corrigir um erro de dimensões incompatíveis
A falha mais comum de plot_surface é um array Z cujas dimensões não correspondem às grades de coordenadas. O script abaixo calcula Z em uma grade de 50 por 50, mas a passa junto com as grades X e Y de 100 por 100:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
x_small = np.linspace(-5, 5, 50)
y_small = np.linspace(-5, 5, 50)
X_small, Y_small = np.meshgrid(x_small, y_small)
Z = np.sin(np.sqrt(X_small**2 + Y_small**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_surface(X, Y, Z, cmap="viridis")
plt.show()
A execução do script para na chamada a plot_surface com este erro:
ValueError: shape mismatch: objects cannot be broadcast to a single shape. Mismatch is between arg 0 with shape (100, 100) and arg 2 with shape (50, 50).
Para corrigir, calcule Z a partir das mesmas grades que você passa para plot_surface, como faz o primeiro exemplo, de modo que Z.shape seja igual a X.shape e Y.shape. Se não tiver certeza, imprima as três dimensões antes de plotar e compare-as: cada entrada de Z precisa de exatamente um par de coordenadas, nem mais nem menos.
Conclusão
As etapas principais são criar grades de coordenadas, fazer Z corresponder a essas grades, adicionar um eixo 3D e chamar Axes3D.plot_surface. Um mapa e uma barra de cores ajudam a explicar os valores de altura, enquanto os controles de vista e amostragem ajudam a apresentar a forma com clareza. Quando precisar inspecionar a própria grade, plot_wireframe renderiza os mesmos dados como linhas. Salve a figura quando precisar de um arquivo de imagem e ajuste a resolução da grade ou a câmera conforme os dados plotados.
Perguntas frequentes
-
Que dados posso usar em um gráfico de superfície?
Use valores definidos em função de duas coordenadas, com cada altura correspondente a uma posição em uma grade retangular. Uma função é conveniente para uma demonstração; valores medidos também servem quando estão organizados em grades de coordenadas compatíveis. Primeiro, é preciso converter pontos dispersos em uma grade. -
Como instalo as bibliotecas necessárias?
Instale NumPy e Matplotlib no ambiente que executa seu script compython -m pip install numpy matplotlib. -
Posso mudar as cores da superfície?
Sim. Defina o argumentocmapdeplot_surfacee adicione uma barra de cores comfig.colorbar(surface, ax=ax)para mostrar a correspondência entre as cores e os valores. -
Como altero o ângulo de visão?
Useax.view_init(elev=..., azim=...). A elevação altera a altura da câmera e o azimute muda sua rotação ao redor do gráfico. -
Por que
X,YeZprecisam ter dimensões compatíveis?
O Matplotlib precisa de um par de coordenadas e uma altura para cada posição da grade. SeZtiver sido calculado em outra grade,plot_surfacegera umValueErrorsobre dimensões que não podem ser transmitidas juntas. Crie grades de coordenadas bidimensionais comnp.meshgrid()e calculeZa partir delas para que as entradas correspondentes se alinhem.