Skip to content

数组变形与合并 ​

在数据处理中,经常需要改变数组的形状、拆分或合并数组。本章介绍 NumPy 提供的各种变形与组合操作。

一、改变形状 ​

1、reshape ​

python
import numpy as np

a = np.arange(12)             # shape (12,)

# 变为 3×4
b = a.reshape(3, 4)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

# 变为 2×2×3
c = a.reshape(2, 2, 3)
# [[[ 0  1  2]
#   [ 3  4  5]]
#  [[ 6  7  8]
#   [ 9 10 11]]]

# 使用 -1 自动推算
d = a.reshape(3, -1)    # shape (3, 4) — 自动算出 4
e = a.reshape(-1, 6)    # shape (2, 6) — 自动算出 2
f = a.reshape(2, 2, -1) # shape (2, 2, 3)

-1 的妙用

reshape 中最多只能有一个维度使用 -1,NumPy 会根据总元素数自动推算该维度的大小。例如 a.reshape(3, -1) 中,总元素 12 ÷ 3 = 4,所以 -1 变为 4。

2、resize(原地修改) ​

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

# np.resize: 返回新数组(可以改变总元素数)
b = np.resize(a, (2, 2))
print(b)   # [[1 2]
           #  [3 4]]

c = np.resize(a, (2, 4))
print(c)   # [[1 2 3 4]
           #  [5 6 1 2]]  — 重复填充

# a.resize: 原地修改(不返回新数组)
a.resize((2, 3))
print(a)
# [[1 2 3]
#  [4 5 6]]

3、ravel 与 flatten ​

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

# ravel: 返回视图(如果可能),更快
b = a.ravel()         # [1 2 3 4 5 6]  (C-order)
c = a.ravel(order='F') # [1 4 2 5 3 6] (Fortran-order)

# flatten: 总是返回副本
d = a.flatten()       # [1 2 3 4 5 6]
d[0] = 99
print(a)              # 不变 — 副本

b[0] = 99
print(a)              # 被修改 — 视图

4、转置与轴变换 ​

python
a = np.arange(24).reshape(2, 3, 4)

# .T 转置(反转所有轴)
print(a.T.shape)      # (4, 3, 2)

# transpose: 自定义轴顺序
b = a.transpose(2, 0, 1)   # shape (4, 2, 3)
# 原轴 2 → 新轴 0
# 原轴 0 → 新轴 1
# 原轴 1 → 新轴 2

# swapaxes: 交换两个轴
c = a.swapaxes(0, 2)       # shape (4, 3, 2)
# 等价于 a.transpose(2, 1, 0)

# moveaxis: 移动轴到新位置
d = np.moveaxis(a, 0, -1)  # shape (3, 4, 2)

5、expand_dims 与 squeeze ​

python
a = np.array([1, 2, 3])   # shape (3,)

# expand_dims: 增加维度
b = np.expand_dims(a, axis=0)  # shape (1, 3)
c = np.expand_dims(a, axis=1)  # shape (3, 1)

# 等价写法
d = a[np.newaxis, :]           # shape (1, 3)
e = a[:, np.newaxis]           # shape (3, 1)

# squeeze: 移除大小为 1 的维度
f = np.array([[[1, 2, 3]]])   # shape (1, 1, 3)
g = np.squeeze(f)              # shape (3,)
h = np.squeeze(f, axis=0)     # shape (1, 3) — 只移除指定轴

二、数组拼接 ​

1、np.concatenate ​

沿已有轴拼接:

python
a = np.array([[1, 2],
              [3, 4]])          # shape (2, 2)

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

# 沿 axis=0(行方向)拼接
c = np.concatenate([a, b], axis=0)
# [[1 2]
#  [3 4]
#  [5 6]]                       shape (3, 2)

d = np.array([[7],
              [8]])              # shape (2, 1)

# 沿 axis=1(列方向)拼接
e = np.concatenate([a, d], axis=1)
# [[1 2 7]
#  [3 4 8]]                     shape (2, 3)

2、np.stack ​

沿新轴堆叠(要求数组形状完全相同):

python
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([7, 8, 9])

# 沿新 axis=0 堆叠
d = np.stack([a, b, c], axis=0)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]              shape (3, 3)

# 沿新 axis=1 堆叠
e = np.stack([a, b, c], axis=1)
# [[1 4 7]
#  [2 5 8]
#  [3 6 9]]              shape (3, 3)

3、便捷函数 ​

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

# hstack: 水平拼接(沿 axis=1)
print(np.hstack([a, b]))
# [[1 2 5 6]
#  [3 4 7 8]]

# vstack: 垂直拼接(沿 axis=0)
print(np.vstack([a, b]))
# [[1 2]
#  [3 4]
#  [5 6]
#  [7 8]]

# dstack: 深度拼接(沿 axis=2)
print(np.dstack([a, b]))
# [[[1 5]
#   [2 6]]
#  [[3 7]
#   [4 8]]]              shape (2, 2, 2)

# column_stack: 按列拼接(一维数组先变为列向量)
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
print(np.column_stack([x, y]))
# [[1 4]
#  [2 5]
#  [3 6]]

三、数组拆分 ​

1、np.split ​

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

# 沿 axis=0 拆分为 3 份
parts = np.split(a, 3, axis=0)
# [array([[0, 1, 2, 3]]),
#  array([[4, 5, 6, 7]]),
#  array([[ 8,  9, 10, 11]])]

# 沿 axis=1 按位置拆分
parts = np.split(a, [1, 3], axis=1)
# [array([[0], [4], [8]]),          — 第 0 列
#  array([[ 1,  2], [ 5,  6], [ 9, 10]]),  — 第 1-2 列
#  array([[ 3], [ 7], [11]])]        — 第 3 列

2、便捷函数 ​

python
a = np.arange(12).reshape(3, 4)

# hsplit: 水平拆分(沿 axis=1)
left, right = np.hsplit(a, 2)
# left:  [[0 1] [4 5] [8 9]]
# right: [[2 3] [6 7] [10 11]]

# vsplit: 垂直拆分(沿 axis=0)
top, bottom = np.vsplit(a, [2])
# top:    [[0 1 2 3] [4 5 6 7]]
# bottom: [[8 9 10 11]]

# dsplit: 深度拆分(沿 axis=2)
c = np.arange(24).reshape(2, 3, 4)
front, back = np.dsplit(c, 2)

四、数组重复与平铺 ​

1、np.tile ​

整体重复数组:

python
a = np.array([1, 2, 3])

print(np.tile(a, 3))          # [1 2 3 1 2 3 1 2 3]
print(np.tile(a, (2, 1)))
# [[1 2 3]
#  [1 2 3]]
print(np.tile(a, (2, 3)))
# [[1 2 3 1 2 3 1 2 3]
#  [1 2 3 1 2 3 1 2 3]]

2、np.repeat ​

逐元素重复:

python
a = np.array([1, 2, 3])

print(np.repeat(a, 3))        # [1 1 1 2 2 2 3 3 3]
print(np.repeat(a, [1, 2, 3]))# [1 2 2 3 3 3]  — 每个元素重复不同次数

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

print(np.repeat(b, 2, axis=0))
# [[1 2]
#  [1 2]
#  [3 4]
#  [3 4]]

print(np.repeat(b, 2, axis=1))
# [[1 1 2 2]
#  [3 3 4 4]]

五、数组翻转与旋转 ​

python
a = np.arange(6).reshape(2, 3)
# [[0 1 2]
#  [3 4 5]]

# 上下翻转
print(np.flipud(a))
# [[3 4 5]
#  [0 1 2]]

# 左右翻转
print(np.fliplr(a))
# [[2 1 0]
#  [5 4 3]]

# 沿指定轴翻转
print(np.flip(a, axis=0))   # 同 flipud
print(np.flip(a, axis=1))   # 同 fliplr

# 旋转 90°
print(np.rot90(a))
# [[2 5]
#  [1 4]
#  [0 3]]
print(np.rot90(a, k=2))     # 旋转 180°

六、数组排序 ​

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

# 返回排序后的副本
print(np.sort(a))          # [1 1 2 3 4 5 6 9]

# 原地排序
a.sort()
print(a)                    # [1 1 2 3 4 5 6 9]

# 返回排序索引
a = np.array([3, 1, 4, 1, 5])
idx = np.argsort(a)
print(idx)                  # [1 3 0 2 4]
print(a[idx])               # [1 1 3 4 5]

# 二维排序
b = np.array([[3, 1],
              [1, 4],
              [4, 2]])

# 按列排序
print(np.sort(b, axis=0))
# [[1 1]
#  [3 2]
#  [4 4]]

# 按行排序
print(np.sort(b, axis=1))
# [[1 3]
#  [1 4]
#  [2 4]]

# 部分排序(最快找前 k 个最小值)
print(np.partition(a, 3))   # 前 3 个位置放最小的 3 个值

📖 官方文档