【问题标题】:3d plotting with python使用 python 进行 3D 绘图
【发布时间】:2026-02-12 14:05:06
【问题描述】:

我正在尝试在 python 中绘制一个曲面。我有一个 N 乘 N 值的表。我创建了两个向量 X 和 Y,每个向量都有 N 个元素。当我尝试绘制此图时,出现错误:

ValueError: total size of new array must be unchanged

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

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

这是一个示例代码:

随机导入 导入数学

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)

【问题讨论】:

  • 你能贴出抛出错误的代码行吗?

标签: python numpy plot matplotlib


【解决方案1】:

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

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

如果您有 1D X 和 Y 数组,并且想要从 2D 网格获得简单的表面,则使用 numpy.meshgridnumpy.mgrid 生成适当的 X 和 Y 2D 数组。

编辑: 为了解释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]

...并获得 3 个 3D 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] 将返回 2 个 10x5 数组,数字分别在 0-1 和 0-5 之间递增),但是让我们跳到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 实际上恰好返回一个由 2 个 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()

【讨论】:

  • Joe,你说得对,但如果将 X.append(x) 移动到第二个循环中,它将在示例中起作用。如果您有一个 NxN 长的一维数组,plot_surface 仍然有效。错误是说 matplotlib 无法重塑 1D 数组,使其成为 N × N 2D 数组。你也太快了。
  • 谢谢。这只是一个测试,看看我是否理解了表面图。实际上,我打算做一些类似于傅立叶变换的事情,但我不知道如何在 python 的单行中进行复杂的运算或条件运算。我将为此发布一个新问题。
  • @Yann - 啊,对!我实际上并没有运行他发布的代码......我想我应该有!看来你真的回答了他的问题,那么。 ......不管它值多少钱,你比我更频繁地击败我。 :)
  • @JoeKington:我现在更困惑了。我真的不明白 mgrid 生成什么。 python 文档中的示例给出了两个 N × N 数组(两个 2s 数组而不是一个)。另一方面,随机文档没有提供将范围放入其中的选项。
  • @Yotam - mgrid 是一个可以索引以生成 ND 数组的对象。 meshgrid 一开始可能更容易理解。 xx, yy = np.mgrid[:10, :10]x, y = np.arange(10), np.arange(10); xx, yy = np.meshgrid(x, y) 相同