Matplotlib 中的表面图

我有一个表示三维空间中一组点的三元组列表。我想绘制一个覆盖所有这些点的曲面。

mplot3d包中的 plot_surface函数要求参数 X、 Y 和 Z 为2d 数组。plot_surface是绘制曲面的正确函数吗? 如何将数据转换为所需的格式?

data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)]
365394 次浏览

对于曲面,它与由3个元组组成的列表稍有不同,您应该在2d 数组中为域传入一个网格。

如果你只有一个三维点的列表,而不是某个函数 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()

3d

看看官方的例子。 X,Y 和 Z 实际上是2d 数组,numpy.meshgrid ()是从1dx 和 y 值中得到2dx,y 网格的一种简单方法。

Http://matplotlib.sourceforge.net/mpl_examples/mplot3d/surface3d_demo.py

这里有一种将3元组转换为31d 数组的 Python 方法。

data = [(1,2,3), (10,20,30), (11, 22, 33), (110, 220, 330)]
X,Y,Z = zip(*data)
In [7]: X
Out[7]: (1, 10, 11, 110)
In [8]: Y
Out[8]: (2, 20, 22, 220)
In [9]: Z
Out[9]: (3, 30, 33, 330)

下面是 mtaplotlib 德劳内三角化(插值) ,它将1 d x,y,z 转换成兼容的(?) :

Http://matplotlib.sourceforge.net/api/mlab_api.html#matplotlib.mlab.griddata

在 Matlab 中,我使用 x上的 delaunay函数做了一些类似的事情,y只是坐标(而不是 z) ,然后使用 trimeshtrisurf绘图,使用 z作为高度。

SciPy 有 Delaunay类,它基于与 Matlab 的 delaunay函数相同的底层 QHull 库,因此您应该得到相同的结果。

从这里开始,应该只需要几行代码就可以将这个 在 python-matplotlib 中绘制三维多边形示例转换为您希望实现的内容,因为 Delaunay提供了每个三角形多边形的规范。

我也遇到了同样的问题。我有均匀间隔的数据是在3个1-D 数组,而不是 matplotlibplot_surface想要的2-D 数组。我的数据碰巧是在一个 pandas.DataFrame,所以这里是 matplotlib.plot_surface例子与修改,以绘制3一维数组。

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')

这是最初的例子。添加下一个位将从3个1-D 数组创建相同的绘图。

# ~~~~ 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()

以下是由此得出的数据:

enter image description here enter image description here

您可以直接从某些文件和绘图中读取数据

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)

surface

奖金组

我想知道如何做一些交互式情节,在这种情况下与人工数据

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

插一句,伊曼纽尔得到了我(或许还有其他许多人)正在寻找的答案。如果你有3个独立数组的3D 分散数据,熊猫是一个难以置信的帮助,比其他选项工作得更好。为了详细说明,假设你的 x,y,z 是一些任意变量。在我的例子中,这些是 c、 γ 和错误,因为我在测试一个支持向量机。绘制数据有许多潜在的选择:

  • Scatter3D (cParams,gammas,avg _ error _ array)-这个工作,但是过于简单化
  • Plot _ wireframe (cParams,gammas,avg _ error _ array)——这个工具可以工作,但是如果你的数据没有被很好地排序,那么它看起来就会很丑陋,就像大量真实的科学数据一样
  • Plot3D (cParams,gamma,avg _ error _ array)-类似于线框图

数据的线框图

Wireframe plot of the data

数据的三维散布

3d scatter of the data

代码如下:

    fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_xlabel('c parameter')
ax.set_ylabel('gamma parameter')
ax.set_zlabel('Error rate')
#ax.plot_wireframe(cParams, gammas, avg_errors_array)
#ax.plot3D(cParams, gammas, avg_errors_array)
#ax.scatter3D(cParams, gammas, avg_errors_array, zdir='z',cmap='viridis')


df = pd.DataFrame({'x': cParams, 'y': gammas, 'z': avg_errors_array})
surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.savefig('./plots/avgErrs_vs_C_andgamma_type_%s.png'%(k))
plt.show()

以下是最终结果:

plot_trisurf of xyz data

不可能直接使用你的数据制作一个三维表面。我建议您使用 Pykridge之类的工具来构建插值模型。这一进程将包括三个步骤:

  1. 使用 pykridge训练一个插值模型
  2. 使用 meshgridXY构建一个网格
  3. Z插值

已经创建了网格和相应的 Z值,现在可以使用 plot_surface了。注意,根据数据的大小,meshgrid函数可以运行一段时间。解决办法是使用 np.linspaceXY轴创建均匀间隔的样本,然后应用插值来推断必要的 Z值。如果是这样,插值值可能不同于原来的 Z,因为 XY已经改变。

只是添加一些进一步的想法,可以帮助其他人与不规则的领域类型的问题。对于用户有三个矢量/列表,x,y,z 表示一个2D 解决方案,其中 z 将被绘制在一个矩形网格上作为一个表面的情况,ArtifixR 的“ plot _ trisurf ()”注释是适用的。一个非矩形域的类似例子是:

import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D


# problem parameters
nu = 50; nv = 50
u = np.linspace(0, 2*np.pi, nu,)
v = np.linspace(0, np.pi, nv,)


xx = np.zeros((nu,nv),dtype='d')
yy = np.zeros((nu,nv),dtype='d')
zz = np.zeros((nu,nv),dtype='d')


# populate x,y,z arrays
for i in range(nu):
for j in range(nv):
xx[i,j] = np.sin(v[j])*np.cos(u[i])
yy[i,j] = np.sin(v[j])*np.sin(u[i])
zz[i,j] = np.exp(-4*(xx[i,j]**2 + yy[i,j]**2)) # bell curve


# convert arrays to vectors
x = xx.flatten()
y = yy.flatten()
z = zz.flatten()


# Plot solution surface
fig = plt.figure(figsize=(6,6))
ax = Axes3D(fig)
ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0,
antialiased=False)
ax.set_title(r'trisurf example',fontsize=16, color='k')
ax.view_init(60, 35)
fig.tight_layout()
plt.show()

上述代码产生:

Surface plot for non-rectangular grid problem

但是,这可能不能解决所有问题,特别是当问题定义在一个不规则的域上时。此外,在域有一个或多个凹面的情况下,德劳内三角化可能导致在域外生成假三角形。在这种情况下,这些流氓三角形必须从三角形去除,以实现正确的表面表示。对于这些情况,用户可能必须显式地包括德劳内三角化计算,以便这些三角形可以通过编程删除。在这种情况下,以下代码可以取代以前的绘图代码:


import matplotlib.tri as mtri
import scipy.spatial
# plot final solution
pts = np.vstack([x, y]).T
tess = scipy.spatial.Delaunay(pts) # tessilation


# Create the matplotlib Triangulation object
xx = tess.points[:, 0]
yy = tess.points[:, 1]
tri = tess.vertices # or tess.simplices depending on scipy version


#############################################################
# NOTE: If 2D domain has concave properties one has to
#       remove delaunay triangles that are exterior to the domain.
#       This operation is problem specific!
#       For simple situations create a polygon of the
#       domain from boundary nodes and identify triangles
#       in 'tri' outside the polygon. Then delete them from
#       'tri'.
#       <ADD THE CODE HERE>
#############################################################


triDat = mtri.Triangulation(x=pts[:, 0], y=pts[:, 1], triangles=tri)


# Plot solution surface
fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
ax.plot_trisurf(triDat, z, linewidth=0, edgecolor='none',
antialiased=False, cmap=cm.jet)
ax.set_title(r'trisurf with delaunay triangulation',
fontsize=16, color='k')
plt.show()

下面给出的示例图解说明了解决方案1)带有虚假三角形,以及2)它们被移除的地方:

enter image description here

triangles removed

我希望以上内容能够对解析数据中的凹形情况有所帮助。

这不是一个通用的解决方案,但可能有助于那些刚刚在谷歌输入“ matplotlib 曲面图”,并在这里着陆的人。

假设您有 data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)],那么您可以使用 x, y, z = zip(*data)获得三个1-d 列表。现在您当然可以使用三个1-d 列表来实现 创建三维散点图

但是,为什么 一般来说不能用这些数据来绘制地表曲面呢?为了理解这一点,考虑一个空白的3-D 情节:

现在,假设在一个“离散”的规则网格上,每个可能的(x,y)值都有一个 z 值,那么就没有问题了,实际上你可以得到一个曲面图:

import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm


x = np.linspace(0, 10, 6)  # [0, 2,..,10] : 6 distinct values
y = np.linspace(0, 20, 5)  # [0, 5,..,20] : 5 distinct values
z = np.linspace(0, 100, 30)  # 6 * 5 = 30 values, 1 for each possible combination of (x,y)


X, Y = np.meshgrid(x, y)
Z = np.reshape(z, X.shape)  # Z.shape must be equal to X.shape = Y.shape


fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')


ax.plot_surface(X, Y, Z)


ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()

如果没有 z 表示(x,y)的所有可能组合,会发生什么?然后在点上(在上面空白图上 x-y 平面上两条黑线的交点处) ,我们不知道 z 的值是多少。它可以是任何东西,我们不知道我们的曲面在那一点应该是“高”还是“低”(虽然它可以用其他函数来近似,但是 surface_plot要求你提供它的参数,其中 X.form = Y.form = Z.form)。