【问题标题】:Wireframe joins the wrong way in numpy matplotlib mplot3d线框在 numpy matplotlib mplot3d 中以错误的方式连接
【发布时间】:2016-07-21 04:11:26
【问题描述】:

我正在尝试使用 matplotlib 在 Python 中创建 3D 线框。

然而,当我开始绘制实际的图表时,线框以错误的方式连接,如下图所示。

如何强制 matplotlib 沿某个轴加入线框?

我的代码如下:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

def rossler(x_n, y_n, z_n, h, a, b, c):
#defining the rossler function
x_n1=x_n+h*(-y_n-z_n)
y_n1=y_n+h*(x_n+a*y_n)
z_n1=z_n+h*(b+z_n*(x_n-c))   
return x_n1,y_n1,z_n1

#defining a, b, and c
a = 1.0/5.0
b = 1.0/5.0
c = 5

#defining time limits and steps
t_0 = 0
t_f = 32*np.pi
h = 0.01
steps = int((t_f-t_0)/h)

#3dify
c_list = np.linspace(5,10,6)
c_size = len(c_list)
c_array = np.zeros((c_size,steps))

for i in range (0, c_size):
    for j in range (0, steps):
        c_array[i][j] = c_list[i]

#create plotting values
t = np.zeros((c_size,steps))
for i in range (0, c_size):
    t[i] = np.linspace(t_0,t_f,steps)
x = np.zeros((c_size,steps))
y = np.zeros((c_size,steps))
z = np.zeros((c_size,steps))
binvar, array_size = x.shape

#initial conditions
x[0] = 0
y[0] = 0
z[0] = 0

for j in range(0, c_size-1):
    for i in range(array_size-1):
        c = c_list[j]
        #re-evaluate the values of the x-arrays depending on the initial conditions
        [x[j][i+1],y[j][i+1],z[j][i+1]]=rossler(x[j][i],y[j][i],z[j][i],t[j][i+1]-t[j][i],a,b,c)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(t,x,c_array, rstride=10, cstride=10)
plt.show()

我将其作为输出:

另一个角度的相同输出:

而我希望线框沿着波峰连接。抱歉,我不能给你我想看的图片,这是我的问题,但我想它更像是教程图片。

【问题讨论】:

  • 尝试scatter 来解决它。当我将情节更改为 ax.scatter(x,y,z_array,s=0.1) 时,我可以看到一些看起来像 6 个堆叠的 Roessler 吸引子被挤压成 xy 平面的东西

标签: python numpy matplotlib 3d mplot3d


【解决方案1】:

如果我理解的话,您想将 6 条轨迹与多边形联系起来。您可以通过 2 x 2 对迹线进行三角测量,然后绘制没有边缘或抗锯齿的表面来做到这一点。也许选择一个好的颜色图也会有所帮助。

请记住,这将是一个非常沉重的情节。导出的 SVG 重量为 10mb :)

import matplotlib.tri as mtri

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

for LineIndex in range(c_size-1):
    # If plotting all at once, you get a MemoryError. I'll plot each 6 points
    for Sample in range(0, array_size-1, 3):
        # I switched x and c_array, because the surface  and the triangles 
        # will look better by default
        X = np.concatenate([t[LineIndex,Sample:Sample+3], t[LineIndex+1,Sample:Sample+3]])
        Y = np.concatenate([c_array[LineIndex,Sample:Sample+3], c_array[LineIndex+1,Sample:Sample+3]])
        Z = np.concatenate([x[LineIndex,Sample:Sample+3], x[LineIndex+1,Sample:Sample+3]])
        T = mtri.Triangulation(X, Y)

        ax.plot_trisurf(X, Y, Z, triangles=T.triangles, edgecolor='none', antialiased=False)

ax.set_xlabel('t')
ax.set_zlabel('x')
plt.savefig('Test.png', format='png', dpi=600)
plt.show()

这是生成的图像:

【讨论】:

    【解决方案2】:

    我不太确定你到底想达到什么目标,但我认为它不会奏效。

    这是您的数据在逐层绘制时的样子(不带填充和带填充):

    您正在尝试将其绘制为线框图。这是the manual 中的线框图的样子:

    请注意巨大的不同:线框图本质上是一个适当的曲面图,唯一的区别是曲面的面是完全透明的。这也意味着你只能绘图

    1. z(x,y) 形式的单值函数,此外还有
    2. 在矩形网格上指定(至少在拓扑上)

    您的数据两者都不是:您的点是沿线给出的,并且它们彼此堆叠,因此不可能这是一个可以绘制的单一表面。

    如果您只是想将您的功能相互可视化,以下是我绘制上述数字的方式:

    from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for zind in range(t.shape[0]):
        tnow,xnow,cnow = t[zind,:],x[zind,:],c_array[zind,:]
        hplot = ax.plot(tnow,xnow,cnow)
    
        # alternatively fill:
        stride = 10
        tnow,xnow,cnow = tnow[::stride],xnow[::stride],cnow[::stride]
        slice_from = slice(None,-1)
        slice_to = slice(1,None)
        xpoly = np.array([tnow[slice_from],
                          tnow[slice_to],
                          tnow[slice_to],
                          tnow[slice_from]]
                          ).T
        ypoly = np.array([xnow[slice_from],
                          xnow[slice_to],
                          np.zeros_like(xnow[slice_to]),
                          np.zeros_like(xnow[slice_from])]
                          ).T
        zpoly = np.array([cnow[slice_from],
                          cnow[slice_to],
                          cnow[slice_to],
                          cnow[slice_from]]
                          ).T
    
        tmppoly = [tuple(zip(xrow,yrow,zrow)) for xrow,yrow,zrow in zip(xpoly,ypoly,zpoly)]
        poly3dcoll = Poly3DCollection(tmppoly,linewidth=0.0)
        poly3dcoll.set_edgecolor(hplot[0].get_color())
        poly3dcoll.set_facecolor(hplot[0].get_color())
        ax.add_collection3d(poly3dcoll)
    
    plt.xlabel('t')
    plt.ylabel('x')
    plt.show()
    

    还有另一种选择:切换坐标轴,使 (x,t) 对对应于垂直平面而不是水平平面。在这种情况下,您的各种c 值的函数绘制在平行平面上。这允许正确使用线框图,但是由于您的函数在不同的时间步长中具有极值,因此结果与原始图一样令人困惑。您可以尝试沿t 轴使用很少的图,并希望极值接近。这种方法需要大量的猜测,所以我自己没有尝试这样做。不过,您可以将每个函数绘制为填充曲面:

    from matplotlib.collections import PolyCollection
    
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for zind in range(t.shape[0]):
        tnow,xnow,cnow = t[zind,:],x[zind,:],c_array[zind,:]
        hplot = ax.plot(tnow,cnow,xnow)
        # alternative to fill:
        stride = 10
        tnow,xnow,cnow = tnow[::stride],xnow[::stride],cnow[::stride]
        slice_from = slice(None,-1)
        slice_to = slice(1,None)
        xpoly = np.array([tnow[slice_from],
                          tnow[slice_to],
                          tnow[slice_to],
                          tnow[slice_from]]
                          ).T
        ypoly = np.array([xnow[slice_from],
                          xnow[slice_to],
                          np.zeros_like(xnow[slice_to]),
                          np.zeros_like(xnow[slice_from])]
                          ).T
        tmppoly = [tuple(zip(xrow,yrow)) for xrow,yrow in zip(xpoly,ypoly)]
        polycoll = PolyCollection(tmppoly,linewidth=0.5)
        polycoll.set_edgecolor(hplot[0].get_color())
        polycoll.set_facecolor(hplot[0].get_color())
        ax.add_collection3d(polycoll,zdir='y',zs=cnow[0])
        hplot[0].set_color('none')
    
    ax.set_xlabel('t')
    ax.set_zlabel('x')
    plt.show()
    

    结果如下:

    不过,有几点需要注意。

    1. 由于缺乏深度信息,3d 散点图和线图很难理解。您可能以一种根本错误的方式处理可视化问题:也许还有其他选项可以用来可视化您的数据。
    2. 即使您像我展示的那样进行绘图,您也应该知道 matplotlib 历来无法正确绘制复杂的 3d 对象。现在,“适当地”是指“具有物理上合理的表观深度”,另请参见 the mplot3d FAQ note 对此的描述。问题的核心是matplotlib将每一个3d对象都投影到2d,然后在屏幕上一个接一个地绘制这些煎饼。有时,所声称的煎饼的绘制顺序与它们的实际相对深度不对应,这会导致人工制品对人类来说非常明显,而且看起来也很奇怪。如果您仔细查看这篇文章中的第一个填充图,您会发现金色的平面图位于洋红色的后面,即使它应该在它的顶部。 3d bar plotsconvoluted surfaces 经常发生类似的事情。
    3. 当你说“抱歉,我不能给你我想看的图片,这是我的问题”,你就大错特错了。这不仅仅是你的问题。您可能非常清楚您要达到的目标,但除非您非常清楚地描述您在脑海中看到的内容,否则外界将不得不求助于猜测。通过尽可能提供信息,您可以让他人和您自己的工作更轻松。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-29
      • 2018-04-13
      • 1970-01-01
      • 1970-01-01
      • 2023-01-21
      • 2020-03-16
      • 2017-10-19
      • 2011-03-22
      相关资源
      最近更新 更多