Skip to content

第 5 章 形状操作

学习目标

  • 掌握 reshape-1 自动推断
  • 区分 ravelflatten(视图与副本)
  • 掌握转置 Tswapaxes
  • 掌握拼接:concatenatevstackhstackstack
  • 掌握拆分:splitvsplithsplit
  • 会用 newaxissqueeze 增删维度

5.1 形状与 reshape

形状(shape)是描述数组各维度长度的元组。reshape(新形状) 在不改变元素个数与顺序的前提下重新排列维度,第 2 章已见基本用法。

一个实用技巧:-1 表示「这个维度自动推断」,一次 reshape 只需指定一个维度,另一个交给 NumPy:

python
import numpy as np

a = np.arange(12)
print(a.reshape(2, -1))    # 2 行,列数自动 = 12/2 = 6
print(a.reshape(-1, 4))    # 4 列,行数自动 = 12/4 = 3

输出:

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

注意 -1 只能出现一次。元素总数不能被指定维度整除时,-1 推断失败会报错:

>>> np.arange(12).reshape(5, -1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: cannot reshape array of size 12 into shape (5,newaxis)

5.2 ravel 与 flatten:展平

展平是把多维数组变成一维。ravel() 返回视图(尽量不复制,修改会影响原数组);flatten() 返回副本(总是新数组)。

python
import numpy as np

b = np.arange(6).reshape(2, 3)
r = b.ravel()          # 视图
r[0] = 99
print(b)               # 原数组被修改

输出:

[[99  1  2]
 [ 3  4  5]]

改用 flatten() 则不互相影响:

python
import numpy as np

b = np.arange(6).reshape(2, 3)
f = b.flatten()        # 副本
f[0] = -1
print(b)               # 原数组不变
print(f)

输出:

[[0 1 2]
 [3 4 5]]
[-1 1 2 3 4 5]

5.3 转置

转置交换行列:原 (m, n) 数组变成 (n, m),用属性 T:

python
import numpy as np

m = np.arange(6).reshape(2, 3)
print(m)
print(m.T)
print(m.T.shape)

输出:

[[0 1 2]
 [3 4 5]]
[[0 3]
 [1 4]
 [2 5]]
(3, 2)

m.T 是视图,修改它也会影响原数组。三维以上数组的轴交换用 swapaxes(轴1, 轴2):

python
import numpy as np

m = np.arange(6).reshape(2, 3)
print(m.swapaxes(0, 1))    # 等价于转置

输出:

[[0 3]
 [1 4]
 [2 5]]

5.4 拼接:把数组连起来

np.concatenate([数组1, 数组2, ...]) 沿已有轴拼接,默认 axis=0

一维拼接直接连成一串:

python
import numpy as np

x = np.array([1, 2, 3])
y = np.array([4, 5])
print(np.concatenate([x, y]))

输出:

[1 2 3 4 5]

二维沿行(axis=0,上下叠)或沿列(axis=1,左右拼):

python
import numpy as np

m1 = np.array([[1, 2], [3, 4]])
m2 = np.array([[5, 6]])       # 1 行 2 列
m3 = np.array([[7], [8]])     # 2 行 1 列

print(np.concatenate([m1, m2], axis=0))   # 行方向:上下
print(np.concatenate([m1, m3], axis=1))   # 列方向:左右

输出:

[[1 2]
 [3 4]
 [5 6]]
[[1 2 7]
 [3 4 8]]

记忆法:axis=0 是行方向(数组向下生长),axis=1 是列方向(数组向右生长)。axis 的完整含义第 6 章讲。

concatenate 要求:除拼接轴外,其余维度必须一致——上例中 axis=0 拼接要求列数相同(都是 2),axis=1 拼接要求行数相同(都是 2)。

vstack(垂直拼接)hstack(水平拼接)是常用简写,省去写 axis:

python
import numpy as np

m1 = np.array([[1, 2], [3, 4]])
m2 = np.array([[5, 6]])
m3 = np.array([[7], [8]])

print(np.vstack([m1, m2]))    # 等价 axis=0
print(np.hstack([m1, m3]))    # 等价 axis=1

输出:

[[1 2]
 [3 4]
 [5 6]]
[[1 2 7]
 [3 4 8]]

np.stack 与 concatenate 不同:它新增一个维度,把每个数组作为新维度的元素:

python
import numpy as np

s = np.stack([np.array([1, 2]), np.array([3, 4])])
print(s)
print(s.shape)    # 2 个一维数组 → (2, 2)

输出:

[[1 2]
 [3 4]]
(2, 2)

5.5 拆分:把数组切开

np.split(数组, 份数) 沿 axis=0 均分,返回数组的列表:

python
import numpy as np

a = np.arange(12)
parts = np.split(a, 3)
print(parts)           # 3 份的列表
print(parts[1])        # 取第 2 份

输出:

[array([0, 1, 2, 3]), array([4, 5, 6, 7]), array([ 8,  9, 10, 11])]
[4 5 6 7]

二维拆分:按行拆用 vsplit,按列拆用 hsplit:

python
import numpy as np

m = np.arange(12).reshape(3, 4)
print(np.vsplit(m, 3))          # 3 行,每行一块
print(np.hsplit(m, 2)[0])       # 4 列均分 2 块,取第 1 块

输出:

[array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8,  9, 10, 11]])]
[[0 1]
 [4 5]
 [8 9]]

注意:均分要求总长度能被份数整除,否则报 ValueError

5.6 增删维度:newaxis 与 squeeze

数组[:, np.newaxis] 在指定位置插入一个长度为 1 的维度,把一维变「列向量」:

python
import numpy as np

v = np.array([1, 2, 3])
print(v[:, np.newaxis])
print(v[:, np.newaxis].shape)
print(v[np.newaxis, :].shape)

输出:

[[1]
 [2]
 [3]]
(3, 1)
(1, 3)

用途:让一维数组与二维数组「对齐」后广播(第 7 章的关键操作)。等价写法是 np.expand_dims(v, axis=1)

squeeze() 删除所有长度为 1 的维度:

python
import numpy as np

t = np.array([[1, 2, 3]])      # (1, 3)
print(t.shape, t.squeeze(), t.squeeze().shape)

输出:

(1, 3) [1 2 3] (3,)

动手实践

  1. 创建 np.arange(24),分别 reshape 成 (2, 12)(4, 6)(-1, 8),打印各形状。
  2. 创建 3×4 数组,用 T 转置并打印形状;验证转置是视图:修改转置结果,检查原数组是否变化。
  3. 把两个 2×3 数组分别做 vstackhstack,观察形状差异。
  4. 创建 12 个元素的数组,用 np.split(a, 3) 拆成 3 份并分别求和,验证三份之和等于原数组总和。

常见错误

错误写法现象原因
a.reshape(5, -1)(12 元素)ValueError: cannot reshape array of size 12 into shape (5,newaxis)5 不能整除 12,-1 推断不出整数
np.concatenate 两个列数不同的数组(axis=0)ValueError: all the input array dimensions except for the concatenation axis must match exactly拼接时非拼接轴必须一致
m.resize(3, 2) 后打印输出 Noneresize 是就地修改,返回 None;返回新数组用 np.resize
修改 ravel() 结果原数组变了ravel 返回视图;要副本用 flatten()
np.split(a, 3) 时长度不能整除ValueError: array split does not result in an equal division均分要求整除
stack 想拼接二维数组多出一个维度stack 是加维度堆叠;拼接用 concatenate/vstack/hstack

章末练习

基础

  1. np.arange(15).reshape(-1, 5) 的结果形状是什么?写出结果。
  2. 创建 2×4 数组,用 ravelflatten 各展平一次,打印两个结果。
  3. T 转置 2×5 数组,打印转置后的形状。

提高

  1. 把三个一维数组 [1, 2][3, 4][5, 6]vstack 拼成 3×2 数组,再转置成 2×3,打印最终结果。
  2. hsplit 把 4×6 数组按列拆成 3 块 4×2 数组,验证各块拼接回去等于原数组。

挑战

  1. 解释 np.stack([a, b], axis=1)np.concatenate([a, b], axis=1) 在二维输入下的形状差异,并用代码验证。
  2. 写代码把一维数组 [1, 2, 3] 变成 (3, 1) 的列向量,再用 np.broadcast_to 或逐元素运算让它与 np.array([[10, 20, 30]]) 相加,观察广播效果(第 7 章预告,先动手试试)。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. np.arange(12).reshape(2, -1) 的结果形状是?
    • A. (2, 6)
    • B. (2, 12)
    • C. (6, 2)
    • D. 报错
  2. 下列哪个返回视图(不是副本)?
    • A. flatten()
    • B. ravel()
    • C. copy()
    • D. np.array(a)
  3. np.array([[1, 2], [3, 4]]).T 的形状是?
    • A. (2, 2)
    • B. (2, 1)
    • C. (1, 2)
    • D. (4,)
  4. np.vstack 相当于 concatenate 的哪个 axis?
    • A. axis=1
    • B. axis=0
    • C. 不需要 axis
    • D. axis=-1
  5. np.concatenate([np.array([[1, 2]]), np.array([[3, 4]])], axis=0) 的结果形状是?
    • A. (1, 4)
    • B. (2, 2)
    • C. (4, 1)
    • D. 报错
  6. np.hsplit(m, 2) 对 4 列数组的结果是?
    • A. 2 个 2 列的数组
    • B. 2 个 4 列的数组
    • C. 4 个数组
    • D. 报错
  7. v[:, np.newaxis] 把形状 (3,) 变成?
    • A. (3, 3)
    • B. (3, 1)
    • C. (1, 3)
    • D. (1, 1)
  8. np.array([[1, 2, 3]]).squeeze() 的形状是?
    • A. (1, 3)
    • B. (3,)
    • C. (1, 1)
    • D. 报错
  9. np.stack([np.array([1, 2]), np.array([3, 4])]) 的形状是?
    • A. (2,)
    • B. (2, 2)
    • C. (4,)
    • D. (1, 4)
  10. reshape 报错 cannot reshape array of size 12 into shape (5,newaxis) 的原因?
    • A. 12 个元素太多
    • B. 5 不能整除 12
    • C. newaxis 拼写错误
    • D. reshape 只能用于一维