NumPy: NumPy 入门
NumPy 是 Python 科学计算的基石。无论是机器学习、数据分析还是工程仿真,几乎所有数值计算库都建立在 NumPy 之上。本节将从 Python 列表的性能痛点出发,揭示 NumPy 速度快 160 倍的秘密,带你迈出数值计算的第一步。
1. 你将学到
- ❶ Python 列表做数值计算的痛点
- ❷ NumPy 的核心优势(速度 / 内存 / 广播)
- ❸ ndarray 与 Python list 的关键差异
- ❹ NumPy 生态系统概览
- ❺ 安装 NumPy 并运行第一个程序
2. Million-level 数据的提速之旅
(1) 痛点:Alice 的列表循环
Alice 是一名数据分析师,她需要对 1 million(1M)个随机数做平方运算。她写了一段"直觉上最自然"的 Python 代码——用 for 循环逐个处理列表元素。
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:列表平方运算(难度⭐)
import time
import random
size = 1_000_000 # 1 million numbers
data = [random.random() for _ in range(size)]
start = time.time()
result = [x * x for x in data]
elapsed = time.time() - start
print(f"List comprehension: {elapsed:.4f} seconds")
# Typical output: List comprehension: 0.8000 seconds
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
Alice 的代码跑了约 0.8 秒。看似不长,但当她需要对上亿条数据反复运算时,等待时间会变得难以忍受。
(2) 解法:Bob 的 NumPy 向量化
Bob 只改了两行:用 np.array 替代列表,用一次向量乘法替代循环。
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:NumPy 平方运算(难度⭐⭐)
import time
import numpy as np
size = 1_000_000 # 1 million numbers
data = np.random.random(size)
start = time.time()
result = data * data # vectorized operation
elapsed = time.time() - start
print(f"NumPy vectorized: {elapsed:.4f} seconds")
# Typical output: NumPy vectorized: 0.0050 seconds
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
同样的 1 million 个数,NumPy 只需约 5 毫秒——快了约 160 倍。
(3) 收益:160 倍提速的秘密
NumPy 的提速不是"Python 更快了",而是把计算交给了 C 语言和连续内存:
- 底层是 C:NumPy 的核心运算由 C/Fortran 实现,绕过了 Python 解释器的逐行执行开销
- 连续内存:ndarray 在内存中紧密排列,CPU 缓存命中率极高
- 向量化:一条指令同时处理整个数组,无需 Python 层面的循环
这就是 NumPy 的核心哲学——用 Python 的便捷调用 C 的速度。
3. Python 数值计算的痛点
(1) 循环开销:逐个解释执行
Python 是动态类型语言,每次循环迭代都要:检查类型 → 查找方法 → 执行运算 → 装箱结果。对于数值计算,这些额外步骤是纯粹的浪费。
# Each iteration: type check + method lookup + boxing
result = []
for x in data: # Python loop overhead per iteration
result.append(x * x) # type check, method dispatch, result boxing
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
对比 C 语言编译后的机器码,一次 Python 循环的开销可以是实际乘法运算的 几十倍。
(2) 类型开销:每个元素都是完整对象
Python 列表存储的不是"原始数字",而是 PyObject 指针。一个整数 42 在 Python 中占用 28 字节,而在 C 中只需 4 字节。
4. NumPy 的核心优势
(1) ndarray 是什么
ndarray(N-dimensional array)是 NumPy 的核心数据结构,它是一个:
- 同质:所有元素类型相同(如全部
float64) - 定长:元素大小固定,无需装箱
- 连续:在内存中紧密排列,无指针间接访问
- 多维:支持 1D、2D 甚至 N 维数据
(2) ndarray vs Python list
| 特性 | Python list | NumPy ndarray |
|---|---|---|
| 元素类型 | 任意混合 | 同质(单一 dtype) |
| 内存布局 | 指针数组,元素分散 | 连续内存块 |
| 单个 int 内存 | 28 字节(PyObject) | 8 字节(int64) |
| 批量运算 | 列表推导 / for 循环 | 向量化,一条语句 |
| 广播 | 不支持 | 自动扩展维度 |
| 切片视图 | 返回副本 | 默认返回视图(零拷贝) |
| 多维支持 | 嵌套列表(不规则) | 原生 N 维,shape 属性 |
| 底层实现 | CPython 解释器 | C / Fortran 编译代码 |
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:内存占用对比(难度⭐)
import sys
import numpy as np
size = 1_000_000 # 1 million integers
# Python list
py_list = list(range(size))
list_bytes = sys.getsizeof(py_list) + sum(sys.getsizeof(x) for x in py_list[:1000]) * size // 1000
print(f"Python list: ~{list_bytes / 1024 / 1024:.1f} MB")
# NumPy ndarray
np_array = np.arange(size, dtype=np.int64)
array_bytes = np_array.nbytes
print(f"NumPy ndarray: {array_bytes / 1024 / 1024:.1f} MB")
print(f"Ratio: {list_bytes / array_bytes:.1f}x more memory for list")
# Typical output:
# Python list: ~44.7 MB
# NumPy ndarray: 7.6 MB
# Ratio: 5.9x more memory for list
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
一个简单的 int64 ndarray 比同等规模的 Python 列表节省约 6 倍 内存——因为 ndarray 存的是原始数值,而不是完整的 Python 对象。
(3) 广播机制初探
"广播"(Broadcasting)让不同形状的数组之间直接运算,无需手动扩展。
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:数组运算 vs 列表推导(难度⭐⭐)
import numpy as np
# Task: add 100 to every element and multiply by 2
data = [1, 2, 3, 4, 5]
# --- Python list ---
result_list = [(x + 100) * 2 for x in data]
print(result_list)
# [202, 204, 206, 208, 210]
# --- NumPy ndarray ---
arr = np.array(data)
result_np = (arr + 100) * 2 # broadcasting + vectorization
print(result_np)
# [202 204 206 208 210]
# With a 2D array and 1D array
matrix = np.array([[1, 2, 3],
[4, 5, 6]])
row = np.array([10, 20, 30])
print(matrix + row)
# [[11 22 33]
# [14 25 36]]
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
列表推导要写循环逻辑,NumPy 只需一行数学表达式——代码即公式。
5. NumPy 生态系统概览
(1) NumPy 生态全景
NumPy 是整个 Python 科学计算生态的"地基"。几乎所有主流库都依赖它:
mindmap
root((NumPy Ecosystem))
Data Science
Pandas
Polars
Machine Learning
Scikit-learn
TensorFlow
PyTorch
Scientific Computing
SciPy
Matplotlib
SymPy
Deep Learning Frameworks
Keras
JAX
MXNet
Specialized Domains
scikit-image
NetworkX
AstroPy
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
(2) 生态全景表
| 库 | 领域 | 依赖 NumPy 的原因 |
|---|---|---|
| Pandas | 数据分析 | DataFrame 底层用 ndarray 存储列数据 |
| Scikit-learn | 机器学习 | 模型输入/输出均为 ndarray |
| SciPy | 科学计算 | 基于 ndarray 提供线性代数/优化/积分 |
| Matplotlib | 可视化 | 绘图数据源为 ndarray |
| TensorFlow | 深度学习 | 张量概念源自 ndarray,可互转 |
| PyTorch | 深度学习 | Tensor 与 ndarray 互相转换 |
| Polars | 数据分析 | 列式存储,但仍兼容 NumPy |
(3) 数值计算场景对比
| 场景 | 纯 Python | NumPy | 推荐 |
|---|---|---|---|
| 几十个数的简单运算 | 够用 | 略快 | 纯 Python |
| 10K+ 数值的批量运算 | 慢 | 快 10~100x | NumPy |
| 矩阵乘法 | 嵌套循环极慢 | 调用 BLAS 极快 | NumPy |
| 线性方程组 | 手写不现实 | np.linalg.solve |
NumPy |
| 字符串处理 | 灵活 | 不擅长 | 纯 Python |
| 混合类型数据 | list 天然支持 | 需结构化数组 | 视情况 |
6. 安装与第一个程序
(1) 安装方式对比
| 方式 | 命令 | 适用场景 |
|---|---|---|
| pip | pip install numpy |
通用,最简单 |
| conda | conda install numpy |
Anaconda/Miniconda 环境 |
| 系统包管理 | apt install python3-numpy |
Linux 系统级安装 |
| 源码编译 | python setup.py build |
需要自定义 BLAS 后端 |
推荐新手使用
pip install numpy,Anaconda 用户用conda install numpy。
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:安装验证(难度⭐)
# Install
pip install numpy
# Verify
python -c "import numpy as np; print(np.__version__)"
# Output (example): 2.1.0
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
(2) 创建第一个 ndarray
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:np.array 创建(难度⭐)
import numpy as np
# From a Python list
a = np.array([1, 2, 3, 4, 5])
print(type(a)) # <class 'numpy.ndarray'>
print(a.dtype) # int64
print(a.shape) # (5,)
# From nested list (2D)
b = np.array([[1, 2, 3],
[4, 5, 6]])
print(b.shape) # (2, 3)
print(b.ndim) # 2
# Specify dtype explicitly
c = np.array([1, 2, 3], dtype=np.float32)
print(c.dtype) # float32
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
(3) 查看版本与配置
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:查看版本和配置(难度⭐)
import numpy as np
print(f"NumPy version: {np.__version__}")
# Show build configuration (BLAS, LAPACK, etc.)
np.show_config()
# Output includes: BLAS, LAPACK backend info,
# which determines computation performance
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
np.show_config() 输出的 BLAS/LAPACK 信息决定了你的 NumPy 在矩阵运算上能跑多快——OpenBLAS、MKL 等不同后端性能差异可达数倍。
7. 综合实战:10M 数据的均值与标准差
面对 10 million 个浮点数,计算均值和标准差——这是数据分析最常见的操作之一。我们用纯 Python 和 NumPy 分别实现,对比代码量、速度和内存。
▶ 示例
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
:综合对比——列表 vs NumPy(难度⭐⭐⭐)
import time
import math
import random
import numpy as np
import sys
size = 10_000_000 # 10 million
# ========== Pure Python ==========
py_data = [random.random() for _ in range(size)]
t0 = time.time()
mean_py = sum(py_data) / len(py_data)
var_py = sum((x - mean_py) ** 2 for x in py_data) / len(py_data)
std_py = math.sqrt(var_py)
elapsed_py = time.time() - t0
# Memory estimate for list
mem_py = sys.getsizeof(py_data) + size * 24 # ~24 bytes per float object
# ========== NumPy ==========
np_data = np.random.random(size)
t0 = time.time()
mean_np = np.mean(np_data)
std_np = np.std(np_data)
elapsed_np = time.time() - t0
# Memory for ndarray
mem_np = np_data.nbytes
# ========== Results ==========
print(f"--- Pure Python ---")
print(f" Mean: {mean_py:.6f} Std: {std_py:.6f}")
print(f" Time: {elapsed_py:.3f}s")
print(f" Memory: ~{mem_py / 1024 / 1024:.0f} MB")
print(f"--- NumPy ---")
print(f" Mean: {mean_np:.6f} Std: {std_np:.6f}")
print(f" Time: {elapsed_np:.3f}s")
print(f" Memory: {mem_np / 1024 / 1024:.1f} MB")
print(f"--- Comparison ---")
print(f" Speedup: {elapsed_py / elapsed_np:.1f}x")
print(f" Memory saving: {mem_py / mem_np:.1f}x")
# Typical output:
# --- Pure Python ---
# Mean: 0.500032 Std: 0.288675
# Time: 3.200s
# Memory: ~305 MB
# --- NumPy ---
# Mean: 0.500032 Std: 0.288675
# Time: 0.030s
# Memory: 76.3 MB
# --- Comparison ---
# Speedup: 106.7x
# Memory saving: 4.0x
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
| 指标 | 纯 Python | NumPy | 对比 |
|---|---|---|---|
| 代码行数(核心逻辑) | 4 行 | 2 行 | NumPy 少 50% |
| 执行时间 | ~3.2s | ~0.03s | 快 ~100x |
| 内存占用 | ~305 MB | ~76 MB | 省 4x |
结论:数据量越大,NumPy 的优势越明显。在 10 million 级别的数据上,NumPy 在速度和内存上都碾压纯 Python。
❓ 常见问题
array 模块只支持 1D 同类型数组,没有广播、线性代数、FFT 等功能。NumPy 的 ndarray 支持多维、广播、丰富的数学函数,是两个完全不同量级的工具。📖 小节
- Python 列表做数值计算有两大痛点:循环解释开销大、每个元素是完整对象占用内存多
- NumPy 通过 C 底层实现 + 连续内存 + 向量化运算,在 1 million 数据上实现约 160 倍提速
- ndarray 是同质、定长、连续的多维数组,比 Python 列表节省约 6 倍内存
- 广播机制让不同形状的数组之间直接运算,代码即公式
- NumPy 是 Python 科学计算生态的地基,Pandas / Scikit-learn / SciPy / TensorFlow 等都依赖它
- 安装只需
pip install numpy,创建数组用np.array(),查看配置用np.show_config()
📝 作业
-
基础题(难度⭐):安装 NumPy 并运行
import numpy as np; print(np.__version__),确认版本号 ≥ 1.24。将输出结果记录下来。 -
进阶题(难度⭐⭐):生成一个包含 5 million 个随机数的列表和一个 ndarray,分别用列表推导和 NumPy 向量化计算每个数的平方加 10,用
time.time()计时并对比耗时。记录两者的耗时比。 -
挑战题(难度⭐⭐⭐):运行
np.show_config(),找到你安装的 NumPy 使用的 BLAS 后端(OpenBLAS / MKL / 其他),并查阅资料简述该后端对你的矩阵运算性能的影响。