前言
NumPy(Numerical Python)是 Python 数据科学栈的基石。它的核心是 ndarray------一个高性能的多维数组对象,配合向量化运算,让数值计算比纯 Python 快数十倍甚至上百倍。Pandas、Matplotlib、Scikit-learn 等几乎所有数据科学库都建立在 NumPy 之上。
本文按使用频率梳理 NumPy 的核心函数,覆盖数组创建、索引切片、形状操作、数学运算、统计聚合等全链路,适合作为案头速查手册。
版本说明: 本文基于 NumPy 1.24+,部分旧版 API(如
np.int)已废弃,建议使用np.int64等明确类型。
一、数组创建
创建数组是使用 NumPy 的第一步,不同场景选不同函数可以少写很多代码。
1.1 从 Python 序列创建
python
import numpy as np
# 从列表创建(最常用)
arr = np.array([1, 2, 3, 4, 5])
# 从嵌套列表创建二维数组
arr = np.array([[1, 2, 3], [4, 5, 6]])
1.2 创建占位数组
当你知道形状但暂不确定数值时,用占位数组初始化:
python
# 空数组(不初始化,内容是内存中的随机值,速度最快)
arr = np.empty((3, 4))
# 全零数组(最常用,适合作为累加器的起点)
arr = np.zeros((3, 4))
# 全一数组(适合作为乘法因子的起点)
arr = np.ones((3, 4))
# 填充指定值
arr = np.full((3, 4), 100)
# 单位矩阵(仅方阵,线性代数常用)
arr = np.eye(4, dtype=int)
# 创建与已有数组同形状的占位数组
arr = np.zeros_like(existing_arr)
arr = np.ones_like(existing_arr)
1.3 等差/等比数组
python
# arange:指定起止 + 步长(类似 Python 内置 range)
np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
# linspace:指定起止 + 元素个数(适合生成 x 轴刻度)
np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1]
np.linspace(0, 10, 100) # 生成 100 个点用于绘图
# logspace:对数刻度(适合在对数坐标下均匀采样)
np.logspace(0, 3, 4) # [1, 10, 100, 1000]
arangevslinspace:arange关心步长,linspace关心点数。画图时几乎总是用linspace,因为控制生成的点数更直观。
二、随机数生成
随机数是模拟、采样和数据增强的基础。NumPy 提供了丰富的随机函数。
python
# 随机整数 [low, high) 范围
np.random.randint(0, 100, 10) # 一维
np.random.randint(0, 100, (3, 4)) # 多维
# 标准正态分布 N(0, 1)
np.random.randn(3, 5)
# [0, 1) 均匀分布
np.random.rand(3, 5)
# 指定范围的均匀分布
np.random.uniform(1, 10, (3, 5))
# 正态分布(自定义均值和标准差)
np.random.normal(loc=0, scale=1, size=(3, 5))
# 从给定数组中有放回/无放回随机抽取
np.random.choice([1, 2, 3, 4, 5], size=3, replace=True)
# 设置随机种子(确保每次运行结果一致,调试必备)
np.random.seed(42)
最佳实践: 每次用到随机数时养成随手写
np.random.seed()的习惯,否则调试时结果变了你还以为代码出了问题。
新版随机数生成器(推荐)
python
# NumPy 1.17+ 推荐使用 Generator API,线程更安全
rng = np.random.default_rng(seed=42)
rng.integers(0, 100, 10)
rng.random((3, 5))
rng.normal(0, 1, (3, 5))
三、数组属性
了解数组的元信息是调试和验证逻辑的基本功。
python
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr.shape # (2, 3) --- 形状
arr.dtype # dtype('int64') --- 元素类型
arr.ndim # 2 --- 维度数(几维数组)
arr.size # 6 --- 元素总数
arr.itemsize # 8 --- 单个元素的字节数(int64 = 8 bytes)
arr.nbytes # 48 --- 总内存占用(size × itemsize)
四、索引与切片
这是日常操作中最频繁的部分,掌握好可以少写很多显式循环。
4.1 一维数组
python
arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr[0] # 0 --- 首元素
arr[-1] # 9 --- 末元素
arr[2:7] # [2, 3, 4, 5, 6] --- 切片(左闭右开)
arr[::2] # [0, 2, 4, 6, 8] --- 步长为 2
arr[::-1] # 反转数组
4.2 二维数组
python
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[1, 2] # 6 --- 第 2 行第 3 列
arr2d[0, :] # [1, 2, 3] --- 第 1 行
arr2d[:, 1] # [2, 5, 8] --- 第 2 列
arr2d[1:, 1:] # [[5, 6], [8, 9]] --- 右下角子矩阵
4.3 花式索引与布尔索引
这是 NumPy 的"杀手级"特性,一次性替代大量循环:
python
# 花式索引:按位置列表取值
arr = np.array([10, 20, 30, 40, 50])
arr[[0, 2, 4]] # [10, 30, 50]
# 布尔索引:按条件过滤
arr[arr > 30] # [40, 50]
arr[(arr > 15) & (arr < 45)] # [20, 30, 40]
# 注意:多个条件用 & / |,不能用 and / or,每个条件必须加括号
mask = (arr > 20) | (arr < 15)
arr[mask]
五、数组形状操作
模型输入常常要求特定形状,形状转换是数据预处理的日常操作。
python
arr = np.arange(12).reshape(3, 4)
# [[0, 1, 2, 3],
# [4, 5, 6, 7],
# [8, 9, 10, 11]]
5.1 展平
python
arr.flatten() # 返回副本,修改不影响原数组
arr.ravel() # 尽可能返回视图,修改会影响原数组(更快)
arr.reshape(-1) # 等价于展平
flatten()vsravel():ravel()不复制数据,性能更好;flatten()总是产生副本。如果不需要独立的副本,优先用ravel()。
5.2 转置与形状变换
python
arr.T # 矩阵转置
arr.reshape(2, 6) # 改变形状(元素总数必须一致)
# 使用 -1 让 NumPy 自动推断维度
arr.reshape(2, -1) # 行数为 2,列数自动计算
arr.reshape(-1, 4) # 列数为 4,行数自动计算
5.3 新增维度
python
arr = np.array([1, 2, 3, 4]) # shape (4,)
arr[np.newaxis, :] # shape (1, 4)
arr[:, np.newaxis] # shape (4, 1)
arr.reshape(1, -1) # 等价
5.4 拼接与分割
python
# 拼接
np.concatenate([arr1, arr2], axis=0) # 垂直拼接(沿着行方向堆)
np.concatenate([arr1, arr2], axis=1) # 水平拼接
np.vstack([arr1, arr2]) # 垂直拼接的快捷方式
np.hstack([arr1, arr2]) # 水平拼接的快捷方式
# 分割
np.split(arr, 3, axis=0) # 切成 3 等份
np.hsplit(arr, 2) # 水平方向切成 2 份
六、数学运算
NumPy 的一个核心优势是向量化运算------不用写 for 循环就能对整个数组做计算。
6.1 标量运算(广播)
python
arr = np.array([1, 2, 3, 4, 5])
arr + 1 # [2, 3, 4, 5, 6]
arr * 2 # [2, 4, 6, 8, 10]
arr ** 2 # [1, 4, 9, 16, 25]
arr / 2 # [0.5, 1., 1.5, 2., 2.5]
np.sqrt(arr) # 逐元素开方
np.log(arr) # 逐元素取对数
np.exp(arr) # 逐元素指数
6.2 数组间运算
python
arr1 + arr2 # 逐元素加法
arr1 * arr2 # 逐元素乘法(不是矩阵乘法!)
# 矩阵乘法(两种写法等价)
np.dot(arr1, arr2)
arr1 @ arr2
6.3 聚合函数
python
np.sum(arr) # 求和
np.prod(arr) # 求积
np.mean(arr) # 均值
np.median(arr) # 中位数
np.std(arr) # 标准差
np.var(arr) # 方差
np.min(arr) / np.max(arr) # 极值
np.argmin(arr) / np.argmax(arr) # 极值索引
6.4 按轴聚合
这是 Pandas 的 groupby 在 NumPy 层的对应物:
python
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
np.sum(arr2d, axis=0) # [5, 7, 9] --- 对每一列求和(沿着行方向压缩)
np.sum(arr2d, axis=1) # [6, 15] --- 对每一行求和
axis 记忆口诀:
axis=0是沿着行的方向"压扁"(列不变),即对每列操作;axis=1是沿着列的方向"压扁",即对每行操作。
七、排序与搜索
python
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])
# 排序
np.sort(arr) # 返回排好序的新数组
arr.sort() # 原地排序(不产生副本)
# 获取排序后各元素的原始索引(得分排名等场景常用)
idx = np.argsort(arr) # arr[idx] 就是排好序的结果
# 去重并排序
np.unique(arr) # [1, 2, 3, 4, 5, 6, 9]
np.unique(arr, return_counts=True) # 同时返回各唯一值的出现次数
# 查找满足条件的索引
np.where(arr > 3) # 返回元组 (array_of_indices,)
np.where(arr > 2, arr, 0) # 三参数形式:满足条件保留,否则置 0
# 查找插入位置(二分查找,要求数组已排序)
np.searchsorted(np.sort(arr), 5)
八、逻辑操作
python
arr = np.array([1, 2, 3, 4, 5])
# 全局判断
np.any(arr > 3) # True --- 是否存在满足条件的元素
np.all(arr > 0) # True --- 是否全部满足条件
# 条件筛选与替换
np.where(arr > 2, arr, 0) # >2 的保留,否则置 0
# 多条件(用列表传入)
conditions = [arr < 3, arr > 3]
choices = [arr * 2, arr * 3]
np.select(conditions, choices, default=0)
# 数值裁剪(去除异常值常用)
np.clip(arr, 2, 4) # <2 的变 2,>4 的变 4
九、累计操作
python
arr = np.array([1, 2, 3, 4, 5])
np.cumsum(arr) # [1, 3, 6, 10, 15] --- 累计和(收益曲线常用)
np.cumprod(arr) # [1, 2, 6, 24, 120] --- 累计积
# 沿指定轴
np.cumsum(arr2d, axis=0) # 每列的逐行累计和
十、数据类型转换
python
arr = np.array([1.5, 2.7, 3.9])
arr.astype(int) # [1, 2, 3] --- 浮点转整型(向下取整)
arr.astype(str) # ['1.5', '2.7', '3.9']
# 类型标识(推荐使用显式类型名)
np.int64(arr)
np.float32(arr)
np.bool_(arr)
精度与内存权衡: 如果数据量很大,把
float64转为float32可以节省一半内存,对精度要求不高的场景非常实用。
十一、拷贝与视图
这是 NumPy 最容易踩的坑:不小心修改了视图,原数组也跟着变了。
python
arr = np.array([1, 2, 3, 4, 5])
# 深拷贝(独立副本,修改不影响原数组)
arr_copy = arr.copy()
# 浅拷贝 / 视图(与原数组共享底层内存)
arr_view = arr.view()
# 切片通常返回视图
sliced = arr[1:4]
sliced[0] = 999 # arr 也会被修改!
# 判断两个数组是否共享内存
np.shares_memory(arr, arr_view) # True
np.shares_memory(arr, arr_copy) # False
arr.base is None # True 表示是"基数组"
经验法则: 除非确定要节省内存,否则对切片结果做修改前先
.copy()一下,能避免大量诡异的 bug。
十二、常用技巧速查
python
# 计算两数组的欧氏距离
dist = np.sqrt(np.sum((arr1 - arr2) ** 2))
# 归一化到 [0, 1]
normalized = (arr - arr.min()) / (arr.max() - arr.min())
# Z-score 标准化
standardized = (arr - arr.mean()) / arr.std()
# 找出 Top-K 值的索引
top3_idx = np.argsort(arr)[-3:]
# 按频率排序(词频统计常用)
values, counts = np.unique(arr, return_counts=True)
sorted_idx = np.argsort(-counts) # 降序
总结
NumPy 的核心功可以归纳为三件事:创建数组、操作数组形状、对数组做向量化计算。使用 NumPy 时牢记两条原则:
- 消除 for 循环------能用向量化运算就用向量化,这是性能提升的根本来源。
- 关注视图 vs 副本------弄清楚什么时候在操作原数据、什么时候在操作复制品,能避免 90% 的诡异 bug。
掌握了这些函数,你就有了用 Python 做数值计算的最底层工具箱。配合 Pandas 做数据处理、Matplotlib 做可视化,就是完整的数据分析三件套。