Numpy的核心要点
1.理解NumPy的核心对象——ndarray
核心特性
- 多维容器:可表示向量(1D)、矩阵(2D)、张量(3D+)。
- 同构数据类型:所有元素类型相同(如
int32
,float64
)。 - 高效内存管理:数据存储在连续内存块中,避免指针开销。
- 向量化操作:无需循环即可对整个数组进行数学运算。
关键属性
shape
:各维度大小的元组(如(2,3))dtype
:元素类型(如float64、int32)strides
:每个维度步长的字节数(内存布局)
print(arr.shape) # (2, 2)
print(arr.dtype) # int32 (默认类型)
import numpy as np
# 创建数组
arr = np.array([[1, 2, 3], [4, 5, 6]]) # 2x3矩阵
print("维度:", arr.ndim) # 2
print("形状:", arr.shape) # (2, 3)
print("数据类型:", arr.dtype) # int32
2.数组创建与初始化
常用方法:
方法 | 说明 | 示例 |
---|---|---|
np.array() |
从Python列表/元组创建 | np.array([1, 2, 3]) |
np.zeros() |
全零数组 | np.zeros((2, 3)) |
np.ones() |
全一数组 | np.ones((3, 2), dtype=float) |
np.arange() |
等差数列 | np.arange(0, 10, 2) → [0,2,4,6,8] |
np.linspace() |
等分区间 | np.linspace(0, 1, 5) → [0.0, 0.25, 0.5, 0.75, 1.0] |
np.random |
生成随机数组 | np.random.randn(3, 3) 生成标准正态分布数组 |
随机数组生成
# 生成0~1均匀分布的3x3矩阵
rand_arr = np.random.rand(3, 3)
# 生成标准正态分布的10个样本
normal_arr = np.random.randn(10)
3. 数组操作与变形
形状操作:
arr = np.arange(6) # [0,1,2,3,4,5]
# 变形为 2x3 矩阵
reshaped = arr.reshape(2, 3)
# 展平为 1D 数组
flattened = reshaped.flatten()
# 转置矩阵
transposed = reshaped.T
# 自动推导维度
reshaped = arr.reshape(3, -1) # -1表示自动计算该维度大小
合并与拆分:
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
# 垂直拼接(按行)
v_stack = np.vstack([a, b]) # 结果形状:(3, 2)
# 水平拆分(按列)
split_arr = np.hsplit(a, 2) # 得到两个数组 [array([[1], [3]]), array([[2], [4]])]
4.矢量化运算(无需循环)
矢量化(Vectorization)指直接对整个数组进行数学运算,无需显式编写循环。NumPy通过底层C代码实现高效批量操作,利用CPU的SIMD指令并行处理数据。
核心优势:
- 代码简洁:用一行代码替代多层循环。
- 性能卓越:比Python循环快10~100倍。
- 可读性强:更贴近数学公式的表达方式。
元素级运算
arr = np.array([1, 2, 3])
# 平方运算(无需循环)
squared = arr ** 2 # [1, 4, 9]
# 三角函数
sin_values = np.sin(arr)
矩阵运算
A = np.array([[1,2], [3,4]])
B = np.array([[5,6], [7,8]])
# 矩阵乘法
dot_product = A @ B # 或 np.dot(A, B)
# 结果:[[19, 22], [43, 50]]
# 逐元素乘法
elementwise = A * B # [[5, 12], [21, 32]]
最佳实践总结
操作 推荐方法 错误示例 数组与标量运算 arr + 5
使用循环逐个元素加5 数组间逐元素操作 arr1 * arr2
嵌套循环计算每个元素乘积 条件过滤 arr[arr > 0]
循环+if判断 数学函数应用 np.sin(arr)
循环调用math.sin() 关键提示:
- 始终优先使用NumPy内置函数而非Python循环。
- 利用广播机制简化维度不同的数组运算。
- 处理超大数据时监控内存使用(
arr.nbytes
查看字节数)。
5.广播机制(Broadcasting)
规则:(向多的看齐)
- 从右向左对齐维度,不足的维度补1。
- 每个维度的大小必须相等或其中一个为1。
示例:
# 矩阵每行加上一个向量
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
vector = np.array([10, 20, 30]) # 形状 (3,)
result = matrix + vector # 广播后结果:[[11,22,33], [14,25,36]]
运算过程:
vector
被广播为形状 (2,3):[[10, 20, 30], [10, 20, 30]]
- 逐元素相加:
[[11, 22, 33], [14, 25, 36]]
广播机制(Broadcasting)规则详解
NumPy的广播机制允许不同形状的数组进行算术运算,其核心规则如下:
1. 规则解析
维度对齐:从右向左逐个维度对齐,缺失的维度自动补1。
维度兼容:每个维度的大小必须满足以下条件之一:
相等
其中一个为1(此时该维度扩展为另一个数组对应维度的大小)
2. 广播步骤
对齐维度:将两个数组的维度向右对齐,不足的维度补1。
检查兼容性:每个维度的大小必须相等或其中一个为1。
扩展维度:将大小为1的维度扩展为另一个数组对应维度的大小。
3. 示例与验证
示例1:形状为 (3,4) 和 (4,) 的数组相加
import numpy as np A = np.ones((3, 4)) # 形状 (3,4) B = np.ones((4,)) # 形状 (4,) → 对齐后补1 → (1,4) C = A + B # 广播后形状 (3,4)
结果:运算成功,B沿第一个维度扩展3次。
示例2:形状为 (5,4,3) 和 (4,1) 的数组相加
D = np.ones((5, 4, 3)) # 形状 (5,4,3) E = np.ones((4, 1)) # 形状 (4,1) → 对齐后补1 → (1,4,1) F = D + E # 广播后形状 (5,4,3)
结果:E的三个维度分别扩展为5、4、3。
示例3:形状为 (2,3) 和 (3,2) 的数组相加
G = np.ones((2, 3)) H = np.ones((3, 2)) try: I = G + H # 尝试相加 except ValueError as e: print("错误信息:", e) # 输出:operands could not be broadcast together
结果:维度不兼容(3和2),触发
ValueError
。示例4:形状为 (3,1,5) 和 (4,1) 的数组相加
J = np.ones((3, 1, 5)) # 形状 (3,1,5) K = np.ones((4, 1)) # 形状 (4,1) → 对齐后补1 → (1,4,1) L = J + K # 广播后形状 (3,4,5)
结果:K的三个维度分别扩展为3、4、5。
示例5:标量与多维数组相加
M = np.ones((2, 3)) scalar = 5 # 标量 → 视为形状 () N = M + scalar # 标量广播为 (2,3)
结果:标量扩展为与M相同的形状。
4. 特殊场景与调试技巧
标量广播:标量被视为0维数组,自动补足所有维度为1。
scalar + np.ones((5, 3, 2)) # 标量广播为 (5,3,2)
维度为1的扩展:
P = np.ones((1, 4)) # 形状 (1,4) Q = np.ones((3, 1)) # 形状 (3,1) R = P + Q # 广播后形状 (3,4)
调试维度不匹配:
错误示例:形状为
(3,4)
和(4,3)
的数组相加。原因:第二个维度4和3不兼容。
5. 广播机制的内存优化
虚拟扩展:广播不会实际复制数据,而是通过“虚拟视图”模拟扩展。
内存高效:即使广播后的数组看似很大,内存占用仍与原数据一致。
6. 广播规则总结表
数组形状
对齐后形状
是否兼容
广播后形状
(3,4)
和(4,)
(3,4)
vs(1,4)
✅
(3,4)
(5,4,3)
和(4,1)
(5,4,3)
vs(1,4,1)
✅
(5,4,3)
(2,3)
和(3,2)
(2,3)
vs(3,2)
❌
报错
(3,1,5)
和(4,1)
(3,1,5)
vs(1,4,1)
✅
(3,4,5)
6. 高级索引
布尔索引:
data = np.array([3, 1, 4, 1, 5])
mask = data > 2
filtered = data[mask] # 输出 [3, 4, 5]
花式索引(Fancy Indexing):
arr = np.arange(25).reshape(5, 5)
# 选择第1行和第3行的第0列和第2列
selected = arr[[1, 3]][:, [0, 2]] # 或直接 arr[[1,3], [0,2]]
7. 性能优化原理
为何NumPy比纯Python快?
- 连续内存:数据存储在连续内存块,CPU缓存利用率高。
- 向量化操作:用C语言实现底层循环,避免Python解释器开销。
- SIMD指令:单指令多数据流加速计算(如同时处理多个浮点数)。
速度对比:
# Python列表求和
py_list = list(range(1_000_000))
%timeit sum(py_list) # 约 50ms
# NumPy数组求和
np_arr = np.array(py_list)
%timeit np.sum(np_arr) # 约 0.3ms(快约100倍)
8. 实际应用场景
数据标准化:
data = np.random.randn(100, 5) # 生成100行5列数据
mean = data.mean(axis=0) # 计算每列均值
std = data.std(axis=0) # 计算每列标准差
normalized = (data - mean) / std # 标准化
解线性方程组:
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
x = np.linalg.solve(A, b) # 解为 [2., 3.]
图像处理:
# 假设 image 是一个 100x100x3 的RGB图像数组
image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
# 提取红色通道
red_channel = image[:, :, 0]
# 转换为灰度图像
gray_image = np.mean(image, axis=2).astype(np.uint8)
9.核心要点总结
核心概念 | 关键点 |
---|---|
ndarray 结构 |
多维、同构数据类型、连续内存 |
矢量化运算 | 避免Python循环,直接操作整个数组 |
广播机制 | 自动扩展不同形状数组维度,简化代码 |
高级索引 | 布尔索引、花式索引、切片(返回视图而非副本) |
性能优势 | 内存连续 + 预编译C代码 + SIMD指令 |