NumPy 完全入门指南:核心数据结构与高效数值计算
在数据科学和机器学习领域,NumPy 是 Python 生态中最重要的基础库之一。它提供了高性能的多维数组对象 ndarray 以及丰富的数值运算函数。本文将带你从零开始学习 NumPy 的核心概念,包括数组创建、索引切片、形状操作、广播机制、通用函数等,并配有大量代码示例。
1. NumPy 是什么?为什么需要它?
NumPy (Numerical Python)是 Python 数值计算的核心库。它提供了 ndarray(N-dimensional array)对象,用于高效存储和操作大规模多维数据。
1.1 为什么需要 NumPy?
Python 原生的 list 在数值计算中存在明显不足:
- 运算慢:逐个元素运算需要显式循环,效率低。
- 不支持批量计算:无法直接对列表所有元素执行数学运算。
- 多维处理困难:嵌套列表处理高维数据非常繁琐。
NumPy 的优势:
- 向量化运算:底层 C 实现,避免 Python 循环,速度极快。
- 高性能:连续内存存储,缓存友好。
- 多维支持:轻松处理矩阵、张量等复杂数据结构。
对比示例:计算两个列表对应元素相加
python
# 原生 list
a = [1, 2, 3]
b = [4, 5, 6]
c = [a[i] + b[i] for i in range(len(a))] # 需要循环
# NumPy
import numpy as np
a_np = np.array([1, 2, 3])
b_np = np.array([4, 5, 6])
c_np = a_np + b_np # 直接相加,向量化
2. ndarray:核心数据结构
ndarray 是 NumPy 中最重要的类,代表一个同质的 N 维数组(所有元素类型相同)。
python
import numpy as np
# 从列表创建一维数组
arr1 = np.array([1, 2, 3])
print(arr1) # [1 2 3]
# 创建二维数组
arr2 = np.array([[1, 2], [3, 4]])
print(arr2)
# [[1 2]
# [3 4]]
2.1 数组属性
每个 ndarray 都有以下重要属性:
| 属性 | 说明 | 示例 |
|---|---|---|
ndim |
维度数(轴的数量) | arr.ndim → 2 |
shape |
形状,每个维度大小的元组 | arr.shape → (2, 3) |
size |
元素总数 | arr.size → 6 |
dtype |
元素的数据类型 | arr.dtype → int64 |
itemsize |
每个元素的字节大小 | arr.itemsize → 8 |
python
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("维度:", arr.ndim) # 2
print("形状:", arr.shape) # (2, 3)
print("元素总数:", arr.size) # 6
print("数据类型:", arr.dtype) # int64(64位整数)
print("每元素字节:", arr.itemsize) # 8
2.2 数据类型
NumPy 支持丰富的数据类型,常用有:
int8,int16,int32,int64float16,float32,float64boolcomplex64,complex128
创建数组时指定类型:
python
arr_float = np.array([1, 2, 3], dtype=np.float32)
print(arr_float.dtype) # float32
类型转换:
python
arr_int = arr_float.astype(np.int32)
特殊值:
- 缺失值 :
np.nan(浮点型,不能用None代替) - 无穷大 :
np.inf
3. 创建数组的各种方式
3.1 从列表或元组创建
python
np.array([1, 2, 3])
np.array([(1,2), (3,4)]) # 元组也可
3.2 全0 / 全1 数组
python
np.zeros((2, 3)) # 2行3列全0
np.ones((2, 3)) # 全1
np.zeros_like(arr) # 形状和 arr 相同的全0数组
np.ones_like(arr)
3.3 等差序列
python
np.arange(0, 10, 2) # [0,2,4,6,8]
np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1.0]
np.logspace(0, 2, 3) # [1, 10, 100] (10^0, 10^1, 10^2)
3.4 随机数组
python
# [0,1) 均匀分布,形状 (2,3)
np.random.rand(2, 3)
# 整数随机,区间 [low, high)
np.random.randint(0, 10, size=(2, 3))
# 标准正态分布
np.random.randn(2, 3)
4. 形状操作
4.1 改变形状(reshape 与 resize)
reshape:返回新视图(尽可能不复制),不改变原数组。resize:修改原数组,大小不足时可重复填充。
python
arr = np.arange(6) # [0,1,2,3,4,5]
reshaped = arr.reshape(2, 3)
print(reshaped)
# [[0 1 2]
# [3 4 5]]
# resize 直接修改原数组
arr.resize(2, 3) # arr 变为 2x3
4.2 转置
python
arr = np.array([[1,2], [3,4]])
print(arr.T) # 转置
# [[1 3]
# [2 4]]
4.3 强制最小维度
python
arr = np.array([1,2,3], ndmin=2) # 变成 (1,3) 二维
print(arr.shape) # (1, 3)
5. 索引与切片
5.1 基本索引
一维数组类似列表:
python
arr = np.array([10, 20, 30, 40])
print(arr[0]) # 10
print(arr[-1]) # 40
二维数组:arr[行, 列]
python
arr2 = np.array([[1,2,3], [4,5,6], [7,8,9]])
print(arr2[0, 1]) # 2(第0行第1列)
print(arr2[1]) # [4,5,6](第1行所有列)
5.2 切片
语法 start:stop:step,每个维度可独立切片。
python
arr2 = np.arange(12).reshape(3, 4)
print(arr2)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# 取前两行,后两列
sub = arr2[:2, 2:] # [[2,3], [6,7]]
三维数组切片:arr[层, 行, 列]
python
arr3 = np.arange(24).reshape(2, 3, 4) # 2层,每层3行4列
print(arr3[0, 1:, 2]) # 第0层,第1行及以后,第2列
5.3 布尔索引
使用布尔数组进行条件筛选,非常强大。
python
arr = np.array([10, 25, 30, 45, 50])
mask = arr > 30
print(mask) # [False False False True True]
print(arr[mask]) # [45 50]
# 直接写入条件
print(arr[arr % 2 == 0]) # [10,30,50]
布尔索引也可用于赋值:
python
arr[arr > 30] = 99
print(arr) # [10 25 30 99 99]
6. 数值运算与通用函数(ufunc)
通用函数(universal function)是对数组进行逐元素运算的函数。
6.1 算术运算
+, -, *, /, **, % 等均支持逐元素运算(配合广播机制)。
python
a = np.array([1,2,3])
b = np.array([4,5,6])
print(a + b) # [5 7 9]
print(a * b) # [4 10 18]
print(a ** 2) # [1 4 9]
6.2 常用数学函数
python
arr = np.array([-1, 0, 1, 2])
np.abs(arr) # 绝对值
np.sqrt(arr) # 平方根(nan 对负数)
np.exp(arr) # 指数
np.sin(arr) # 三角函数
7. 统计方法与逻辑判断
7.1 统计函数
可以指定 axis 按行或按列计算。
| 函数 | 说明 | 示例 |
|---|---|---|
arr.max() |
最大值 | arr.max(axis=0) 每列最大值 |
arr.min() |
最小值 | |
arr.mean() |
均值 | |
arr.median() |
中位数(需导入) | np.median(arr) |
arr.std() |
标准差 | |
arr.var() |
方差 | |
arr.sum() |
求和 | |
arr.argmax() |
最大值的索引 | |
arr.argmin() |
最小值的索引 |
python
arr = np.array([[1,2,3], [4,5,6]])
print(arr.sum()) # 21
print(arr.sum(axis=0)) # 每列求和 -> [5,7,9]
print(arr.mean(axis=1)) # 每行均值 -> [2.,5.]
7.2 逻辑判断
python
arr = np.array([1, 2, 3, 4])
print(np.all(arr > 0)) # True,所有元素大于0
print(np.any(arr > 3)) # True,存在大于3的元素
8. 广播机制
广播 允许不同形状的数组进行算术运算。规则:从尾部维度开始比较,如果两个数组的维度大小相等或其中一个为 1,则兼容;否则无法广播。
python
# 一维 + 标量
arr = np.array([1,2,3])
print(arr + 10) # [11 12 13] 标量扩展为 [10,10,10]
# 二维 + 一维
A = np.ones((3, 4))
B = np.array([1,2,3,4])
print(A + B) # B 广播为 (3,4)
# 二维 + 列向量
C = np.array([[1],[2],[3]]) # (3,1)
print(A + C) # 广播为 (3,4)
9. 高级技巧
9.1 条件赋值 np.where
python
arr = np.array([10, 20, 30, 40])
result = np.where(arr > 25, 100, 0)
print(result) # [0 0 100 100]
9.2 掩码数组
掩码数组用于标记无效或缺失数据。
python
data = np.array([1, 2, np.nan, 4])
mask = np.isnan(data)
data[mask] = 0 # 将缺失值替换为0
9.3 去重 np.unique
python
arr = np.array([2, 1, 3, 2, 1])
uniq = np.unique(arr) # [1,2,3] 自动排序
9.4 内存视图:asarray vs array
np.array()总是复制数据(除非指定copy=False且输入已是数组)。np.asarray()尽可能不复制,输入是数组时直接返回原视图,节省内存。
python
a = np.arange(5)
b = np.asarray(a) # b 是 a 的视图
b[0] = 99
print(a[0]) # 99,原数组被修改
9.5 批量条件赋值(结合布尔索引)
python
arr = np.random.randint(0, 100, size=10)
arr[arr < 50] = 0
arr[arr >= 50] = 1
10. 应用场景与补充
- 数据加载与转换:从 CSV 或二进制文件读入数据,转换为 ndarray。
- 随机数据生成:模拟实验数据。
- 机器学习:特征矩阵、权重矩阵运算。
- 深度学习底层计算 :例如 LoRA 微调中的低秩矩阵乘法
A @ B。
矩阵乘法(点积):
python
A = np.random.rand(3, 4)
B = np.random.rand(4, 5)
C = np.dot(A, B) # 或 C = A @ B
print(C.shape) # (3, 5)
小结
- NumPy 的核心是
ndarray,支持高效的向量化运算。 - 掌握创建数组的方法:
array、zeros、ones、arange、linspace、random。 - 熟悉数组属性:
shape、dtype、ndim、size。 - 索引和切片强大且灵活,包括布尔索引。
- 通用函数(ufunc)和统计函数让数值计算简洁。
- 广播机制避免了不必要的数据复制。
- 高级技巧如
where、unique、asarray可提升代码效率。
NumPy 是学习 Pandas、Matplotlib、Scikit-learn 乃至深度学习框架的基础。建议读者多动手练习,将本文示例逐一运行并尝试修改参数,以加深理解。