Python下的3D绘图

11

我正在尝试在python中绘制一个曲面。我有一个N*N值的表格。我已经创建了两个由N个元素组成的向量X和Y。当我尝试绘制时,会出现错误:

ValueError: total size of new array must be unchanged

我查看了示例,发现对于Z的N个元素,X和Y也有N个元素。

这对我来说没有任何意义。为什么我需要N个元素而不是N乘以N个元素?

以下是示例代码:

import random import math

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

bignum = 100

mat = []
X = []
Y = []

for x in range(0,bignum):
    mat.append([])
    X.append(x);
    for y in range (0,bignum):
        mat[x].append(random.random())
        Y.append(y)

fig = plt.figure(figsize=plt.figaspect(2.))
ax = fig.add_subplot(1,1,1, projection='3d')
surf = ax.plot_surface(X,Y,mat)

你能贴出导致错误的代码行吗? - NoBugs
1个回答

20

首先,绝不要做这样的事情:

mat = []
X = []
Y = []

for x in range(0,bignum):
    mat.append([])
    X.append(x);
    for y in range (0,bignum):
        mat[x].append(random.random())
        Y.append(y)

这等同于:

mat = np.random.random((bignum, bignum))
X, Y = np.mgrid[:bignum, :bignum]

... 但它比使用列表并转换为数组要快数个数量级,并且使用的内存只是一小部分。

然而,你的示例完美地运行了。

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

bignum = 100
mat = np.random.random((bignum, bignum))
X, Y = np.mgrid[:bignum, :bignum]

fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
surf = ax.plot_surface(X,Y,mat)
plt.show()

图片描述

如果你阅读了plot_surface的文档,就会发现X、Y和Z应该是二维数组。

这样做是为了能够通过隐式定义点之间的连接来绘制更复杂的曲面(例如球体)。 (例如,可以参考matplotlib图库中的这个例子:http://matplotlib.sourceforge.net/examples/mplot3d/surface3d_demo2.html

如果你有一维的X和Y数组,并希望从二维网格得到简单的曲面,则使用numpy.meshgridnumpy.mgrid生成相应的X和Y二维数组。

编辑: 只是为了解释mgridmeshgrid的作用,让我们看一下它们的输出:

print np.mgrid[:5, :5]
产生:
array([[[0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1],
        [2, 2, 2, 2, 2],
        [3, 3, 3, 3, 3],
        [4, 4, 4, 4, 4]],

       [[0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4],
        [0, 1, 2, 3, 4]]])
所以,它返回一个形状为2x5x5的单一3D数组,但更容易将其视为两个2D数组。其中一个表示5x5网格上任意点的i坐标,而另一个表示j坐标。

由于Python解包的工作方式,我们可以直接写:

xx, yy = np.mgrid[:5, :5]

Python不关心mgrid返回的具体内容,它只会尝试将其解包为两个条目。因为numpy数组在它们的第一个轴上迭代切片,如果我们解包具有形状(2x5x5)的数组,我们将得到2个大小为5x5的数组。类似地,我们可以做一些像这样的事情:

xx, yy, zz = np.mgrid[:5, :5, :5]

从stackoverflow获取而来,我们可以使用np.mgrid函数获得3个数组,它们都是5x5x5的索引矩阵。如果我们使用不同的范围(例如xx, yy = np.mgrid[10:15, 3:8])进行切片,它将会在10到14以及3到7的区间内重复索引。

mgrid还有一些其他的功能(例如可以使用复合步骤参数模拟linspace。例如:xx, yy = np.mgrid[0:1:10j, 0:5:5j]将返回两个分别在0-1和0-5之间递增的大小为10x5的数组),但是我们现在先跳过这些内容,继续介绍meshgrid

meshgrid函数也会将两个数组进行类似于mgrid的扩展操作。例如:

x = np.arange(5)
y = np.arange(5)
xx, yy = np.meshgrid(x, y)
print xx, yy

产出:

(array([[0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4]]), 

array([[0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2],
       [3, 3, 3, 3, 3],
       [4, 4, 4, 4, 4]]))

meshgrid实际上返回了一个包含两个元素的元组,每个元素都是一个5x5的二维数组,但这种区别并不重要。关键的区别在于索引值不必朝特定方向递增,它只是将给定的数组进行平铺。例如:

x = [0.1, 2.4, -5, 19]
y = [-4.3, 2, -1, 18.4]
xx, yy = np.meshgrid(x, y)
产生:
(array([[  0.1,   2.4,  -5. ,  19. ],
       [  0.1,   2.4,  -5. ,  19. ],
       [  0.1,   2.4,  -5. ,  19. ],
       [  0.1,   2.4,  -5. ,  19. ]]),
 array([[ -4.3,  -4.3,  -4.3,  -4.3],
       [  2. ,   2. ,   2. ,   2. ],
       [ -1. ,  -1. ,  -1. ,  -1. ],
       [ 18.4,  18.4,  18.4,  18.4]]))

你会注意到,它只是将我们提供的值平铺。

基本上,当你需要使用与输入网格相同形状的索引时,就可以使用它们。 当您想要在网格值上评估函数时,这通常非常有用。

例如:

import numpy as np
import matplotlib.pyplot as plt

x, y = np.mgrid[-10:10, -10:10]
dist = np.hypot(x, y) # Linear distance from point 0, 0
z = np.cos(2 * dist / np.pi)

plt.title(r'$\cos(\frac{2*\sqrt{x^2 + y^2}}{\pi})$', size=20)
plt.imshow(z, origin='lower', interpolation='bicubic',
          extent=(x.min(), x.max(), y.min(), y.max()))
plt.colorbar()
plt.show()

这里输入图片描述


2
Joe,你说得完全正确,但如果将X.append(x)移动到第二个循环内部,它在示例中将起作用。如果您有一个NxN长的1D数组,则plot_surface仍然有效。错误是说matplotlib无法将1D数组重塑为N乘N的2D数组。另外,你太快了。 - Yann
谢谢。这只是一个测试,以查看我是否理解表面绘图。实际上,我计划做类似于傅里叶变换的事情,但我不知道如何在Python中进行复杂操作或条件操作。我将发布一个新问题来解决这个问题。 - Yotam
@Yann - 啊,对了!我实际上没有按照他发布的代码运行...我想我应该这样做!看起来你实际上回答了他的问题。而且无论如何,你比我更经常地抢先一步。 :) - Joe Kington
@JoeKington:我现在更加困惑了。我真的不明白mgrid生成什么。Python文档中的示例提供了两个N乘N的数组(两个2乘2的数组而不是一个)。另一方面,随机文档没有提供将范围放入其中的选项。 - Yotam
@Yotam - mgrid 是一个对象,您可以对其进行索引以生成 ND 数组。meshgrid 可能更容易理解一些。xx, yy = np.mgrid[:10, :10]x, y = np.arange(10), np.arange(10); xx, yy = np.meshgrid(x, y) 相同。 - Joe Kington
此外,np.random.random只是在指定的形状中生成一组随机数。因此,mat = np.random.random((10,10)) 只会生成一个10x10的数组,其中包含0到1之间的随机数。 - Joe Kington

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接