01-从零入门教程

知识库
知识库文档
/tech-stacks/numpy/tutorial/01-从零入门教程.md

文档

NumPy 入门教程 —— 从 Python 列表到向量化思维

本章目标

  • 理解 NumPy 在 Python 生态系统中的定位
  • 掌握 ndarray 的内存模型与性能原理
  • 学会用向量化思维替代显式循环
  • 掌握常见数据操作技巧

1. NumPy 的定位

Python 科学计算栈层级:

应用层:    scikit-learn | TensorFlow/PyTorch | SciPy
             ↓              ↓                ↓
中间层:    Pandas      |  NumPy (ndarray)  |  Matplotlib
             ↓              ↓                ↓
底层:      NumPy (C/Fortran 实现 + Python API)
             ↓
         BLAS/LAPACK (MKL / OpenBLAS / Accelerate)

一句话:NumPy 是 Python 数据科学的事实标准"通用语言"。

2. ndarray 内存模型

为什么 ndarray 这么快?

# Python 列表:每个元素是 PyObject 指针
py_list = [1, 2, 3, 4]
# 内存布局: [obj_ptr] → [int_obj 1]
#             [obj_ptr] → [int_obj 2]  # 内存碎片化
#             ...

# NumPy ndarray:连续内存块
np_arr = np.array([1, 2, 3, 4], dtype=np.int64)
# 内存布局: [1][2][3][4]  # 连续 32 bytes

三大性能来源:

  1. 连续内存 — CPU 缓存友好,SIMD 向量指令
  2. C 循环 — 运算代码在 C 层执行,绕过 Python 解释器
  3. BLAS — 线性代数调用高度优化的 BLAS 库(MKL/OpenBLAS)

数据排布:C-order vs Fortran-order

arr = np.arange(12).reshape(3, 4)

# C-order(默认,按行存储)
print(arr.flags['C_CONTIGUOUS'])  # True
# 内存: [0 1 2 3 | 4 5 6 7 | 8 9 10 11]

# Fortran-order(按列存储)
arr_f = np.asfortranarray(arr)
# 内存: [0 4 8 | 1 5 9 | 2 6 10 | 3 7 11]

# 性能影响:沿连续维度的操作更快

3. 向量化思维

从"循环思维"到"向量化思维"

# 问题:计算 1 到 100 万每个数的 sin 值

# ❌ Python 循环思维
import math
result = [math.sin(i) for i in range(1, 1_000_001)]

# ✅ NumPy 向量化思维
x = np.arange(1, 1_000_001)
result = np.sin(x)

常见向量化模式

# ① 条件赋值
# ❌ 循环
for i in range(len(data)):
    if data[i] > 0:
        data[i] = 1
    else:
        data[i] = -1

# ✅ 向量化
data = np.where(data > 0, 1, -1)

# ② 分段函数
conditions = [data < 0, data == 0, data > 0]
choices = [-1, 0, 1]
result = np.select(conditions, choices)

# ③ 分组聚合(没有 groupby 但有替代方案)
labels = np.array(["a", "b", "a", "c", "b"])
values = np.array([10, 20, 30, 40, 50])

# 用 unique + 循环(分组数少时仍很快)
for label in np.unique(labels):
    mask = labels == label
    print(f"{label}: sum={values[mask].sum()}, mean={values[mask].mean()}")

4. 高级索引技巧

# ============================================================
# 布尔索引 —— 条件筛选
# ============================================================
data = np.random.randn(1000, 5)

# 筛选第3列 > 0 的所有行
filtered = data[data[:, 2] > 0]

# 多条件
mask = (data[:, 0] > 0) & (data[:, 1] < 0)  # 用 & 不是 and
filtered = data[mask]

# ============================================================
# 花式索引 —— 任意顺序选择
# ============================================================
arr = np.array(["a", "b", "c", "d", "e"])
indices = [0, 2, 4, 1, 3]
print(arr[indices])  # ['a' 'c' 'e' 'b' 'd']

# 行列同时花式索引
matrix = np.arange(25).reshape(5, 5)
rows = [0, 2, 4]
cols = [1, 3]
# 需要 ix_ 取笛卡尔积
print(matrix[np.ix_(rows, cols)])

# ============================================================
# where 寻址
# ============================================================
arr = np.array([5, 2, 8, 1, 9, 3])
indices = np.where(arr > 4)
print(indices)  # (array([0, 2, 4]),)
print(arr[indices])  # [5 8 9]

5. 性能优化技巧

5.1 避免不必要的复制

# ❌ 切片后再赋值会创建临时数组
a = np.random.randn(1000000)
b = a[::2].copy()  # copy() 强制复制

# ✅ 直接使用视图
b = a[::2]  # 视图,不复制数据

# ❌ 增量改变形状
for i in range(100):
    a = a.reshape(100, 100)  # 每次都复制

# ✅ 一次性 reshape
a = a.reshape(100, 100)

5.2 out 参数避免分配新内存

# ❌ 创建3个临时数组
result = np.sin(a) + np.cos(a) + np.tan(a)

# ✅ 使用 out 参数复用内存
result = np.empty_like(a)
np.sin(a, out=result)
np.cos(a, out=result)   # 覆盖 result
# 这个例子不太合适——如需累加:
result = np.sin(a)
np.add(result, np.cos(a), out=result)
np.add(result, np.tan(a), out=result)

5.3 选择正确的数据类型

# float64 (8 bytes) → float32 (4 bytes) 内存减半
a = np.random.randn(10_000_000).astype(np.float32)

# float64 → int8 内存减至 1/8
labels = np.random.randint(0, 10, 10_000_000, dtype=np.int8)

6. NumPy vs Pandas:何时用哪个?

场景 推荐
纯数值矩阵运算 NumPy
图像/信号处理 NumPy
混合类型表格数据 Pandas
数据清洗/探索 Pandas
深度学习张量 PyTorch/TensorFlow
简单统计聚合 都可以

思考题

  1. 为什么 np.sin(arr)[math.sin(x) for x in arr] 快得多?
  2. ndarray 的 viewcopy 有什么区别?什么时候它们是危险的?
  3. 广播机制在什么情况下会失败?如何 debug 形状不匹配?
  4. 什么时候不应该用 NumPy(即纯 Python 反而更好)?

信息

路径
/tech-stacks/numpy/tutorial/01-从零入门教程.md
更新时间
2026/5/30