matplotlib中的曲面图


104

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

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

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


请开始标记所有这些重复的表面,然后将重复的彼此封闭。还要标记numpymesh作为与meshgrid生成有关的网格
smci

Answers:


120

对于曲面,它与三元组列表略有不同,您应该为2d数组中的域传递网格。

如果您只拥有3d点列表而不是某些函数f(x, y) -> z,则将遇到问题,因为有多种方法可以将3d点云三角化为表面。

这是一个光滑的表面示例:

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


1
嗨,谢谢你。您能否详细说明一下具有功能如何f(x,y) -> z获得更多信息,而不仅仅是像OP最初那样使用列表方法。
格雷戈里·库恩

15
但是,当z是一个自变量而不是x和y的函数时,该怎么办?
Labibah 2015年

4
在这种情况下,也许您应该查看一下plot_trisurf。但是,正如我已经提到的,这是不平凡的,因为您需要对表面进行三角测量,并且有多种解决方案。作为一个基本示例,仅考虑由(0,0,0.2),(0,1,0),(1,1,0.2),(1,0,0)给出的4个点。从上方看,它看起来像一个正方形,上面有一点褶皱。 但是“对折”沿哪个对角线发生? 是“高”对角线为0.2还是“低”对角线为0?两者都是有效的表面!因此,在定义明确的解决方案之前,您需要选择三角剖分算法。
2015年

为什么从mpl_toolkits.mplot3d导入Axes3D,而在上面的代码中的任何地方都没有使用Axes3D?
绚瀬絵里2015年

5
此导入有副作用。没有此导入,将无法projection='3d'在呼叫中使用kwarg fig.add_subplot
2015年

33

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

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

5
严格来说,这里没有熊猫。
downer

我很难重现此情节。要达到此目的,将有一些(较小的)样本值是多少?
JRsz

21

我只是遇到了同样的问题。我已均匀间隔即在3 1-d阵列,而不是2-d阵列数据matplotlibplot_surface欲望。我的数据恰好在,pandas.DataFrame所以这里是修改3个1D数组的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')

那是原始的例子。在下一个位加上这个位,可以从3个1D数组中创建相同的图。

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

以下是得出的数字:

在此处输入图片说明 在此处输入图片说明


我想知道是否可以去除表面上的线条(上图),我的意思是可以赋予​​表面光泽外观而不是鳞片状外观吗?谢谢。@ stvn66
衍射

@diffracteD,请尝试使用较小的网格大小。我几乎可以肯定,这就是设定轮廓之间宽度的原因。通过在更精细的网格上进行评估,您实际上应该减小“像素大小”并提高分辨率,以达到更平滑的渐变。
史蒂文·C·豪威尔

有没有一种方法可以根据特定类别对上面的表面进行着色?对于前。 类别x,y,z是数据格式,我想根据特定类别对通过x,y,z的表面进行着色。
Rudresh Ajgaonkar '16

@RudreshAjgaonkar,您应该能够使用三个单独的绘图命令,对于每个类别都使用一个,对于这三个类别使用所需的任何颜色。
史蒂文·霍维尔

您可以提供示例代码吗?我是matplotlib和python的新手。
Rudresh Ajgaonkar '16

4

只是为了说明一下,伊曼纽尔(Emanuel)有了我(可能还有许多其他人)正在寻找的答案。如果您在3个单独的阵列中有3d分散的数据,则pandas是一个了不起的帮助,并且比其他选项要好得多。详细地说,假设您的x,y,z是一些任意变量。在我的情况下,这些是c,gamma和错误,因为我正在测试支持向量机。有很多潜在的选择来绘制数据:

  • scatter3D(cParams,gammas,avg_errors_array)-可行,但是过于简单
  • plot_wireframe(cParams,gammas,avg_errors_array)-可以工作,但是如果您的数据排序不好,看起来会很丑陋,这可能是大量真实科学数据的情况
  • ax.plot3D(cParams,gammas,avg_errors_array)-类似于线框

数据线框图

数据线框图

数据的3D分散

数据的3D分散

代码如下:

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

这是最终输出:

xyz数据的plot_trisurf


3

查看官方示例。X,Y和Z实际上是2d数组,numpy.meshgrid()是从1d x和y值中获取2d x,y网格的简单方法。

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

这是将3元组转换为3个1d数组的pythonic方法。

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 delaunay三角剖分(插值),它将1d x,y,z转换为兼容的(?):

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


不,在该示例中,XYZ是二维的。
2012年

我站得住了。如果您的数据是均匀分布的,请使用meshgrid(),如链接示例中所示。如果您的数据间距不均匀,则使用例如griddata()进行插值。
Dima Tisnek'2

1

在Matlab中,我仅使用,坐标(而不是)delaunay上的函数做了类似的事情,然后使用或绘制,使用了高度。xyztrimeshtrisurfz

SciPy具有Delaunay类,该类基于与Matlab delaunay函数相同的基础QHull库,因此您应该获得相同的结果。

从那里开始,应该有几行代码将python-matplotlib示例中的Plotting 3D Polygons转换为您希望实现的目标,从而为Delaunay您提供了每个三角形多边形的规格。


请参阅基于的答案ax.plot_trisurf(..)
Evgeni Sergeev

1

只是添加一些其他想法,这些想法可能会帮助其他人解决不规则的域类型问题。对于用户具有三个向量/列表的情况,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()

上面的代码产生:

非矩形网格问题的曲面图

但是,这可能无法解决所有问题,尤其是在不规则域中定义问题的情况下。同样,在畴具有一个或多个凹面区域的情况下,德劳内三角剖分可能会导致在畴外部生成虚假三角形。在这种情况下,必须从三角测量中删除这些流氓三角形,以实现正确的表面表示。对于这些情况,用户可能必须明确包括delaunay三角剖分计算,以便可以通过编程方式删除这些三角形。在这种情况下,以下代码可以代替以前的绘图代码:


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)去除了溶液的位置:

在此处输入图片说明

三角形已删除

我希望以上内容可能对解决方案数据中出现凹形情况的人们有所帮助。


0

无法使用您的数据直接制作3d曲面。我建议您使用诸如pykridge之类的工具构建插值模型。该过程将包括三个步骤:

  1. 使用训练插值模型 pykridge
  2. 使用X和构建网格Ymeshgrid
  3. 内插值 Z

创建了网格和相应的Z值之后,现在就可以使用了plot_surface。请注意,根据数据的大小,该meshgrid功能可以运行一段时间。解决方法是使用np.linspacefor XYaxis 创建均匀间隔的样本,然后应用插值来推断必要的Z值。如果是这样,则插值可能会与原始值有所不同,Z因为X并且Y已经更改。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.