前言

这份文件讲什么?

这是一份专门给代码基础很弱的学生准备的 reshape() 教学文件。

它重点解决这些最常见的问题:

  1. shape 到底是什么意思
  2. reshape() 到底在干什么
  3. -1 到底是什么意思
  4. 为什么有时候写两个参数,有时候写三个参数
  5. 为什么机器学习里总是先 reshape 再标准化、再 reshape 回去
  6. reshape(-1, 1)reshape(1, -1)reshape(-1, 4)reshape(-1, 5, 8) 分别是什么意思

这份 notebook 的目标不是让你“背语法”,而是让你真正理解:

数组只是换了排布方式,总元素个数并没有变。


一、先从 shape 开始:什么叫“形状”?

在 NumPy 里,每个数组都有一个 shape,表示:

这个数组现在是按什么结构排布的

例如:

  • (6,) 表示一维数组,一共有 6 个元素
  • (2, 3) 表示二维数组,2 行 3 列
  • (2, 3, 4) 表示三维数组,可以理解成有 2 个块,每个块里是 3 行 4 列

重点提醒

shape 不是在说数组“值是多少”,而是在说:

这些值现在是怎么摆放的

import numpy as np

x = np.array([1, 2, 3, 4, 5, 6])

print("x =")
print(x)
print("x.shape =", x.shape)

二、reshape() 到底在干什么?

reshape() 的作用是:

不改变元素总个数,只改变这些元素的排布方式

例如,原来是一维数组:

[1, 2, 3, 4, 5, 6]

你可以把它改成二维数组:

[[1, 2, 3],
 [4, 5, 6]]

虽然长得不一样,但里面的 6 个数字还是同样那 6 个数字。

最重要的规则

reshape() 前后,总元素个数必须一样

x_2d = x.reshape(2, 3)

print("x.reshape(2, 3) =")
print(x_2d)
print("shape =", x_2d.shape)

三、为什么 (2, 3) 可以,但 (2, 4) 不可以?

因为原数组里一共只有 6 个元素。

如果写:

reshape(2, 3)

表示:

  • 2 行
  • 3 列

总共需要:

2 * 3 = 6

正好对得上。

如果写:

reshape(2, 4)

表示:

  • 2 行
  • 4 列

总共需要:

2 * 4 = 8

但原数组只有 6 个元素,所以不行。

print("原数组元素总数 =", x.size)

print("\n可以的例子:reshape(3, 2)")
print(x.reshape(3, 2))

print("\n不可以的例子:reshape(2, 4)")
try:
    print(x.reshape(2, 4))
except Exception as e:
    print("报错信息:", e)

四、-1 到底是什么意思?

-1 的意思是:

这一维我不手工算了,让 NumPy 自动帮我算

例如:

x.reshape(-1, 1)

意思是:

  • 列数固定为 1
  • 行数让 NumPy 自动算

因为原数组一共有 6 个元素,所以 NumPy 会自动算出:

  • 6 行
  • 1 列

所以结果 shape 是 (6, 1)

x_col = x.reshape(-1, 1)

print("x.reshape(-1, 1) =")
print(x_col)
print("shape =", x_col.shape)

五、再看一个:reshape(-1, 2) 是什么意思?

x.reshape(-1, 2)

意思是:

  • 每行 2 个元素
  • 一共多少行让 NumPy 自动算

因为总共有 6 个元素,所以会自动算出:

  • 3 行
  • 2 列

也就是 shape = (3, 2)

x_3_2 = x.reshape(-1, 2)

print("x.reshape(-1, 2) =")
print(x_3_2)
print("shape =", x_3_2.shape)

六、reshape(2, -1) 又是什么意思?

这次反过来:

x.reshape(2, -1)

意思是:

  • 行数固定 2
  • 列数让 NumPy 自动算

因为总共有 6 个元素,分成 2 行后,每行就必须有 3 个元素。

所以结果 shape 是 (2, 3)

x_2_auto = x.reshape(2, -1)

print("x.reshape(2, -1) =")
print(x_2_auto)
print("shape =", x_2_auto.shape)

七、为什么 -1 很常用?

因为很多时候你只知道:

  • 我希望每行有几个特征
  • 但我不想自己去手工算一共有多少行

这时就可以写 -1

例如:

reshape(-1, 4)

意思就是:

  • 每行有 4 个特征
  • 一共有多少行,NumPy 自动算

这在机器学习里特别常见。


八、reshape() 里为什么有时候是两个参数?

当你写:

reshape(a, b)

你是在把数组变成二维

二维数组最常见的理解方式是:

  • 第 1 维:行数
  • 第 2 维:列数

在机器学习里,二维数组经常表示:

(样本数, 特征数)

例如:

  • (100, 2):100 个样本,每个样本 2 个特征
  • (640, 8):640 个样本,每个样本 8 个特征
a = np.arange(12)

print("原数组 a =")
print(a)
print("a.shape =", a.shape)

a_2d = a.reshape(3, 4)
print("\na.reshape(3, 4) =")
print(a_2d)
print("shape =", a_2d.shape)

九、reshape() 里为什么有时候是三个参数?

当你写:

reshape(a, b, c)

你是在把数组变成三维

三维数组在机器学习里很常见于:

  • 时序数据
  • 视频数据
  • 多通道信号
  • 批量图像

时序数据最常见的写法

(样本数, 时间步, 特征数)

例如:

  • (640, 5, 8) 表示:
    • 640 个样本
    • 每个样本 5 个时间步
    • 每个时间步 8 个特征
b = np.arange(24)

b_3d = b.reshape(2, 3, 4)

print("b.reshape(2, 3, 4) =")
print(b_3d)
print("shape =", b_3d.shape)

十、为什么机器学习里经常“先拉平,再 reshape 回去”?

这和很多作业题非常相关。

例如,原始输入可能是时序数据:

(样本数, 时间步, 特征数)

但像 StandardScaler() 这样的工具通常要求输入是二维:

(样本数, 特征数)

所以常见流程是:

第一步:先把三维拉平成二维

比如:

X.reshape(-1, 8)

第二步:做标准化

scaler.fit_transform(...)

第三步:再 reshape 回三维

比如:

.reshape(-1, 5, 8)

这就是为什么很多时序题里你会看到一连串 reshape。

X_demo = np.arange(80).reshape(2, 10, 4)

print("原始三维数据 X_demo.shape =", X_demo.shape)

X_flat = X_demo.reshape(-1, 4)
print("拉平成二维后 X_flat.shape =", X_flat.shape)

X_back = X_flat.reshape(-1, 10, 4)
print("再变回三维后 X_back.shape =", X_back.shape)

十一、为什么这里拉平成 (-1, 4)

因为原来三维数据的最后一维是 4,表示:

每个时间步有 4 个特征

我们做标准化时,仍然希望:

  • 每一列对应一个固定特征
  • 不要把特征混乱掉

所以 reshape 成:

(-1, 4)

意思就是:

  • 前面样本数和时间步数先合并
  • 每一行保留 4 个特征

这样标准化器就能“按列处理每个特征”。


十二、为什么 reshape(-1, 1)reshape(1, -1) 完全不同?

这两个虽然都用了 -1,但意思完全不一样。

reshape(-1, 1)

表示:

  • 变成一列
  • 行数自动算

结果像这样:

[[1],
 [2],
 [3],
 [4]]

shape 是 (4, 1)

reshape(1, -1)

表示:

  • 变成一行
  • 列数自动算

结果像这样:

[[1, 2, 3, 4]]

shape 是 (1, 4)

c = np.array([1, 2, 3, 4])

print("c.reshape(-1, 1) =")
print(c.reshape(-1, 1))
print("shape =", c.reshape(-1, 1).shape)

print("\nc.reshape(1, -1) =")
print(c.reshape(1, -1))
print("shape =", c.reshape(1, -1).shape)

十三、为什么线性回归里经常写 reshape(-1, 1)

因为很多 sklearn 模型要求输入 X 必须是二维。

如果只有一个特征,例如:

x = [1, 2, 3, 4]

它原本 shape 是:

(4,)

这是一个一维数组。

但模型想要的是:

(4, 1)

意思是:

  • 4 个样本
  • 每个样本 1 个特征

所以就常写:

x.reshape(-1, 1)
x_lin = np.array([1, 2, 3, 4])

print("原 shape =", x_lin.shape)
print("reshape(-1, 1) 后 shape =", x_lin.reshape(-1, 1).shape)
print(x_lin.reshape(-1, 1))

十四、为什么 RNN / LSTM 题里常写 reshape(-1, 5, 8)

因为 RNN / LSTM 读的是三维时序数据。

例如:

(样本数, 时间步, 特征数)

如果题目规定:

  • 每个样本 5 个时间步
  • 每个时间步 8 个特征

那么最终喂给 LSTM 的 shape 就应该是:

(-1, 5, 8)

这里的 -1 表示:

  • 有多少个样本,让 NumPy 自动算
z = np.arange(160)

z_seq = z.reshape(-1, 5, 8)

print("z.reshape(-1, 5, 8) 后 shape =", z_seq.shape)
print("也就是:样本数自动算,每个样本 5 个时间步,每步 8 个特征")

十五、reshape() 最常见的报错原因是什么?

1. 总元素个数对不上

例如原来只有 6 个元素,却想 reshape 成 (2, 4),这是不行的。

2. 同时写了两个 -1

例如:

reshape(-1, -1)

这是不行的,因为 NumPy 不知道该自动算哪一维。

3. 逻辑上 shape 虽然对,但语义不对

例如本来最后一维是“特征数”,你却写错成别的数,虽然总元素个数对得上,但机器学习意义错了。

print("错误示例 1:元素数对不上")
try:
    print(np.arange(6).reshape(2, 4))
except Exception as e:
    print("报错:", e)

print("\n错误示例 2:同时写两个 -1")
try:
    print(np.arange(6).reshape(-1, -1))
except Exception as e:
    print("报错:", e)

十六、你应该怎么判断 reshape 写得对不对?

以后遇到 reshape,不要死记,直接问自己这 4 个问题:

1. 原数组一共有多少个元素?

先看 .size

2. 你想变成几维?

  • 二维?
  • 三维?

3. 每一维在机器学习里代表什么?

例如:

  • 样本数
  • 时间步数
  • 特征数

4. reshape 前后,总元素个数是否一致?

这是硬性要求。

demo = np.arange(24)

print("demo.size =", demo.size)
print("demo.reshape(2, 3, 4).shape =", demo.reshape(2, 3, 4).shape)
print("2 * 3 * 4 =", 2 * 3 * 4)

十七、最短记忆版

你可以记住下面几句:

  • reshape():改形状,不改总元素个数
  • -1:这一维让 NumPy 自动算
  • reshape(-1, 1):变成一列
  • reshape(1, -1):变成一行
  • reshape(-1, 4):二维,每行 4 个特征
  • reshape(-1, 5, 8):三维,每个样本 5 个时间步、每步 8 个特征

十八、课堂追问

  1. reshape() 会不会改变数组里的数字?
  2. 为什么 reshape() 前后元素总数必须一致?
  3. -1 到底是什么意思?
  4. reshape(-1, 1)reshape(1, -1) 有什么区别?
  5. 为什么 StandardScaler() 前常常先把三维数据拉平成二维?
  6. 为什么 RNN / LSTM 的输入通常是三维数组?
  7. 如果原数组有 80 个元素,写 reshape(-1, 10, 4) 时,NumPy 会自动算出多少个样本?

总结

本文从零开始,系统讲解了 NumPy 中 reshape() 函数的用法,重点解决了以下几个核心概念:

  • shape(形状):只是元素的排列方式,不改变元素本身。
  • reshape 规则:元素总数必须前后一致,否则会报错。
  • -1 的含义:让 NumPy 自动推算这一维的大小,极大简化了代码。
  • 二维与三维:分别对应 (样本数, 特征数)(样本数, 时间步, 特征数) 的典型机器学习数据格式。
  • 典型应用:将一维特征转为二维供 sklearn 使用 reshape(-1,1);标准化前将时序数据拉平成二维,处理完再还原;构建 LSTM 输入时用 reshape(-1, time_steps, features)

理解这些基础后,再遇到任何 reshape 都不会慌张。记住的关键一句话:reshape 只是换种摆法,数字一个不少。

最后,不妨尝试回答文中十八个课堂追问,检验一下自己是否真的掌握了 reshape 的精髓。

更多推荐