在实际数据处理中,经常需要调整数组的结构,如改变维度、展平、转置、合并或分割数组。NumPy提供了丰富且高效的形状操作函数,本节将详细介绍这些常用操作。
shape 属性
使用 shape 属性可以获取数组的当前形状,也可以直接为它赋值来改变形状(要求元素总数不变)。
import numpy as np
arr = np.arange(12)
print("原数组:", arr)
print("原形状:", arr.shape)
# 直接修改 shape 属性(原地修改)
arr.shape = (3, 4)
print("修改 shape 后:\n", arr)
print("新形状:", arr.shape)
输出:
原数组: [ 0 1 2 3 4 5 6 7 8 9 10 11]
原形状: (12,)
修改 shape 后:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
新形状: (3, 4)
reshape():返回新形状的视图
reshape(a, newshape) 返回一个具有新形状的视图(如果可能),不会修改原数组。如果无法返回视图(例如不连续内存),则返回副本。
arr = np.arange(12)
arr_2d = arr.reshape(3, 4) # 返回新数组(视图)
print("reshape(3,4):\n", arr_2d)
# 使用 -1 自动推断维度
arr_auto = arr.reshape(2, -1) # 自动计算列数 6
print("reshape(2,-1):\n", arr_auto)
# 验证是否为视图(共享内存)
arr[0] = 999
print("修改原数组后,arr_2d[0,0] =", arr_2d[0,0]) # 也会变
输出:
reshape(3,4):
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
reshape(2,-1):
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
修改原数组后,arr_2d[0,0] = 999
reshape 通常返回视图,但并非绝对。如果需要确保独立副本,可以先 copy() 再 reshape。
resize():改变形状(原地或返回新数组)
NumPy中有两个 resize:一个是 ndarray.resize() 方法(原地修改),另一个是 np.resize() 函数(返回新数组,可调整总元素数)。
# ndarray.resize() 原地修改(无返回值)
arr = np.arange(6)
arr.resize(2, 3) # 直接改变原数组
print("原地 resize(2,3):\n", arr)
# np.resize() 返回新数组,可以改变总元素数(重复或截断)
arr2 = np.arange(6)
new_arr = np.resize(arr2, (3, 4)) # 原数组6个元素,新形状12个元素,会重复原数组
print("np.resize(3,4):\n", new_arr)
输出:
原地 resize(2,3):
[[0 1 2]
[3 4 5]]
np.resize(3,4):
[[0 1 2 3]
[4 5 0 1]
[2 3 4 5]]
注意:np.resize 在元素不足时会循环使用原数组的数据填充。
ravel() 与 flatten()
ravel() 返回一维视图(尽可能),而 flatten() 返回一维副本。
arr = np.array([[1, 2, 3], [4, 5, 6]])
# ravel() 返回视图(如果可能)
r = arr.ravel()
print("ravel:", r)
r[0] = 999
print("修改 ravel 后原数组:\n", arr) # 原数组改变(因为是视图)
# flatten() 返回副本
f = arr.flatten()
f[0] = 888
print("修改 flatten 后原数组:\n", arr) # 原数组不变
输出:
ravel: [1 2 3 4 5 6]
修改 ravel 后原数组:
[[999 2 3]
[ 4 5 6]]
修改 flatten 后原数组:
[[999 2 3]
[ 4 5 6]]
transpose() 和 T 属性
对于二维数组,T 和 transpose() 效果相同(交换轴)。对于高维数组,transpose() 可以指定轴顺序。
arr = np.arange(6).reshape(2, 3)
print("原数组:\n", arr)
# T 属性
print("arr.T:\n", arr.T)
# transpose() 方法,默认也是交换轴
print("arr.transpose():\n", arr.transpose())
# 高维数组转置,指定轴顺序
arr3d = np.arange(24).reshape(2, 3, 4)
print("原三维数组 shape:", arr3d.shape)
arr_t = arr3d.transpose(1, 0, 2) # 将轴0和轴1交换
print("transpose(1,0,2) shape:", arr_t.shape)
输出:
原数组:
[[0 1 2]
[3 4 5]]
arr.T:
[[0 3]
[1 4]
[2 5]]
arr.transpose():
[[0 3]
[1 4]
[2 5]]
原三维数组 shape: (2, 3, 4)
transpose(1,0,2) shape: (3, 2, 4)
swapaxes()
swapaxes(axis1, axis2) 交换数组的两个轴,返回视图。
arr = np.arange(24).reshape(2, 3, 4)
print("原数组 shape:", arr.shape)
swapped = arr.swapaxes(0, 2) # 交换第1轴和第3轴
print("swapaxes(0,2) shape:", swapped.shape)
输出:
原数组 shape: (2, 3, 4)
swapaxes(0,2) shape: (4, 3, 2)
newaxis, expand_dims(), squeeze()
np.newaxis 或 None 可以增加一个新维度。expand_dims() 显式指定位置。squeeze() 删除所有长度为1的维度。
arr = np.array([1, 2, 3]) # shape (3,)
# 使用 newaxis
row_vec = arr[np.newaxis, :] # shape (1, 3)
col_vec = arr[:, np.newaxis] # shape (3, 1)
print("row_vec shape:", row_vec.shape)
print("col_vec shape:", col_vec.shape)
# 使用 expand_dims
expanded = np.expand_dims(arr, axis=1) # 在索引1处增加维度 -> (3,1)
print("expand_dims(axis=1) shape:", expanded.shape)
# squeeze 去除单维度
arr_sq = np.array([[[1], [2], [3]]]) # shape (1,3,1)
squeezed = np.squeeze(arr_sq) # shape (3,)
print("squeeze 后 shape:", squeezed.shape)
输出:
row_vec shape: (1, 3)
col_vec shape: (3, 1)
expand_dims(axis=1) shape: (3, 1)
squeeze 后 shape: (3,)
NumPy提供了多种合并数组的方式:concatenate, stack, vstack, hstack 等。
np.concatenate()沿现有轴拼接数组,要求除拼接轴外其他维度相同。
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
# 沿0轴拼接(垂直方向)
c = np.concatenate((a, b), axis=0)
print("concatenate axis=0:\n", c)
# 沿1轴拼接(水平方向),需要列数匹配
d = np.concatenate((a, b.T), axis=1) # b.T 变成 (2,1)
print("concatenate axis=1:\n", d)
输出:
concatenate axis=0:
[[1 2]
[3 4]
[5 6]]
concatenate axis=1:
[[1 2 5]
[3 4 6]]
np.vstack() 和 np.hstack()分别垂直(行)和水平(列)堆叠,更方便。
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print("vstack:\n", np.vstack((a, b))) # 变成二维 (2,3)
print("hstack:", np.hstack((a, b))) # 一维拼接 (6,)
输出:
vstack:
[[1 2 3]
[4 5 6]]
hstack: [1 2 3 4 5 6]
np.stack()沿新轴堆叠,增加一个维度。
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.stack((a, b), axis=0) # shape (2,3)
d = np.stack((a, b), axis=1) # shape (3,2)
print("stack axis=0:\n", c)
print("stack axis=1:\n", d)
输出:
stack axis=0:
[[1 2 3]
[4 5 6]]
stack axis=1:
[[1 4]
[2 5]
[3 6]]
使用 split, vsplit, hsplit 将数组分割成多个子数组。
arr = np.arange(12).reshape(3, 4)
print("原数组:\n", arr)
# 水平分割(按列):将数组分成3块
h1, h2, h3 = np.hsplit(arr, 3) # 每块1列(4列/3?实际如果无法均分会报错,这里4列分成3块会报错,所以改为2块)
h1, h2 = np.hsplit(arr, 2) # 分成2块,每块2列
print("hsplit 第1块:\n", h1)
print("hsplit 第2块:\n", h2)
# 垂直分割(按行)
v1, v2, v3 = np.vsplit(arr, 3) # 每块1行,共3行
print("vsplit 第1块:\n", v1)
# 通用的 split,指定轴
s1, s2 = np.split(arr, [1], axis=1) # 在第1列之后分割,得到两块:前1列,后3列
print("split at [1] axis=1:\n", s1, "\n", s2)
输出:
原数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
hsplit 第1块:
[[0 1]
[4 5]
[8 9]]
hsplit 第2块:
[[ 2 3]
[ 6 7]
[10 11]]
vsplit 第1块:
[[0 1 2 3]]
split at [1] axis=1:
[[0]
[4]
[8]]
[[ 1 2 3]
[ 5 6 7]
[ 9 10 11]]
| 操作 | 返回类型 | 说明 |
|---|---|---|
reshape() | 视图(尽可能) | 不改变原数组 |
resize() (方法) | 原地修改 | ndarray.resize() 无返回值 |
np.resize() | 副本 | 可调整总元素数 |
ravel() | 视图(尽可能) | 返回一维视图 |
flatten() | 副本 | 返回一维副本 |
transpose()/T | 视图 | 转置 |
swapaxes() | 视图 | 交换轴 |
squeeze() | 视图 | 去除单维度 |
concatenate()/stack() | 副本 | 合并数组(总是新数组) |
split() | 视图列表 | 分割后每个子数组是原数组的视图 |
本节介绍了NumPy中常用的数组形状操作,包括改变形状、展平、转置、增加/删除维度、合并与分割。灵活运用这些函数,可以高效地准备和组织数据,为后续的数学运算和分析打下基础。接下来,我们将学习NumPy的数组运算。