掌握 NumPy reshape:改变数组形状、增减维度与控制元素顺序

2026-07-20 37 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

numpy.reshape() 改变的是数组的维度结构,而不是元素总数。它可以把一维数据整理成矩阵、增加批次或通道维度,也可以把高维数组重新展平。真正容易出错的地方,是维度乘积、-1 自动推断、元素排列顺序,以及新数组是否与原数组共享内存。

形状可以改变,元素数量不能改变

一个数组能否重塑,取决于新旧形状包含的元素总数是否一致。例如,包含 12 个元素的一维数组,可以变成 (3, 4)(2, 2, 3)(12, 1),但不能变成 (5, 3)

下面的示例可以直接运行。运行前安装 NumPy:

python -m pip install numpy
import numpy as np

values = np.arange(12)

matrix = values.reshape(3, 4)
cube = values.reshape(2, 2, 3)
column = values.reshape(12, 1)

print("原始形状:", values.shape)
print("矩阵形状:", matrix.shape)
print(matrix)
print("三维形状:", cube.shape)
print("列向量形状:", column.shape)

如果执行 values.reshape(5, 3),NumPy 会抛出 ValueError,因为 12 个元素无法填满 15 个位置。处理外部数据时,不要只依赖异常信息,可以先验证数量:

import numpy as np

values = np.arange(12)
target_shape = (3, 4)

if np.prod(target_shape) != values.size:
    raise ValueError("目标形状与元素数量不匹配")

result = values.reshape(target_shape)
print(result)

-1 推断一个维度

当某个维度可以由元素总数计算时,可以把它写成 -1。这在样本数量动态变化时尤其有用:

import numpy as np

records = np.arange(24)

# 每条记录包含 6 个值,让 NumPy 推断记录数量。
table = records.reshape(-1, 6)
print(table.shape)  # (4, 6)

# 保留第一维,把后面的维度展平。
batch = np.arange(24).reshape(2, 3, 4)
features = batch.reshape(batch.shape[0], -1)
print(features.shape)  # (2, 12)

一次重塑中只能出现一个 -1。像 reshape(-1, -1) 这样的写法没有足够信息确定结果,因此会失败。

增加和移除维度

reshape() 不仅能重新分组元素,也能添加长度为 1 的轴。这些轴常用于批处理、广播和机器学习模型输入。

import numpy as np

image = np.arange(12).reshape(3, 4)

# 增加批次维度:(height, width) -> (batch, height, width)
batched = image.reshape(1, 3, 4)

# 增加尾部通道维度:(height, width) -> (height, width, channels)
grayscale = image.reshape(3, 4, 1)

# 再恢复为二维数组。
restored = grayscale.reshape(3, 4)

print(batched.shape)   # (1, 3, 4)
print(grayscale.shape) # (3, 4, 1)
print(restored.shape)  # (3, 4)

如果意图只是删除所有长度为 1 的轴,np.squeeze() 往往表达得更清楚;如果只是增加轴,np.expand_dims()None 索引也更明确。reshape() 更适合已知完整目标形状的场景。

order 决定元素如何被读取和写入

默认的 order="C" 按最后一个轴变化最快的顺序处理元素。order="F" 则按第一个轴变化最快的顺序处理。两者使用相同的数据,却可能得到不同的二维排列:

import numpy as np

values = np.arange(1, 7)

by_rows = values.reshape(2, 3, order="C")
by_columns = values.reshape(2, 3, order="F")

print("C 顺序:")
print(by_rows)

print("F 顺序:")
print(by_columns)

输出分别为:

C 顺序:
[[1 2 3]
 [4 5 6]]
F 顺序:
[[1 3 5]
 [2 4 6]]

这里的 order 描述索引遍历和重排规则,不等同于强制结果采用某种底层连续内存布局。读取 Fortran、MATLAB 或某些科学计算程序生成的数据时,应先确认对方使用的维度约定和存储顺序。

共享内存不是可以默认依赖的契约

reshape() 会在条件允许时返回原数组的视图,否则可能创建副本。调用方不应仅凭一次实验就假定两者总是共享数据。需要判断时,可以使用 np.shares_memory()

import numpy as np

source = np.arange(12)
reshaped = source.reshape(3, 4)

print(np.shares_memory(source, reshaped))

reshaped[0, 0] = 99
print(source[0])

# 如果后续代码必须拥有独立数据,应显式复制。
independent = source.reshape(3, 4).copy()
independent[0, 0] = -1
print(source[0])

对切片、转置或非连续数组再次执行 reshape() 时,是否复制更值得关注。性能敏感代码应测量实际内存占用;需要隔离修改时,应明确调用 .copy()

使用前的检查清单

使用 reshape() 时,可以快速检查以下事项:

  • 新形状的维度乘积是否等于 array.size
  • 是否只使用了一个 -1
  • 长度为 1 的轴代表批次、通道还是单纯的占位维度。
  • 数据来源是否要求 CF 顺序。
  • 后续代码是否会原地修改结果,以及是否需要显式复制。

reshape() 本身并不复杂,复杂的是形状背后的数据语义。把每个轴的含义写清楚,并在接口边界检查 shape,通常比反复修正维度错误更可靠。


相关推荐