NumPy Reshape 详解:数组变形完全指南
NumPy 是 Python 科学计算的核心库,提供了高性能的多维数组对象(ndarray)以及处理这些数组的工具。在数据处理和机器学习中,经常需要改变数组的形状而不改变其数据。这就是数组变形(Reshaping)的作用。本文将详细介绍 NumPy 中各种数组变形的方法,助您全面掌握这一重要功能。
1. 什么是数组变形(Reshaping)?
数组变形是指改变数组的维度(轴的数量)或每个维度的大小,但保持数组元素的总数不变。例如,一个包含 12 个元素的一维数组可以被变形为 3 行 4 列的二维数组,或 2 层 2 行 3 列的三维数组。数据的排列顺序可能会因为变形方式而改变。
2. np.reshape() 函数和 ndarray.reshape() 方法
这是 NumPy 中最常用也是最核心的变形工具。它们的功能基本相同,np.reshape(a, newshape, order='C') 是一个函数,而 a.reshape(newshape, order='C') 是 ndarray 对象的方法。
基本用法:指定所有维度
您需要提供一个元组作为 newshape 参数,其中包含所有新维度的大小。
“`python
import numpy as np
创建一个包含12个元素的一维数组
arr = np.arange(12)
print(“原始数组:”, arr)
print(“原始形状:”, arr.shape) # (12,)
变形为 3 行 4 列的二维数组
reshaped_arr = arr.reshape((3, 4))
print(“\n变形为 (3, 4) 的数组:”)
print(reshaped_arr)
print(“新形状:”, reshaped_arr.shape) # (3, 4)
变形为 2 层 2 行 3 列的三维数组
reshaped_3d_arr = arr.reshape((2, 2, 3))
print(“\n变形为 (2, 2, 3) 的数组:”)
print(reshaped_3d_arr)
print(“新形状:”, reshaped_3d_arr.shape) # (2, 2, 3)
“`
使用 -1 进行维度推断
当您不确定某个维度的大小,或者希望 NumPy 自动计算它时,可以使用 -1 作为 newshape 元组中的一个值。NumPy 会根据数组元素的总数和已知的维度来推断该维度的大小。但请注意,只能有一个维度被设置为 -1。
“`python
变形为 4 列,行数自动推断
reshaped_arr_auto_rows = arr.reshape((-1, 4))
print(“\n变形为 (-1, 4) 的数组(行数自动推断):”)
print(reshaped_arr_auto_rows)
print(“新形状:”, reshaped_arr_auto_rows.shape) # (3, 4)
变形为 3 行,列数自动推断
reshaped_arr_auto_cols = arr.reshape((3, -1))
print(“\n变形为 (3, -1) 的数组(列数自动推断):”)
print(reshaped_arr_auto_cols)
print(“新形状:”, reshaped_arr_auto_cols.shape) # (3, 4)
“`
order 参数:控制元素读取/写入顺序
reshape 函数有一个 order 参数,它决定了如何从原始数组中读取元素并填充到新数组中。
* 'C' (默认值,C-style):以行优先(row-major)顺序读取/写入元素。这意味着最右边的轴变化最快。
* 'F' (Fortran-style):以列优先(column-major)顺序读取/写入元素。这意味着最左边的轴变化最快。
* 'A' (Any-order):如果数组在内存中是 Fortran 连续的,则使用 'F' 顺序;否则使用 'C' 顺序。
* 'K' (Keep-order):尽可能保持元素在内存中的现有顺序。
“`python
原始数组(行优先)
arr_2d = np.arange(6).reshape(2, 3)
print(“\n原始 2D 数组:”)
print(arr_2d)
[[0 1 2]
[3 4 5]]
使用 ‘C’ 顺序变形为 (3, 2)
reshaped_c_order = arr_2d.reshape(3, 2, order=’C’)
print(“\n使用 ‘C’ 顺序变形为 (3, 2):”)
print(reshaped_c_order)
[[0 1]
[2 3]
[4 5]]
使用 ‘F’ 顺序变形为 (3, 2)
reshaped_f_order = arr_2d.reshape(3, 2, order=’F’)
print(“\n使用 ‘F’ 顺序变形为 (3, 2):”)
print(reshaped_f_order)
[[0 3]
[1 4]
[2 5]]
``order` 参数对于处理数据的连续性(特别是与外部库交互时)至关重要。
理解
注意: reshape 操作通常返回原始数组的一个视图(view),这意味着新数组和原始数组共享底层数据缓冲区。修改其中一个数组会影响另一个。如果需要一个独立的数据副本,请在 reshape 后调用 .copy() 方法。
3. 其他常用的数组变形方法
除了 reshape,NumPy 还提供了许多专门用于特定变形场景的方法。
3.1. 展平数组:np.ravel() 和 ndarray.flatten()
这两个函数都将多维数组展平为一维数组。
* np.ravel(a, order='C'):通常返回一个视图,如果原始数组在内存中是连续的。否则会返回一个副本。它也可以指定 order 参数。
* ndarray.flatten(order='C'):总是返回一个展平后的数组副本,不共享数据。它也可以指定 order 参数。
“`python
arr_2d = np.array([[0, 1, 2],
[3, 4, 5]])
使用 ravel
flat_ravel = np.ravel(arr_2d)
print(“\n使用 ravel 展平:”, flat_ravel) # [0 1 2 3 4 5]
使用 flatten
flat_flatten = arr_2d.flatten()
print(“使用 flatten 展平:”, flat_flatten) # [0 1 2 3 4 5]
修改 flat_flatten 不会影响 arr_2d
flat_flatten[0] = 99
print(“修改 flatten 结果后,原数组:\n”, arr_2d)
“`
3.2. 增加维度:np.newaxis 或 None,以及 np.expand_dims()
在某些情况下,例如将一维数组作为单行或单列传递给需要二维输入的函数时,需要增加维度。
-
使用
np.newaxis或None: 这是最简洁、常用的方法。它们是同一个东西。
在索引中放置np.newaxis或None可以增加一个维度。“`python
arr_1d = np.array([1, 2, 3])
print(“\n原始一维数组:”, arr_1d, “形状:”, arr_1d.shape) # (3,)增加一个维度,使其成为行向量 (1, 3)
row_vector = arr_1d[np.newaxis, :] # 或者 arr_1d[None, :]
print(“作为行向量:”, row_vector, “形状:”, row_vector.shape) # (1, 3)增加一个维度,使其成为列向量 (3, 1)
col_vector = arr_1d[:, np.newaxis] # 或者 arr_1d[:, None]
print(“作为列向量:”, col_vector, “形状:”, col_vector.shape) # (3, 1)在中间增加维度
arr_2d = np.arange(6).reshape(2, 3)
arr_3d_expanded = arr_2d[:, np.newaxis, :]
print(“\n在中间增加维度 (2, 1, 3):\n”, arr_3d_expanded, “\n形状:”, arr_3d_expanded.shape)
“` -
使用
np.expand_dims(a, axis): 更显式的方法,可以指定在哪一个axis上增加维度。“`python
arr_1d = np.array([1, 2, 3])在轴 0 上增加维度 (1, 3)
expanded_axis_0 = np.expand_dims(arr_1d, axis=0)
print(“\n使用 expand_dims (axis=0):”, expanded_axis_0, “形状:”, expanded_axis_0.shape)在轴 1 上增加维度 (3, 1)
expanded_axis_1 = np.expand_dims(arr_1d, axis=1)
print(“使用 expand_dims (axis=1):”, expanded_axis_1, “形状:”, expanded_axis_1.shape)
“`
3.3. 移除维度:np.squeeze()
当数组中存在大小为 1 的维度(也称为单例维度)时,np.squeeze() 可以移除这些维度。
“`python
arr_single_dim = np.zeros((1, 3, 1, 2))
print(“\n原始数组 (1, 3, 1, 2):”, arr_single_dim.shape)
移除所有单例维度
squeezed_arr = np.squeeze(arr_single_dim)
print(“移除单例维度后:”, squeezed_arr.shape) # (3, 2)
指定要移除的轴
squeezed_axis_0 = np.squeeze(arr_single_dim, axis=0)
print(“移除 axis=0 后:”, squeezed_axis_0.shape) # (3, 1, 2)
“`
3.4. 转置数组:np.transpose() 和 ndarray.T
转置操作用于交换数组的轴。对于二维数组,这会将行变为列,列变为行。
ndarray.T属性: 方便地用于二维数组,将其转置。np.transpose(a, axes=None): 更通用,可以用于任意维度的数组,并通过axes参数指定轴的重排顺序。
“`python
arr_2d = np.array([[1, 2, 3],
[4, 5, 6]])
print(“\n原始 2D 数组:\n”, arr_2d)
使用 .T 属性
transposed_arr_T = arr_2d.T
print(“使用 .T 转置:\n”, transposed_arr_T) # 形状 (3, 2)
使用 transpose 函数(对于 2D 数组,默认也是交换轴)
transposed_arr_func = np.transpose(arr_2d)
print(“使用 transpose 函数转置:\n”, transposed_arr_func) # 形状 (3, 2)
对于 3D 数组,可以指定轴的顺序
arr_3d = np.arange(24).reshape((2, 3, 4))
print(“\n原始 3D 数组形状:”, arr_3d.shape) # (2, 3, 4)
将轴 0 和轴 1 交换 (3, 2, 4)
transposed_3d = np.transpose(arr_3d, axes=(1, 0, 2))
print(“转置 3D 数组形状 (axes=(1, 0, 2)):”, transposed_3d.shape) # (3, 2, 4)
“`
4. 组合和拆分数组的函数(间接变形)
虽然这些函数不是直接的“变形”,但它们通过组合或拆分数组来创建新的形状。
np.concatenate((a1, a2, ...), axis=0): 沿现有轴连接一系列数组。np.stack((a1, a2, ...), axis=0): 沿着一个新轴堆叠一系列数组,增加一个维度。np.split(),np.array_split(),np.hsplit(),np.vsplit(),np.dsplit(): 拆分数组。
“`python
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
沿 axis=0 连接 (行堆叠)
concat_rows = np.concatenate((arr1, arr2), axis=0)
print(“\n沿 axis=0 连接:\n”, concat_rows)
[[1 2]
[3 4]
[5 6]
[7 8]]
沿 axis=1 连接 (列堆叠)
concat_cols = np.concatenate((arr1, arr2), axis=1)
print(“沿 axis=1 连接:\n”, concat_cols)
[[1 2 5 6]
[3 4 7 8]]
沿新轴堆叠 (增加一个维度)
stacked_arr = np.stack((arr1, arr2), axis=0)
print(“堆叠 arr1 和 arr2 (axis=0):\n”, stacked_arr, “\n形状:”, stacked_arr.shape)
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
“`
5. 常见陷阱与最佳实践
- 元素总数必须匹配: 变形前后数组的元素总数必须保持一致。例如,一个 12 个元素的数组不能变形为
(5, 3)(15个元素)。 - 视图 vs. 副本: 记住
reshape、ravel通常返回视图,而flatten总是返回副本。当您修改数组并希望原数组不受影响时,请务必使用.copy()。 - 理解
order参数: 这对于确保数据正确排列至关重要,尤其是在与需要特定内存布局的外部库(如深度学习框架)交互时。 - 可视化数组: 对于复杂的多维数组变形,手动绘制或使用
print()语句观察数组内容和形状是理解其变化的有效方法。 - 使用
np.newaxis优于reshape添加单例维度: 在索引中使用np.newaxis通常更简洁明了,特别是在广播(Broadcasting)操作中。
6. 总结
NumPy 的数组变形功能是数据处理和机器学习中的基石。通过 np.reshape()、np.ravel()、np.flatten()、np.newaxis、np.squeeze() 和 np.transpose() 等工具,您可以灵活地操纵数组的形状,以满足算法和数据分析的需求。掌握这些技术将大大提高您使用 NumPy 的效率和能力。