Appearance
NumPy 从入门到实战精通
导航目录
- 一、前言:NumPy 简介、用途、安装与导入
- 二、基础概念:ndarray、维度、形状、dtype、数组属性
- 三、数组创建:基础创建、特殊数组、从列表/元组/字典创建
- 四、数组索引与切片:一维、多维、布尔索引、花式索引
- 五、数组形状操作:reshape、resize、flatten、ravel、transpose
- 六、数组拼接与拆分:concatenate、vstack、hstack、split
- 七、数值运算:算术、比较、逻辑运算与广播基础
- 八、数学函数:基础函数、统计函数、常用工具函数
- 九、广播机制:原理、场景、用法与错误排查
- 十、缺失值与异常值处理:isnan、isinf、nan_to_num、clip
- 十一、数组类型转换:astype、asarray、asfarray
- 十二、高级功能:矩阵运算、线性代数、向量化
- 十三、实战技巧:一行代码、高效处理大数组、防错写法
- 十四、常见坑与避坑指南:广播、视图副本、dtype、矩阵乘法
- 十五、结语
前言
NumPy 简介
NumPy(Numerical Python)是 Python 生态中最核心的数值计算库,提供高性能多维数组(ndarray)和大量数学函数,是 Pandas、SciPy、scikit-learn、PyTorch 等库的重要基础。
python
import numpy as np
print(np.__version__)
# 输出解释:打印当前 NumPy 版本,确认环境可用关键注意点
- NumPy 的核心对象是
ndarray,不是 Python 原生list。 - 绝大多数数值任务先用 NumPy 表达,再扩展到其他库。
核心用途
NumPy 主要用于数组计算、矩阵运算、统计分析、信号处理、机器学习预处理等场景。
python
import numpy as np
scores = np.array([78, 85, 92, 88, 76])
print(scores.mean(), scores.std())
# 输出解释:快速计算平均值与标准差,展示 NumPy 的统计能力关键注意点
- NumPy 运算通常比 Python 循环更快(底层 C 实现 + 向量化)。
安装方法
支持常规安装与镜像安装(国内网络常用)。
bash
pip install numpy
# 输出解释:从默认源安装 NumPybash
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy
# 输出解释:使用清华镜像安装 NumPy关键注意点
- 建议在虚拟环境中安装,避免不同项目依赖冲突。
导入惯例
NumPy 社区统一使用 import numpy as np。
python
import numpy as np
a = np.array([1, 2, 3])
print(a)
# 输出解释:创建并输出一维数组关键注意点
- 文档、教程、开源代码几乎都默认
np这个别名。
基础概念
ndarray、维度(轴)、形状、dtype
ndarray 是同类型元素构成的多维容器;维度即轴(axis);形状是每个维度长度;dtype 是元素类型。
python
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int32)
print(a)
print("shape:", a.shape, "ndim:", a.ndim, "dtype:", a.dtype)
# 输出解释:shape=(2,3) 表示 2 行 3 列;ndim=2 表示二维;dtype=int32关键注意点
- 同一个
ndarray中元素类型统一,混合类型会自动提升(如 int + float -> float)。
数组常用属性
常见属性包括:shape、dtype、ndim、size、itemsize。
python
import numpy as np
a = np.array([[10, 20], [30, 40]], dtype=np.int64)
print(a.shape) # (2, 2)
print(a.dtype) # int64
print(a.ndim) # 2
print(a.size) # 4
print(a.itemsize) # 8
# 输出解释:itemsize=8 表示每个 int64 元素占 8 字节关键注意点
size是总元素个数,不是字节数。- 总内存约等于
size * itemsize。
数组创建
基础创建:array / zeros / ones / empty / arange / linspace
这些是最常用的数组生成方法。
python
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.zeros((2, 3))
arr3 = np.ones((2, 3), dtype=np.int32)
arr4 = np.empty((2, 2))
arr5 = np.arange(0, 10, 2)
arr6 = np.linspace(0, 1, 5)
print(arr1)
print(arr2)
print(arr3)
print(arr4) # 注意:empty 不会初始化,值是内存中的随机残留
print(arr5)
print(arr6)
# 输出解释:分别展示手动创建、全零、全一、未初始化、等差序列、等间距采样关键注意点
np.empty()快但不安全,需立即覆盖赋值后再使用。np.arange()可能有浮点精度问题,浮点步长优先np.linspace()。
特殊数组:eye / identity / diag / random
适用于矩阵初始化、单位矩阵、对角矩阵和随机数据模拟。
python
import numpy as np
eye_m = np.eye(3, 4) # 3x4 主对角线为1
id_m = np.identity(3) # 3x3 单位矩阵
diag_m = np.diag([1, 2, 3]) # 对角矩阵
rng = np.random.default_rng(42)
rand_a = rng.random((2, 3)) # [0,1) 均匀分布
rand_i = rng.integers(1, 10, (2, 3))
print(eye_m)
print(id_m)
print(diag_m)
print(rand_a)
print(rand_i)
# 输出解释:生成单位类矩阵与可复现实验随机数组(固定 seed=42)关键注意点
- 新代码建议使用
np.random.default_rng(),比旧版全局随机状态更安全。
从列表/元组/字典创建数组
列表和元组可直接转换;字典通常先取 keys/values/items 再转换。
python
import numpy as np
lst = [1, 2, 3]
tup = (4, 5, 6)
dct = {"a": 10, "b": 20, "c": 30}
a = np.array(lst)
b = np.array(tup)
c = np.array(list(dct.keys()))
d = np.array(list(dct.values()))
print(a, b, c, d)
# 输出解释:字典本身不能直接变成规则数值矩阵,通常转换其键或值关键注意点
- 字典转数组时元素可能是字符串,
dtype会变成字符串类型。
数组索引与切片
一维数组:索引与切片(步长)
与 Python 列表语法接近,但返回对象可能是视图。
python
import numpy as np
a = np.array([10, 20, 30, 40, 50, 60])
print(a[0], a[-1]) # 单个索引
print(a[1:5]) # 区间切片
print(a[::2]) # 步长切片
# 输出解释:分别输出首尾元素、子数组、隔位采样关键注意点
- 切片区间是左闭右开:
start包含,stop不包含。
二维/多维数组:行列索引、布尔索引、花式索引
NumPy 支持非常灵活的数据筛选方式。
python
import numpy as np
a = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(a[1, 2]) # 第2行第3列 -> 6
print(a[:, 1]) # 所有行的第2列
print(a[a > 5]) # 布尔索引
print(a[[0, 2], [1, 2]]) # 花式索引 -> [2, 9]
# 输出解释:演示点选、整列、条件筛选、按坐标批量取值关键注意点
- 布尔索引返回一维结果(按条件“拉平”提取)。
- 花式索引通常返回副本,不是视图。
视图与副本区别
切片通常是视图;显式复制才是独立副本。
python
import numpy as np
a = np.array([1, 2, 3, 4])
b = a[1:3] # 视图
c = a[1:3].copy() # 副本
b[0] = 99
c[0] = 88
print(a) # [1, 99, 3, 4]
print(b) # [99, 3]
print(c) # [88, 3]
# 输出解释:修改视图会影响原数组;修改副本不会关键注意点
- 需要“隔离修改”时,务必使用
.copy()。
数组形状操作
reshape / resize / flatten / ravel / transpose / T / swapaxes
用于调整数组结构,不改变或尽量少改变数据本身。
python
import numpy as np
a = np.arange(12)
b = a.reshape(3, 4)
flat1 = b.flatten() # 副本
flat2 = b.ravel() # 视图优先
t1 = b.transpose()
t2 = b.T
sw = np.swapaxes(b.reshape(3, 2, 2), 0, 1)
print("b:\n", b)
print("flatten:", flat1)
print("ravel:", flat2)
print("transpose:\n", t1)
print("T:\n", t2)
print("swapaxes shape:", sw.shape)
b.resize((2, 6)) # 原地调整
print("resize:\n", b)
# 输出解释:展示不同重塑方式与转置效果,resize 会改原数组形状关键注意点
reshape常返回视图;flatten总是副本。resize是原地操作,谨慎使用。
数组拼接与拆分
拼接:concatenate / vstack / hstack / dstack
按不同轴将多个数组合并。
python
import numpy as np
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print(np.concatenate([a, b], axis=0))
print(np.vstack([a, b]))
print(np.hstack([a, b]))
print(np.dstack([a, b]).shape)
# 输出解释:按行拼接、垂直拼接、按列拼接、按深度拼接(得到3D)关键注意点
- 除拼接轴外,其余维度必须一致,否则报错。
拆分:split / vsplit / hsplit
将数组按指定轴拆成多个子数组。
python
import numpy as np
a = np.arange(16).reshape(4, 4)
print(np.split(a, 2, axis=0)) # 沿行拆2份
print(np.vsplit(a, 2)) # 等价于 axis=0
print(np.hsplit(a, 2)) # 沿列拆2份
# 输出解释:返回由子数组组成的列表关键注意点
- 等分拆分时,长度必须整除;不整除可用
np.array_split()。
数值运算
基础算术运算与广播机制
NumPy 支持逐元素计算,自动广播不同形状数组。
python
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([10, 20, 30])
print(a + b) # 广播:b 扩展到每一行
print(a - 1)
print(a * 2)
print(a / 2)
print(a ** 2)
print(a % 2)
# 输出解释:逐元素加减乘除、幂、取余;a+b 演示广播关键注意点
- 运算默认逐元素,不是矩阵乘法;矩阵乘法用
@或np.dot()。
比较运算
返回布尔数组,可直接用于筛选。
python
import numpy as np
a = np.array([3, 5, 7, 9])
print(a > 5)
print(a == 7)
print((a >= 5) & (a < 9))
# 输出解释:返回同形状 bool 数组,表示每个位置比较结果关键注意点
- 组合条件要用
&、|,并加括号;不要用and、or。
逻辑运算:logical_and / logical_or / logical_not
适合明确表达布尔逻辑。
python
import numpy as np
x = np.array([True, False, True])
y = np.array([True, True, False])
print(np.logical_and(x, y))
print(np.logical_or(x, y))
print(np.logical_not(x))
# 输出解释:逐元素逻辑与、或、非关键注意点
- 逻辑函数在可读性上通常优于复杂的符号组合。
数学函数
基础函数:abs / sqrt / exp / log / sin / cos / tan
NumPy 内置常见数学函数,支持向量化输入。
python
import numpy as np
a = np.array([-1, 0, 1, 4], dtype=float)
print(np.abs(a))
print(np.sqrt(np.array([1, 4, 9])))
print(np.exp(np.array([0, 1])))
print(np.log(np.array([1, np.e])))
print(np.sin(np.array([0, np.pi / 2])))
# 输出解释:分别输出绝对值、开方、指数、对数、三角函数结果关键注意点
log默认自然对数(底数 e)。
统计函数
统计函数可指定轴 axis 对行/列聚合。
python
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
print(np.sum(a), np.sum(a, axis=0))
print(np.mean(a), np.mean(a, axis=1))
print(np.median(a))
print(np.std(a), np.var(a))
print(np.min(a), np.max(a))
print(np.argmin(a), np.argmax(a))
# 输出解释:全局统计 + 按轴统计;argmin/argmax 返回扁平索引关键注意点
- 需要二维坐标时,可配合
np.unravel_index()使用。
其他常用函数:round / clip / where / unique
常见于清洗、筛选、离散化场景。
python
import numpy as np
a = np.array([1.234, 5.678, -2.345, 5.678])
print(np.round(a, 2))
print(np.clip(a, 0, 5))
print(np.where(a > 0, a, 0))
print(np.unique(a))
# 输出解释:四舍五入、截断范围、条件替换、去重排序关键注意点
np.where(cond, x, y)是向量化条件表达式,常替代 if-else 循环。
广播机制
原理与适用场景
广播会在不复制大块数据的前提下,让不同形状数组参与逐元素运算。匹配规则(从尾维开始):
- 维度相等,或
- 其中一个维度是 1,或
- 某一方缺失该维度(视作 1)
python
import numpy as np
x = np.arange(6).reshape(2, 3) # (2,3)
y = np.array([10, 20, 30]) # (3,)
z = np.array([[1], [2]]) # (2,1)
print(x + y) # y 按行广播
print(x + z) # z 按列广播
# 输出解释:不同方向扩展后再逐元素相加关键注意点
- 广播不是随意扩展,必须满足形状兼容规则。
常见用法与注意事项
常用于归一化、批量加偏置、向量化距离计算。
python
import numpy as np
data = np.array([[1, 2, 3], [4, 5, 6]], dtype=float)
col_mean = data.mean(axis=0, keepdims=True)
normalized = data - col_mean
print(col_mean)
print(normalized)
# 输出解释:按列中心化,keepdims=True 便于广播对齐python
import numpy as np
a = np.ones((2, 3))
b = np.ones((2, 2))
# print(a + b) # ValueError: operands could not be broadcast together
# 输出解释:形状 (2,3) 与 (2,2) 不兼容,广播失败关键注意点
- 遇到广播错误先打印
.shape,从尾维逐位比对。
缺失值与异常值处理
nan/inf 检测与修复
数值清洗中常见 NaN、inf、-inf。
python
import numpy as np
a = np.array([1.0, np.nan, np.inf, -np.inf, 5.0])
print(np.isnan(a))
print(np.isinf(a))
clean = np.nan_to_num(a, nan=0.0, posinf=999.0, neginf=-999.0)
print(clean)
# 输出解释:先定位异常,再替换成可控数值关键注意点
np.nan != np.nan,判断 NaN 必须用np.isnan()。
clip 处理异常值(离群值)
通过上下限裁剪,将极端值压缩到合理区间。
python
import numpy as np
data = np.array([10, 12, 15, 300, -100, 18])
clipped = np.clip(data, 0, 100)
print(clipped)
# 输出解释:小于0设为0,大于100设为100关键注意点
clip是“截断”不是“删除”,适合稳健预处理。
数组类型转换
astype / asarray / asfarray
控制数据类型和数组转换行为。
python
import numpy as np
lst = [1, 2, 3]
a = np.asarray(lst) # 尽量不复制
b = a.astype(np.float64) # 转为 float64(通常复制)
c = np.asfarray([1, 2, 3]) # 转为浮点数组
print(a.dtype, b.dtype, c.dtype)
# 输出解释:int -> float 类型转换关键注意点
astype可能触发数据截断(如 float -> int),转换前确认精度需求。
高级功能
矩阵运算:dot / @ / matmul / inv / det
用于线性代数、机器学习、图形计算等。
python
import numpy as np
A = np.array([[1, 2], [3, 4]], dtype=float)
B = np.array([[5, 6], [7, 8]], dtype=float)
print(np.dot(A, B))
print(A @ B)
print(np.matmul(A, B))
print(np.linalg.inv(A))
print(np.linalg.det(A))
# 输出解释:前三者是矩阵乘法;inv 求逆;det 求行列式关键注意点
- 只有方阵且可逆时才可求逆;数值任务常优先
np.linalg.solve()避免显式求逆。
线性代数:norm / eig
norm 用于长度或误差度量,eig 用于特征分解。
python
import numpy as np
v = np.array([3, 4], dtype=float)
M = np.array([[2, 0], [0, 3]], dtype=float)
print(np.linalg.norm(v)) # 5.0
vals, vecs = np.linalg.eig(M)
print(vals)
print(vecs)
# 输出解释:norm 求向量长度;eig 返回特征值与特征向量关键注意点
- 特征向量方向可相差符号(
v与-v等价),属正常现象。
向量化操作(替代 for 循环)
向量化能显著提升性能并减少代码量。
python
import numpy as np
import time
n = 1_000_000
a = np.arange(n)
t1 = time.perf_counter()
out_loop = [x * x + 1 for x in a] # Python 循环
t2 = time.perf_counter()
out_vec = a * a + 1 # NumPy 向量化
t3 = time.perf_counter()
print(f"loop: {t2 - t1:.4f}s, vectorized: {t3 - t2:.4f}s")
print(np.allclose(np.array(out_loop), out_vec))
# 输出解释:向量化通常更快,且结果一致关键注意点
- 性能敏感代码优先“批量计算”,避免逐元素 Python 循环。
实战技巧
一行代码实现常用操作
高频任务可用 NumPy 一行完成,提升开发效率。
python
import numpy as np
a = np.array([1, 2, 2, 3, 4, 4, 5], dtype=float)
print((a - a.min()) / (a.max() - a.min())) # 归一化到 [0,1]
print((a - a.mean()) / a.std()) # 标准化(z-score)
print(np.where(a > 3, 1, 0)) # 二值化
print(np.unique(a, return_counts=True)) # 去重并计数
# 输出解释:展示数据预处理最常见的一行写法关键注意点
- 一行代码要兼顾可读性,复杂表达式可拆成中间变量。
高效处理大数据量数组
通过选择 dtype、避免不必要复制、使用原地运算可降低内存压力。
python
import numpy as np
a = np.ones(10_000_000, dtype=np.float32) # 比 float64 更省内存
b = np.full_like(a, 2.0)
a += b # 原地加法,减少临时数组
print(a[:5], a.dtype)
# 输出解释:原地运算 + 合适 dtype 可显著节省资源关键注意点
- 大数组场景优先检查
dtype与是否可原地更新(+=、*=)。
避免常见错误的实用写法
先对齐形状、显式复制、统一类型,能减少 80% NumPy 问题。
python
import numpy as np
x = np.array([1, 2, 3], dtype=np.float64)
y = np.array([4, 5, 6], dtype=np.float64)
assert x.shape == y.shape
z = (x + y).copy()
print(z)
# 输出解释:先做 shape 校验,再计算并复制结果,避免引用联动关键注意点
- 开发阶段建议经常打印
shape和dtype做快速自检。
常见坑与避坑指南
坑1:广播错误(shape 不兼容)
python
import numpy as np
a = np.ones((2, 3))
b = np.ones((3, 1))
# print(a + b) # 会报错
print(a.shape, b.shape)
# 输出解释:(2,3) 与 (3,1) 从尾维比较不兼容,无法广播关键注意点
- 修复方式:
reshape、expand_dims或调整运算轴。
坑2:视图与副本混淆导致“误改原数据”
python
import numpy as np
a = np.array([1, 2, 3, 4])
sub = a[:2] # 视图
sub[0] = 999
print(a)
# 输出解释:a 被同步改动,因为 sub 共享底层内存关键注意点
- 不想联动就用
a[:2].copy()。
坑3:数据类型不匹配导致精度丢失或溢出
python
import numpy as np
a = np.array([1.9, 2.1, 3.7])
b = a.astype(np.int32)
print(b)
# 输出解释:转 int 后小数被截断,不会四舍五入关键注意点
- 类型转换前先确认业务是否允许截断或精度变化。
坑4:把矩阵乘法误写成逐元素乘法
python
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A * B) # 逐元素
print(A @ B) # 矩阵乘法
# 输出解释:两者含义完全不同,机器学习中尤其容易写错关键注意点
- 记忆方式:
*是点对点,@才是线性代数意义的乘法。
坑5:随机结果不可复现
python
import numpy as np
rng1 = np.random.default_rng(2026)
rng2 = np.random.default_rng(2026)
print(rng1.integers(0, 10, 5))
print(rng2.integers(0, 10, 5))
# 输出解释:同 seed 产生相同随机序列,便于复现实验关键注意点
- 训练、实验、调参时固定 seed 是好习惯。
结语
掌握 NumPy 的关键在于三点:理解数组模型(shape/dtype/axis)、善用向量化和广播、养成 shape/dtype 自检习惯。把本页代码逐段运行一遍,你就能从入门快速过渡到高效实战。