跳到主要内容

数组变形与操作

学习目标

  • 掌握 reshape、flatten、ravel 等变形操作
  • 学会数组的拼接(concatenate、stack、hstack、vstack)
  • 学会数组的分割(split、hsplit、vsplit)
  • 理解转置和轴交换

reshape:改变形状

reshape 是最常用的变形操作——在不改变数据的前提下改变数组的形状。

基本用法

import numpy as np

arr = np.arange(12) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(arr.shape) # (12,)

# 变成 3 行 4 列
m1 = arr.reshape(3, 4)
print(m1)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]

# 变成 4 行 3 列
m2 = arr.reshape(4, 3)
print(m2)
# [[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]

# 变成 2×2×3 的三维数组
m3 = arr.reshape(2, 2, 3)
print(m3)
# [[[ 0 1 2]
# [ 3 4 5]]
# [[ 6 7 8]
# [ 9 10 11]]]
元素总数必须一致

reshape 前后的元素总数必须相同,否则报错:

arr = np.arange(12)    # 12 个元素
arr.reshape(3, 5) # ❌ 报错!3 × 5 = 15 ≠ 12
arr.reshape(3, 4) # ✅ 3 × 4 = 12

用 -1 自动计算

-1 表示"让 NumPy 自动计算这个维度":

arr = np.arange(12)

# 我想要 3 行,列数你帮我算
m1 = arr.reshape(3, -1) # 自动算出 4 列
print(m1.shape) # (3, 4)

# 我想要 4 列,行数你帮我算
m2 = arr.reshape(-1, 4) # 自动算出 3 行
print(m2.shape) # (3, 4)

# 变成一列(列向量)
col = arr.reshape(-1, 1)
print(col.shape) # (12, 1)
-1 只能用一次

reshape 中最多只有一个维度可以是 -1。因为只有一个未知数才能算出来。

arr.reshape(-1, -1)  # ❌ 报错!不能有两个 -1

flatten 和 ravel:展平数组

把多维数组变回一维:

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

# flatten:返回拷贝(修改不影响原数组)
flat = matrix.flatten()
print(flat) # [1 2 3 4 5 6]
flat[0] = 99
print(matrix[0, 0]) # 1 ← 原数组不变

# ravel:返回视图(修改会影响原数组)
rav = matrix.ravel()
print(rav) # [1 2 3 4 5 6]
rav[0] = 99
print(matrix[0, 0]) # 99 ← 原数组也变了!
方法返回类型修改是否影响原数组速度
flatten()拷贝不影响较慢(要复制数据)
ravel()视图影响较快(不复制)
reshape(-1)视图影响较快

数组拼接

concatenate:通用拼接

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

# 一维拼接
c = np.concatenate([a, b])
print(c) # [1 2 3 4 5 6]

二维拼接需要指定方向(axis):

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

# axis=0:上下拼接(行数增加)
v = np.concatenate([m1, m2], axis=0)
print(v)
# [[1 2]
# [3 4]
# [5 6]
# [7 8]]

# axis=1:左右拼接(列数增加)
h = np.concatenate([m1, m2], axis=1)
print(h)
# [[1 2 5 6]
# [3 4 7 8]]

vstack 和 hstack:快捷拼接

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

# vstack = vertical stack = 上下拼接 = concatenate(axis=0)
print(np.vstack([m1, m2]))
# [[1 2]
# [3 4]
# [5 6]
# [7 8]]

# hstack = horizontal stack = 左右拼接 = concatenate(axis=1)
print(np.hstack([m1, m2]))
# [[1 2 5 6]
# [3 4 7 8]]

stack:创建新维度

stackconcatenate 的区别是——stack增加一个维度

a = np.array([1, 2, 3])   # shape: (3,)
b = np.array([4, 5, 6]) # shape: (3,)

# stack 沿新维度堆叠
s0 = np.stack([a, b], axis=0) # 相当于"横着放"
print(s0)
# [[1 2 3]
# [4 5 6]]
print(s0.shape) # (2, 3)

s1 = np.stack([a, b], axis=1) # 相当于"竖着放"
print(s1)
# [[1 4]
# [2 5]
# [3 6]]
print(s1.shape) # (3, 2)

拼接方法总结

函数作用维度变化
np.concatenate()沿已有轴拼接维度不变,某个轴变长
np.vstack()上下拼接行数增加
np.hstack()左右拼接列数增加
np.stack()沿新轴堆叠增加一个维度

数组分割

split:均匀分割

arr = np.arange(12)   # [ 0  1  2  3  4  5  6  7  8  9 10 11]

# 均匀分成 3 份
parts = np.split(arr, 3)
print(parts[0]) # [0 1 2 3]
print(parts[1]) # [4 5 6 7]
print(parts[2]) # [8 9 10 11]

# 按指定位置分割
parts2 = np.split(arr, [3, 7]) # 在索引 3 和 7 处切
print(parts2[0]) # [0 1 2]
print(parts2[1]) # [3 4 5 6]
print(parts2[2]) # [7 8 9 10 11]

二维分割

matrix = np.arange(16).reshape(4, 4)
print(matrix)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]
# [12 13 14 15]]

# vsplit:上下分割
top, bottom = np.vsplit(matrix, 2)
print(top)
# [[0 1 2 3]
# [4 5 6 7]]

# hsplit:左右分割
left, right = np.hsplit(matrix, 2)
print(left)
# [[ 0 1]
# [ 4 5]
# [ 8 9]
# [12 13]]

转置与轴交换

二维转置

转置就是行变列,列变行

matrix = np.array([
[1, 2, 3],
[4, 5, 6]
])
print(matrix.shape) # (2, 3)

# 转置
t = matrix.T
print(t)
# [[1 4]
# [2 5]
# [3 6]]
print(t.shape) # (3, 2)

# 也可以用 transpose
t2 = matrix.transpose()
print(np.array_equal(t, t2)) # True

添加维度:np.newaxis 和 expand_dims

有时候我们需要给数组增加一个维度(比如把行向量变成列向量):

arr = np.array([1, 2, 3])      # shape: (3,)

# 方法 1:np.newaxis
row = arr[np.newaxis, :] # shape: (1, 3) 行向量
col = arr[:, np.newaxis] # shape: (3, 1) 列向量
print(row) # [[1 2 3]]
print(col)
# [[1]
# [2]
# [3]]

# 方法 2:np.expand_dims
row2 = np.expand_dims(arr, axis=0) # 在 axis=0 处添加维度 → (1, 3)
col2 = np.expand_dims(arr, axis=1) # 在 axis=1 处添加维度 → (3, 1)

# 方法 3:reshape
row3 = arr.reshape(1, -1) # (1, 3)
col3 = arr.reshape(-1, 1) # (3, 1)

压缩维度:squeeze

去掉大小为 1 的维度:

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

squeezed = arr.squeeze()
print(squeezed.shape) # (3,)
print(squeezed) # [1 2 3]

实战:数据重组

import numpy as np

# 场景:你有 12 个月的销售数据(一维)
monthly_sales = np.array([
120, 135, 150, 180, 200, 210,
195, 188, 220, 250, 280, 310
])

# 重组成 4 个季度 × 3 个月
quarterly = monthly_sales.reshape(4, 3)
print("季度数据:")
print(quarterly)
# [[120 135 150] Q1
# [180 200 210] Q2
# [195 188 220] Q3
# [250 280 310]] Q4

# 每季度总销售额
q_totals = quarterly.sum(axis=1)
quarters = ["Q1", "Q2", "Q3", "Q4"]
for q, total in zip(quarters, q_totals):
print(f" {q}: {total}")

# 上半年 vs 下半年
first_half, second_half = np.vsplit(quarterly, 2)
print(f"\n上半年总额: {first_half.sum()}")
print(f"下半年总额: {second_half.sum()}")

小结

操作函数说明
改变形状reshape()元素总数不变,改变维度排列
展平flatten() / ravel()多维变一维
拼接concatenate() / vstack() / hstack()多个数组合并
堆叠stack()合并并增加一个维度
分割split() / vsplit() / hsplit()一个数组拆成多个
转置.T / transpose()行列互换
增加维度np.newaxis / expand_dims()添加 size=1 的维度
压缩维度squeeze()去掉 size=1 的维度

动手练习

练习 1:reshape 练习

arr = np.arange(24)

# 1. 变成 4×6 的矩阵
# 2. 变成 2×3×4 的三维数组
# 3. 变成 6 行(列数自动计算)
# 4. 把 (2,3,4) 数组展平回一维

练习 2:拼接与分割

# 有 3 个班的成绩数据
class_a = np.array([[85, 90], [78, 82], [92, 88]]) # 3 人 × 2 科
class_b = np.array([[76, 80], [95, 91], [83, 87]]) # 3 人 × 2 科
class_c = np.array([[88, 92], [71, 75], [90, 85]]) # 3 人 × 2 科

# 1. 把 3 个班的成绩合并成一个 9×2 的矩阵
# 2. 如果有第 3 科成绩需要补充,怎么拼接?
extra_scores = np.array([[70], [65], [80], [75], [90], [85], [78], [72], [88]])
# 3. 把合并后的 9×3 矩阵按每 3 人分割回 3 组

练习 3:数据重组

# 一年 365 天的温度数据(假数据)
np.random.seed(42)
daily_temps = np.random.uniform(low=-5, high=38, size=360) # 取 360 天方便分割

# 1. 重组成 12 个月 × 30 天
# 2. 计算每月平均温度
# 3. 找出最热和最冷的月份
# 4. 计算上半年和下半年的平均温度差