Skip to content

NumPy 从入门到实战精通

导航目录

前言

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
# 输出解释:从默认源安装 NumPy
bash
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)。

数组常用属性

常见属性包括:shapedtypendimsizeitemsize

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 数组,表示每个位置比较结果

关键注意点

  • 组合条件要用 &|,并加括号;不要用 andor

逻辑运算: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. 维度相等,或
  2. 其中一个维度是 1,或
  3. 某一方缺失该维度(视作 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 检测与修复

数值清洗中常见 NaNinf-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 校验,再计算并复制结果,避免引用联动

关键注意点

  • 开发阶段建议经常打印 shapedtype 做快速自检。

常见坑与避坑指南

坑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) 从尾维比较不兼容,无法广播

关键注意点

  • 修复方式:reshapeexpand_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 自检习惯。把本页代码逐段运行一遍,你就能从入门快速过渡到高效实战。