python | numpy小记(八):理解 NumPy 中的 `np.meshgrid`
python | numpy小记(八):理解 NumPy 中的 `np.meshgrid`
- 一、核心思想:从“轴”到“网”
- 二、工作原理与详细示例
- 三、为什么它如此有用?——矢量化计算
- 四、进阶用法
- 1. 绘制三维曲面和等高线图
- 2. `indexing` 参数
- 总结
一、核心思想:从“轴”到“网”
想象一下,你正在一张白纸上画一个坐标网格。
- 首先,你在 x 轴(横轴)上标记了几个点,比如
x = [1, 2, 3]
。 - 然后,你在 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)
),我需要一种方法来轻松地获取每个点的 x
和 y
坐标。
numpy.meshgrid
的作用就是帮你解决这个问题。它接收代表坐标轴上点的一维向量(如上面的 x
和 y
),然后生成两个二维矩阵(我们通常称之为 XX
和 YY
)。这两个矩阵包含了网格上每个点的x坐标和y坐标。
XX
矩阵:存储了网格中每一个点的 x 坐标。YY
矩阵:存储了网格中每一个点的 y 坐标。
二、工作原理与详细示例
让我们用上面那个简单的例子来看看 meshgrid
是如何工作的。
import numpy as np# 1. 定义x轴和y轴上的点
x = np.array([1, 2, 3]) # 长度为 3
y = 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]
。
最关键的一点是:现在 XX
和 YY
在相同位置 (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
的坐标。这里的 x
和 y
就必须是 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)
可以看到,输出矩阵的形状和值的排布都发生了变化。了解这个参数对于避免在复杂计算中出错非常重要。
总结
- 核心功能:
np.meshgrid
从一维的坐标轴向量生成二维(或多维)的坐标网格矩阵。 - 主要目的:为矢量化计算服务,让你能够对整个网格上的所有点进行快速、高效的并行计算,避免使用慢速的Python循环。
- 经典应用:计算二维/三维函数在网格上的值、生成数据用于绘制等高线图和三维表面图。
- 注意事项:留意
indexing
参数 ('xy'
vs'ij'
),确保它符合你的计算或绘图需求。