Python 的 NumPy是处理数组/矩阵数据最常用的库。
矩阵可以被视为二维值“网格”,其中网格中每个值的位置由一对值 (i, j) 给出。
这些值表示该值在网格中的行号和列号。
在本教程中,我们将了解如何使用 Python 中的 NumPy 库创建此类网格并使用它们。
什么是网格?
术语“meshgrid”由两个词组成——“mesh”和“grid”,这两个词通常
指“网络”或“交错结构”或等距值的“排列”
笛卡尔坐标系就是一个典型的例子。
如图所示,每个位置由一对有序索引或 X 和 Y 坐标表示。
使用 NumPymeshgrid
方法中,我们将创建这样的有序对来构造一个网格。
网状网格有什么好处?
网格在任何需要构建明确定义的二维甚至多维空间以及需要能够引用空间中每个位置的应用程序中都很有用。
然而,网格最突出的应用是数据可视化。为了可视化数据中的模式,或者在 2D 或 3D 空间中绘制函数,网格通过创建有序的因变量对而发挥着重要作用。
以下是使用网格网格作为中间步骤实现的数据可视化的两个示例。
第一个图显示了圆的等高线图,其半径和中心位于 (0,0)。
第二个图是 3D 高斯曲面图。
这些图使用使用生成的坐标numpy.meshgrid
.
从两个 1D 数组创建 2D NumPy 网格
让我们从一个使用网格网格的简单示例开始。
我们想要创建一个 5×5 笛卡尔网格,x 坐标范围从 1 到 5,y 坐标范围从 11 到 15。
这意味着有序的 (x, y) 坐标对将从 (1,11) 开始一直到 (5,15)。
我们还需要单独存储网格中每个位置的 x 和 y 坐标。
X = np.array([1,2,3,4,5])
Y = np.array([11, 12, 13, 14, 15])
xx, yy = np.meshgrid(X,Y)
print("X coordinates:\n{}\n".format(xx))
print("Y coordinates:\n{}".format(yy))
Output:
我们得到两个 NumPy 数组作为输出,每个数组的形状为 5×5。
第一个数组表示网格中每个位置的 x 坐标,而第二个数组表示相应的 y 坐标。
如果您在两个数组中相同位置处获取有序的值对,您将获得我们预期网格中所有位置的 (x, y) 坐标。
例如,两个数组中位置[0,0]的值代表位置(1,11)。 [0,1]处的代表位置(2,11),依此类推。
使用稀疏 = True 创建 NumPy 网格
如果你仔细观察输出np.meshgrid
在前面的示例中,第一个输出数组 xx 具有按行重复的相同一维数组,第二个输出数组 yy 具有按列重复的相同数组。
因此,要构建网格,我们只需要有关重复的一维数组及其方向的信息。
这是通过指定参数‘的值来实现的sparse
' 为 '真实'
顾名思义,它返回网格的“稀疏”表示。
让我们重建相同的网格sparse=True
X = np.array([1,2,3,4,5])
Y = np.array([11, 12, 13, 14, 15])
xx, yy = np.meshgrid(X,Y, sparse=True)
print("X coordinates:\n{}\n".format(xx))
print("Y coordinates:\n{}".format(yy))
Output:
现在输出数组不再是 5×5 的形状。 xx 数组的形状为 1×5,yy 数组的形状为 5×1
请注意,这不会影响网格上值的任何进一步计算。
例如,如果您想计算网格值上的某个函数,您可以使用稀疏表示来执行此操作并获得 5×5 输出。
这是可能的,因为NumPy 数组的广播
创建极坐标的 NumPy 网格
到目前为止,我们已经了解了如何在笛卡尔坐标系中生成坐标网格。
然而,还存在另一种坐标系,称为“极坐标系”,它同样流行并且常用于各种应用中。
在这个系统中,我们使用半径 r(距原点)和角度 θ(与水平面)表示平面中的位置。
我们可以使用以下公式将笛卡尔坐标转换为极坐标,反之亦然
下图显示了同一平面上的两个坐标系,这将有助于您更好地理解它们之间的关系。
现在我们了解了极坐标系及其与笛卡尔坐标系的关系,并且由于我们已经创建了笛卡尔坐标系的网格,
让我们创建一个极坐标网格。
R = np.linspace(1,5,10)
THETA = np.linspace(0, np.pi, 45)
radii, thetas = np.meshgrid(R,THETA)
print("R:{}".format(R.shape))
print("THETA:{}".format(THETA.shape))
print("meshgrid radii:{}".format(radii.shape))
print("mehgrid thetas:{}".format(thetas.shape))
Output:
我们首先定义了半径的范围。它是从 1 到 5 的十个等距值。
然后,我们定义了角度的范围,从 0 到 π,或从 0° 到 180°。我们正在考虑此范围内的 45 个不同值。
然后,我们创建这些半径和角度的网格。
结果,我们得到两个矩阵,每个矩阵代表半径和角度。这两个矩阵的形状均为 45×10。
现在让我们尝试使用 pyplot 在极坐标平面上可视化这些生成的点。
import matplotlib.pyplot as plt
ax = plt.subplot(111, polar=True)
ax.plot(thetas, radii, marker='.', ls='none')
plt.show()
Output:
该图中绘制了 450 个点,代表由 45 个角度和 10 个半径创建的网格。
具有“矩阵索引”的 NumPy 网格网格
到目前为止我们观察到np.meshgrid
方法返回带有笛卡尔索引的坐标
这意味着第一个数组代表列(X 坐标),第二个数组代表行(Y 坐标)。
但是,如果您考虑计算机科学中使用的二维数组或矩阵,我们会使用“行优先”索引来访问此类数组,
即第一个坐标代表行,第二个坐标代表列。这种索引称为“矩阵索引”。
我们可以通过将字符串“in”分配给参数“来生成具有矩阵索引的网格网格indexing
' 的np.meshgrid
method.
i = np.array([1,2,3,4,5]) #rows
j = np.array([11, 12, 13, 14, 15]) #columns
ii, jj = np.meshgrid(i,j, indexing='ij')
print("row indices:\n{}\n".format(ii))
print("column indices:\n{}".format(jj))
Output:
如果仔细观察,这些是之前使用默认笛卡尔 (x, y) 索引生成的数组的转置。
让我们验证这一观察。
print("ii equal to xx transposed ? ==>",np.all(ii == xx.T))
print("jj equal to yy transposed ? ==>",np.all(jj == yy.T))
Output:
翻转 NumPy 网格
一旦我们有了 x 和 y 坐标,我们就可以通过操作各个输出数组来垂直或水平翻转网格。
让我们首先创建一个包含 150 个值的网格,并将其绘制出来,以便我们可以可视化网格的翻转。
X = np.linspace(1,15,15)
Y = np.linspace(20,30,10)
xx, yy = np.meshgrid(X,Y)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xx, yy, ls="None", marker=".")
plt.show()
Output:
该图包含网格中的 150 个点。每列都有相同颜色的点。
我们可以通过按列反转两个坐标数组来沿垂直轴翻转网格。
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xx[:,::-1], yy[:,::-1], ls="None", marker=".")
plt.show()
Output:
该图中的网格从上图中沿垂直轴翻转。
这通过第一列和最后一列、第二列和倒数第二列等之间点的颜色互换来表示。
使用 NumPy 矩阵创建网格网格
我们一直在使用一维 NumPy 数组创建网格。
但是,如果我们传递 2 维或更多维 NumPy 数组作为 x 和 y 的参数,会发生什么?
我们将使用NumPy 随机种子这样你就可以在你的计算机上获得相同的随机数。
np.random.seed(42)
a = np.random.randint(1,5, (2,2))
b = np.random.randint(6,10, (3,3))
print("a:\n{}\n".format(a))
print("b:\n{}".format(b))
xx, yy = np.meshgrid(a,b)
print("xx:\n{}".format(xx))
print("shape of xx:{}\n".format(xx.shape))
print("yy:\n{}".format(yy))
print("shape of yy:{}\n".format(yy.shape))
Output:
显然,无论输入参数的形状如何,只要输入数组的数量为 2,我们都会得到二维 NumPy 数组作为输出。
这相当于在创建网格之前展平输入数组。
xx, yy = np.meshgrid(a.ravel(),b.ravel()) #passing flattened arrays
print("xx:\n{}".format(xx))
print("shape of xx:{}\n".format(xx.shape))
print("yy:\n{}".format(yy))
print("shape of yy:{}\n".format(yy.shape))
Output:
输出与我们以原始形状传递 2D 数组来创建网格时得到的输出相同。
创建 3 维网格
到目前为止,我们只看到了 2D 平面中网格的构造。
通过提供 x 和 y 坐标数组,我们得到两个输出数组,每个数组对应 2D 平面中的 x 和 y 坐标。
现在让我们看看如何在 3D 空间中生成由 3 个坐标定义的网格。
X = np.linspace(1,4,4)
Y = np.linspace(6,8, 3)
Z = np.linspace(12,15,4)
xx, yy, zz = np.meshgrid(X,Y,Z)
print(xx.shape, yy.shape, zz.shape)
Output:
现在我们得到 3 个输出数组,每个数组对应 3D 空间中的 x、y 和 z 坐标。
而且,这三个数组都是3维的。
让我们将这些坐标可视化为3d plot.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xx, yy, zz)
ax.set_zlim(12, 15)
plt.show()
Output:
使用 NumPy 网格创建 3D 曲面图
现在让我们研究一下使用的应用之一np.meshgrid
,它正在创建 3D 绘图。
我们将首先创建 x 和 y 坐标的 2D 网格,并计算第三轴 (z) 值作为 x 和 y 的函数。
from mpl_toolkits.mplot3d import Axes3D
X = np.linspace(-20,20,100)
Y = np.linspace(-20,20,100)
X, Y = np.meshgrid(X,Y)
Z = 4*xx**2 + yy**2
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, cmap="plasma", linewidth=0, antialiased=False, alpha=0.5)
plt.show()
Output:
避免 NumPy 网格内存错误
当我们为变量赋值或执行任何计算时,这些值会存储在计算机的“临时”内存或 RAM 中。
这些内存的范围为 8–32 GB。
在创建网格时,我们需要小心,不要创建太大的网格,以至于内存无法容纳。
例如,让我们尝试创建一个大小为 100000×100000 浮点数的网格。
X = np.random.randn(100000)
Y = np.random.randn(100000)
xx,yy = np.meshgrid(X,Y)
Output:
在这里,我们尝试生成一个包含 100 亿个浮点数的网格。
如果每个浮点数占用 8 个字节的内存,那么 100 亿个这样的数字将需要大约 74 GB 的内存,这即使在现代个人计算机中也并不常见。
由于容纳如此大的网格所需的内存超出了可用内存,因此我们得到了这个“MemoryError”。
因此,我们必须小心,不要通过平滑数据或选择较小的坐标范围来创建太大的网格。
请注意,即使选择的大小很大并且在内存限制内,如果计算机上没有剩余可用内存,也可能会导致计算机冻结。
结论
在本教程中,我们了解了什么是网格及其各种优点。
我们学习了如何使用 NumPy 创建矩形坐标网格meshgrid
method.
我们还研究了这些网格的稀疏表示。
然后我们学习了如何使用 meshgrid 方法生成极坐标网格。
我们在创建网格时了解了两种类型的索引,即笛卡尔(默认)索引和矩阵索引。
之后,我们研究了如何翻转网格。我们还尝试通过传递二维数组作为输入来创建网格。
然后我们使用 3 个数组生成 3D 网格,每个数组对应 x、y 和 z 维度。
然后我们研究了网格网格方法在创建 3D 曲面图中的应用。
最后,我们研究了创建网格时出现的“内存错误”问题以及如何避免它们。