> 技术文档 > python | numpy小记(八):理解 NumPy 中的 `np.meshgrid`

python | numpy小记(八):理解 NumPy 中的 `np.meshgrid`


python | numpy小记(八):理解 NumPy 中的 `np.meshgrid`

      • 一、核心思想:从“轴”到“网”
      • 二、工作原理与详细示例
      • 三、为什么它如此有用?——矢量化计算
      • 四、进阶用法
        • 1. 绘制三维曲面和等高线图
        • 2. `indexing` 参数
      • 总结

一、核心思想:从“轴”到“网”

想象一下,你正在一张白纸上画一个坐标网格

  1. 首先,你在 x 轴(横轴)上标记了几个点,比如 x = [1, 2, 3]
  2. 然后,你在 y 轴(纵轴)上标记了几个点,比如 y = [10, 20, 30, 40]

现在,这些标记的点定义了一个网格。这个网格上有很多交叉点,它们的坐标分别是:
(1, 10), (2, 10), (3, 10)
(1, 20), (2, 20), (3, 20)
(1, 30), (2, 30), (3, 30)
(1, 40), (2, 40), (3, 40)

现在问题来了:如果我想对每一个网格点进行计算(比如计算 z = f(x, y)),我需要一种方法来轻松地获取每个点的 xy 坐标。

numpy.meshgrid 的作用就是帮你解决这个问题。它接收代表坐标轴上点的一维向量(如上面的 xy),然后生成两个二维矩阵(我们通常称之为 XXYY)。这两个矩阵包含了网格上每个点的x坐标和y坐标。

  • XX 矩阵:存储了网格中每一个点x 坐标
  • YY 矩阵:存储了网格中每一个点y 坐标

二、工作原理与详细示例

让我们用上面那个简单的例子来看看 meshgrid 是如何工作的。

import numpy as np# 1. 定义x轴和y轴上的点x = np.array([1, 2, 3]) # 长度为 3y = np.array([10, 20, 30, 40]) # 长度为 4# 2. 使用 meshgrid 生成坐标矩阵XX, YY = np.meshgrid(x, y)# 3. 打印输出,观察结果print(\"原始 x 向量:\\n\", x)print(\"原始 y 向量:\\n\", y)print(\"-\" * 20)print(\"生成的 XX 矩阵 (x-coordinates):\\n\", XX)print(\"XX 矩阵的形状:\", XX.shape)print(\"-\" * 20)print(\"生成的 YY 矩阵 (y-coordinates):\\n\", YY)print(\"YY 矩阵的形状:\", YY.shape)

输出结果:

原始 x 向量: [1 2 3]原始 y 向量: [10 20 30 40]--------------------生成的 XX 矩阵 (x-coordinates): [[1 2 3] [1 2 3] [1 2 3] [1 2 3]]XX 矩阵的形状: (4, 3)--------------------生成的 YY 矩阵 (y-coordinates): [[10 10 10] [20 20 20] [30 30 30] [40 40 40]]YY 矩阵的形状: (4, 3)

结果分析:

  • XX 矩阵的构建meshgrid 将原始的 x 向量 [1, 2, 3] 水平地复制了 len(y) 次(也就是4次),像叠煎饼一样垂直叠起来,形成一个 (4, 3) 的矩阵。所以,XX 的每一行都是 [1, 2, 3]
  • YY 矩阵的构建meshgrid 将原始的 y 向量 [10, 20, 30, 40] 垂直地复制了 len(x) 次(也就是3次),像手风琴一样水平拉开,形成一个 (4, 3) 的矩阵。所以,YY 的每一列都是 [10, 20, 30, 40]

最关键的一点是:现在 XXYY 在相同位置 (i, j) 上的值,正好对应了我们网格中第 i 行、第 j 列那个点的坐标!

例如,我们想找网格上第2行(索引为1)、第3列(索引为2)的那个点:

  • XX[1, 2] 的值是 3
  • YY[1, 2] 的值是 20
  • 所以这个点的坐标就是 (3, 20),和我们手动列出的一模一样!

三、为什么它如此有用?——矢量化计算

meshgrid 最大的优势在于它能让我们避免使用缓慢的 Python for 循环,而是利用 NumPy 的**矢量化计算(Vectorization)**能力,对整个网格进行一次性、高性能的计算。

应用场景:计算二维函数 z = sin(x) + cos(y) 在整个网格上的值。

方法一:使用 for 循环(效率低)

z_loop = np.zeros_like(XX, dtype=float) # 创建一个和XX形状相同的零矩阵for i in range(XX.shape[0]): # 遍历行 for j in range(XX.shape[1]): # 遍历列 z_loop[i, j] = np.sin(XX[i, j]) + np.cos(YY[i, j])# print(\"使用循环计算的结果:\\n\", z_loop)

这种方法非常直观,但当网格很大时,嵌套的Python循环会变得非常慢。

方法二:使用 meshgrid 和矢量化计算(高效)

# 因为 XX 和 YY 的形状完全相同,我们可以直接对它们进行数学运算# NumPy 会自动对矩阵中的每一个元素执行操作Z_vectorized = np.sin(XX) + np.cos(YY)# print(\"使用矢量化计算的结果:\\n\", Z_vectorized)

这一行代码就完成了和上面两层for循环完全一样的工作!它直接在C语言层面进行计算,速度比Python循环快几个数量级。

这就是 meshgrid 的威力所在:它为矢量化计算搭建了舞台。

四、进阶用法

1. 绘制三维曲面和等高线图

在数据可视化领域,尤其是在使用 matplotlib 库时,meshgrid 是不可或缺的。当你需要绘制一个三维曲面 z = f(x, y) 时,你需要提供 x, y, z 的坐标。这里的 xy 就必须是 meshgrid 生成的二维坐标矩阵。

import matplotlib.pyplot as pltx_surf = np.linspace(-5, 5, 100)y_surf = np.linspace(-5, 5, 100)XX_surf, YY_surf = np.meshgrid(x_surf, y_surf)Z_surf = np.sinc(np.sqrt(XX_surf**2 + YY_surf**2)) # 计算一个二维sinc函数fig = plt.figure()ax = fig.add_subplot(111, projection=\'3d\')ax.plot_surface(XX_surf, YY_surf, Z_surf, cmap=\'viridis\')plt.title(\"3D Surface Plot using meshgrid\")plt.show()
2. indexing 参数

meshgrid 有一个重要的参数 indexing,它有两个值:\'xy\' (默认值) 和 \'ij\'

  • indexing=\'xy\'(笛卡尔坐标系索引):

    • 输出矩阵的形状是 (len(y), len(x))
    • XX 的行是 x 的拷贝,YY 的列是 y 的拷贝。
    • 这种方式最符合绘图和几何直觉。我们上面的所有例子都使用这种默认方式。
  • indexing=\'ij\'(矩阵索引):

    • 输出矩阵的形状是 (len(x), len(y))
    • XX 的列是 x 的拷贝,YY 的行是 y 的拷贝。基本上是 \'xy\' 模式结果的转置。
    • 这种方式在进行纯粹的矩阵和数组运算时可能更自然。
XX_ij, YY_ij = np.meshgrid(x, y, indexing=\'ij\')print(\"XX 矩阵 (indexing=\'ij\'):\\n\", XX_ij)print(\"XX_ij 的形状:\", XX_ij.shape)print(\"-\" * 20)print(\"YY 矩阵 (indexing=\'ij\'):\\n\", YY_ij)print(\"YY_ij 的形状:\", YY_ij.shape)

输出:

XX 矩阵 (indexing=\'ij\'): [[1 1 1 1] [2 2 2 2] [3 3 3 3]]XX_ij 的形状: (3, 4)--------------------YY 矩阵 (indexing=\'ij\'): [[10 20 30 40] [10 20 30 40] [10 20 30 40]]YY_ij 的形状: (3, 4)

可以看到,输出矩阵的形状和值的排布都发生了变化。了解这个参数对于避免在复杂计算中出错非常重要。

总结

  1. 核心功能np.meshgrid 从一维的坐标轴向量生成二维(或多维)的坐标网格矩阵。
  2. 主要目的:为矢量化计算服务,让你能够对整个网格上的所有点进行快速、高效的并行计算,避免使用慢速的Python循环。
  3. 经典应用:计算二维/三维函数在网格上的值、生成数据用于绘制等高线图和三维表面图。
  4. 注意事项:留意 indexing 参数 (\'xy\' vs \'ij\'),确保它符合你的计算或绘图需求。