matplotlib中的表面图
我有一个包含三元组的列表,这些三元组表示三维空间中的一些点。我想画出一个表面,覆盖所有这些点。
mplot3d
这个包里的plot_surface
函数需要的参数X、Y和Z必须是二维数组。请问plot_surface
是画表面的正确函数吗?我该如何把我的数据转换成所需的格式呢?
data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)]
9 个回答
29
我刚遇到过同样的问题。我有一些均匀分布的数据,这些数据在三个一维数组中,而不是matplotlib
的plot_surface
所需要的二维数组。我的数据正好在一个pandas.DataFrame
里,所以这里有一个matplotlib.plot_surface
的例子,我对它进行了修改,以便用三个一维数组来绘图。
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.title('Original Code')
这是原始的例子。接下来的这段代码可以用三个一维数组创建相同的图。
# ~~~~ MODIFICATION TO EXAMPLE BEGINS HERE ~~~~ #
import pandas as pd
from scipy.interpolate import griddata
# create 1D-arrays from the 2D-arrays
x = X.reshape(1600)
y = Y.reshape(1600)
z = Z.reshape(1600)
xyz = {'x': x, 'y': y, 'z': z}
# put the data into a pandas DataFrame (this is what my data looks like)
df = pd.DataFrame(xyz, index=range(len(xyz['x'])))
# re-create the 2D-arrays
x1 = np.linspace(df['x'].min(), df['x'].max(), len(df['x'].unique()))
y1 = np.linspace(df['y'].min(), df['y'].max(), len(df['y'].unique()))
x2, y2 = np.meshgrid(x1, y1)
z2 = griddata((df['x'], df['y']), df['z'], (x2, y2), method='cubic')
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x2, y2, z2, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.title('Meshgrid Created from 3 1D Arrays')
# ~~~~ MODIFICATION TO EXAMPLE ENDS HERE ~~~~ #
plt.show()
以下是生成的图形:
58
你可以直接从某个文件中读取数据并进行绘图。
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from sys import argv
x,y,z = np.loadtxt('your_file', unpack=True)
fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.savefig('teste.pdf')
plt.show()
如果需要的话,你可以设置vmin和vmax来定义颜色条的范围,比如:
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1, vmin=0, vmax=2000)
额外内容
我在想怎么做一些互动式的图表,这里用的是人工生成的数据。
from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
from IPython.display import Image
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
def plot(i):
fig = plt.figure()
ax = plt.axes(projection='3d')
theta = 2 * np.pi * np.random.random(1000)
r = i * np.random.random(1000)
x = np.ravel(r * np.sin(theta))
y = np.ravel(r * np.cos(theta))
z = f(x, y)
ax.plot_trisurf(x, y, z, cmap='viridis', edgecolor='none')
fig.tight_layout()
interactive_plot = interactive(plot, i=(2, 10))
interactive_plot
180
对于表面来说,处理方式和三元组的列表有点不同,你需要传入一个二维数组的网格作为区域。
如果你只有一堆三维点,而不是某个函数 f(x, y) -> z
,那么你就会遇到问题,因为可以用多种方法将这些三维点云转换成一个表面。
下面是一个光滑表面的例子:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
# Axes3D import has side effects, it enables using projection='3d' in add_subplot
import matplotlib.pyplot as plt
import random
def fun(x, y):
return x**2 + y
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-3.0, 3.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array(fun(np.ravel(X), np.ravel(Y)))
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()