如何在 Python 中重塑 NumPy 数组

探索 NumPy 数组的重塑技巧

本教程将深入讲解如何运用 NumPy 的 reshape() 函数来调整 NumPy 数组的结构,而无需触及原始数据。

在处理 NumPy 数组时,你可能经常需要将现有的数组转换为具有不同维度的形式。 当你在数据处理过程中执行多个步骤时,这个需求尤其突出。

NumPy reshape() 提供了一种简洁的方法来实现这一目标。 在接下来的内容中,你将掌握 reshape() 的使用方法,并学会将数组转换成各种维度。

理解 NumPy 数组的重塑

在 NumPy 数组的运用中,你可能会先创建一个一维的数字数组。 之后,你需要将其转化为符合需求的特定维度数组。

当新数组的维度在初始阶段未知或需要在运算过程中推导时,这种重塑操作显得尤为重要。 此外,某些特定的数据处理步骤也可能需要输入具有特定形状的数据。

这时,重塑就派上了用场。

举个例子,想象一下一个由 6 个元素组成的一维数组,我们称之为向量。 我们可以将其重塑为 2×3、3×2 或 6×1 等不同形状的数组。

▶️ 为了更好地理解本教程中的示例,请确保你的环境中已经安装了 Python 和 NumPy。 如果你还没有安装 NumPy,请参考我们的 NumPy 安装指南。

现在,你可以通过运行 import numpy as np 来导入 NumPy 并将其命名为 np,以便在后续代码中使用。

接下来,我们将深入探讨 reshape() 的语法。

NumPy reshape() 的语法

以下是 NumPy reshape() 函数的基本语法:

np.reshape(arr, newshape, order="C"|"F"|"A")
  • arr:这是一个有效的 NumPy 数组对象,也就是你需要进行重塑的数组。
  • newshape:这是新数组的形状。 它可以是一个整数或一个元组。
  • newshape 是一个整数时,返回的数组将是一维的。
  • order:这个参数定义了读取需要重塑的数组元素的顺序。
  • 默认值是 'C',意味着数组元素将按照类似 C 的索引顺序(从 0 开始)读取。
  • 'F' 代表类似 Fortran 的索引顺序(从 1 开始)。 而 'A' 则根据数组 arr 的内存布局,选择类似 C 或类似 Fortran 的顺序读取元素。

那么 np.reshape() 会返回什么呢?

它会尽可能返回原始数组的重塑视图。 如果无法返回视图,它会返回数组的一个副本。

上面提到,NumPy 的 reshape() 函数会尽量返回视图。 接下来,我们将深入探讨视图和副本之间的差异。

NumPy 数组的视图与副本

顾名思义,副本是原始数组的一个独立复制版本。 对副本的任何修改都不会影响到原始数组。

而视图,则是原始数组的一种重塑形式。 这意味着对视图的任何修改都会直接反映到原始数组,反之亦然。

运用 NumPy reshape() 将一维数组转化为二维数组

#1. 首先,我们使用 np.arange() 创建一个示例数组。

我们需要一个包含 1 到 12 共 12 个数字的数组,我们称之为 arr1。 由于 NumPy 的 arange() 函数默认不包含结束值,所以我们将结束值设置为 13。

现在,让我们运用上面的语法,将 arr1 重塑为一个形状为 (4, 3) 的二维数组。 我们将这个数组命名为 arr2,它将包含 4 行和 3 列。

import numpy as np

arr1 = np.arange(1,13)
print("原始数组,重塑前:\n")
print(arr1)

# 重塑数组
arr2 = np.reshape(arr1,(4,3))
print("\n重塑后的数组:")
print(arr2)

让我们看看原始数组和重塑后的数组。

原始数组,重塑前:

[ 1  2  3  4  5  6  7  8  9 10 11 12]

重塑后的数组:
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]

除了将数组作为参数传递给 np.reshape(),你还可以在原始数组上直接调用 .reshape() 方法。

你可以运行 dir(arr1) 来查看数组对象 arr1 可用的所有方法和属性。

dir(arr1)

# 输出 
[
...
...
'reshape'
...
..
]

从上面的代码单元中,你可以看到 .reshape() 是一个可以应用到现有 NumPy 数组 arr1 上的有效方法。

▶️ 因此,你也可以使用以下更简洁的语法来重塑 NumPy 数组。

arr.reshape(d0,d1,...,dn)

# 其中:

# d0, d1,..,dn 是重塑后数组的维度

# d0 * d1 * ...* dn = N,arr 中的元素总数

在接下来的教程中,我们将使用这种更简洁的语法来演示。

#2. 让我们尝试将我们的 12 元素向量重塑为一个 12 x 1 的数组。

import numpy as np

arr1 = np.arange(1,13)
print("原始数组,重塑前:\n")
print(arr1)

# 重塑数组
arr3 = arr1.reshape(12,1)
print("\n重塑后的数组:")
print(arr3)

在下面的输出中,你可以看到数组已成功重塑。

原始数组,重塑前:

[ 1  2  3  4  5  6  7  8  9 10 11 12]

重塑后的数组:
[[ 1]
 [ 2]
 [ 3]
 [ 4]
 [ 5]
 [ 6]
 [ 7]
 [ 8]
 [ 9]
 [10]
 [11]
 [12]]

❔ 那么,我们如何确定得到的是副本还是视图呢?

你可以调用返回数组的基本属性来检查。

  • 如果数组是副本,那么基本属性将返回 None
  • 如果数组是视图,那么基本属性将返回原始数组。

让我们快速验证一下。

arr3.base
# 输出
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

正如你所看到的,arr3base 属性返回了原始数组。 这意味着我们得到的是原始数组的视图。

#3. 现在,让我们尝试将向量重塑为另一个有效的 2 x 6 数组。

import numpy as np

arr1 = np.arange(1,13)
print("原始数组,重塑前:\n")
print(arr1)

# 重塑数组
arr4 = arr1.reshape(2,6)
print("\n重塑后的数组:")
print(arr4)

这是输出:

原始数组,重塑前:

[ 1  2  3  4  5  6  7  8  9 10 11 12]

重塑后的数组:
[[ 1  2  3  4  5  6]
 [ 7  8  9 10 11 12]]

在下一节中,我们将把 arr1 重塑为一个三维数组。

运用 NumPy reshape() 将一维数组转化为三维数组

要将 arr1 重塑为一个三维数组,我们将所需的维度设置为 (1, 4, 3)。

import numpy as np

arr1 = np.arange(1,13)
print("原始数组,重塑前:\n")
print(arr1)

# 重塑数组
arr3D = arr1.reshape(1,4,3)
print("\n重塑后的数组:")
print(arr3D)

我们现在已经创建了一个与原始数组 arr1 包含相同 12 个元素的三维数组。

原始数组,重塑前:

[ 1  2  3  4  5  6  7  8  9 10 11 12]

重塑后的数组:
[[[ 1  2  3]
  [ 4  5  6]
  [ 7  8  9]
  [10 11 12]]]

如何在重塑过程中调试值错误

如果你还记得 reshape() 的语法,那么只有当维度乘积等于数组中的元素数量时,重塑操作才有效。

import numpy as np

arr1 = np.arange(1,13)
print("原始数组,重塑前:\n")
print(arr1)

# 重塑数组
arr2D = arr1.reshape(4,4)
print("\n重塑后的数组:")
print(arr2D)

在这里,我们尝试将一个包含 12 个元素的数组重塑为一个 4×4 的数组,而后者应该包含 16 个元素。 因此,解释器会抛出一个值错误,如下所示。

原始数组,重塑前:

[ 1  2  3  4  5  6  7  8  9 10 11 12]
-----------------------------------------------------------
ValueError                                
Traceback (most recent call last)
<ipython-input-11-63552bcc8c37> in <module>()
      6 
      7 # 重塑数组
----> 8 arr2 = arr1.reshape(4,4)
      9 print("\n重塑后的数组:")
     10 print(arr2)

ValueError: cannot reshape array of size 12 into shape (4,4)

为了避免此类错误,你可以使用 -1 来自动推断其中一个维度的形状,使其与元素总数相匹配。

例如,如果你事先知道 n-1 个维度,你可以使用 -1 来推断重塑数组中的第 n 个维度。

假设你有一个包含 24 个元素的数组,并且你想将其重塑为一个三维数组。 你需要 4 行和 3 列,你可以沿着第三个维度传入 -1 的值。

import numpy as np

arr1 = np.arange(1,25)
print("原始数组,重塑前:\n")
print(arr1)

# 重塑数组
arr_res = arr1.reshape(4,3,-1)
print("\n重塑后的数组:")
print(arr_res)
print(f"arr_res 的形状:{arr_res.shape}")

当你检查形状数组的形状时,你会发现重塑后的数组在第三维的形状为 2。

原始数组,重塑前:

[ 1  2  3  4  5  6  7  8  9 10 11 12
13 14 15 16 17 18 19 20 21 22 23 24]

重塑后的数组:
[[[ 1  2]
  [ 3  4]
  [ 5  6]]

 [[ 7  8]
  [ 9 10]
  [11 12]]

 [[13 14]
  [15 16]
  [17 18]]

 [[19 20]
  [21 22]
  [23 24]]]
arr_res 的形状:(4, 3, 2)

这种方法在扁平化数组时尤其有用,我们将在下一节中讨论。

运用 NumPy reshape() 扁平化数组

有时,你可能需要从一个 N 维数组返回到一个扁平数组。 例如,你可能需要将一个图像扁平化为一个长像素向量。

让我们使用以下步骤创建一个简单的示例:

  • 生成一个 3×3 的灰度图像数组 img_arr,像素值范围为 0 到 255。
  • 接下来,扁平化 img_arr 并打印扁平化后的数组 flat_arr
  • 此外,打印 img_arrflat_arr 的形状以进行验证。
img_arr = np.random.randint(0, 255, (3,3))
print(img_arr)
print(f"img_arr 的形状: {img_arr.shape}")
flat_arr = img_arr.reshape(-1)
print(flat_arr)
print(f"flat_arr 的形状: {flat_arr.shape}")

以下是输出结果。

[[195 145  77]
 [ 63 193 223]
 [215  43  36]]
img_arr 的形状: (3, 3)

[195 145  77  63 193 223 215  43  36]
flat_arr 的形状: (9,)

在上面的代码单元中,你可以看到 flat_arr 是一个包含 9 个像素值的一维向量。

总结👩‍🏫

是时候快速回顾一下我们所学的内容了。

  • 使用 np.reshape(arr, newshape)arr 重塑为 newshape 中指定的形状。 newshape 是一个元组,指定了重塑后数组的维度。
  • 或者,使用 arr.reshape(d0, d1, ..., dn)arr 重塑为形状 d0 x d1 x ... x dn
  • 请务必检查 d0 * d1 * ...* dn = N,其中 N 是原始数组中的元素数量,以避免重塑过程中的值错误。
  • 如果你想自动推断维度,可以在新形状中最多使用一个维度设置为 -1
  • 最后,你可以使用 arr.reshape(-1) 来扁平化数组。

现在你已经掌握了如何使用 NumPy 的 reshape() 函数,接下来可以深入了解 NumPy 的 linspace() 函数的工作原理。

如果你愿意,可以在 Jupyter notebook 中尝试这些代码示例。 如果你在寻找其他开发环境,可以参考我们的 Jupyter 替代品指南。