前言
上一节我们理解了ndarray的核心概念(shape、dtype、strides、axis),本节课聚焦于如何操作数组中的数据。我们不可能每一个API都讲到,AI时代不再局限于API的记忆,需要有一个概念知道Numpy可以做什么,附带官网地址。
数组创建
除了从列表直接创建,NumPy 提供了很多便捷的构造方法。
js
print('zeros(2, 3):', np.zeros((2, 3)))
print('ones(2, 3):', np.ones((2, 3)))
print('full(2, 3, 7):', np.full((2, 3), 7))
print('eye(3):', np.eye(3))
js
# np.arange: 创建等差数组
print('arange(0, 10, 2):', np.arange(0, 10, 2))
# np.linspace: 创建等间隔数组
print('linspace(0, 1, 5):', np.linspace(0, 1, 5))
# np.linspace: 创建等间隔数组
print('linspace(0, 1, 5, endpoint=False):', np.linspace(0, 1, 5, endpoint=False))
js
# default_rng: 创建随机数生成器
rng = np.random.default_rng(41)
# uniform: 生成均匀分布随机数
print('uniform(0, 1, (2,3)):\n', rng.uniform(0, 1, (2,3)), '\n')
# normal: 生成正态分布随机数
print('normal(0, 1, (2,3)):\n', rng.normal(0, 1, (2,3)), '\n')
# integers: 生成随机整数
print('integers(0, 10, (2,3)):\n', rng.integers(0, 10, (2,3)))
这里np.random.default_rng(41)的参数41代表参数seed,使用相同种子,每次运行会得到相同的随机数序列,便于复现实验。
索引与切片
NumPy 的索引语法与 Python 列表类似,但支持多维操作。
基本索引与切片
语法:arr[start:stop:step],多维就用逗号分隔。
- start:起始索引,默认 0
- stop:结束索引(不包含),默认数组长度
- step:步长,默认 1
注意:切片返回的是视图(view),不是拷贝------改切片会影响原数组。
js
# np.arange: 创建等差数组
arr = np.arange(12).reshape(3, 4)
print('原数组:\n', arr, '\n')
print('arr[0]:', arr[0])
print('arr[:, 1]:\n', arr[:, 1:])
print('arr[1:, 2:]:\n', arr[1:, 2:])
lua
原数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
arr[0]: [0 1 2 3]
arr[:, 1]:
[[ 1 2 3]
[ 5 6 7]
[ 9 10 11]]
arr[1:, 2:]:
[[ 6 7]
[10 11]]
js
view = arr[1:, 2:]
view[0, 0] = 99
print('修改视图后原数组:\n', arr)
# copy: 创建数组的副本
copy = arr[1:, 2:].copy()
lua
修改视图后原数组:
[[ 0 1 2 3]
[ 4 5 99 7]
[ 8 9 10 11]]
花式索引(Fancy Indexing)
用整数数组选取不连续的行/列。返回的是拷贝。
js
# np.arange: 创建等差数组
arr = np.arange(12).reshape(4, 3)
print('数组:\n', arr, '\n')
print('arr[[0, 2]]:\n', arr[[0, 2]], '\n')
rows = [0, 1, 3]
cols = [2, 1, 0]
print('arr[rows, cols]:', arr[rows, cols])
lua
数组:
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
arr[[0, 2]]:
[[0 1 2]
[6 7 8]]
arr[rows, cols]: [2 4 9]
这里面arr[rows, cols]可能大家不好理解,我们知道对于二维数组我们要取其中一个值是用arr[row, col],这里同样也是这个道理,只不过复数的情况下取出来的是个数组,拆解上面的语句arr[[0, 1, 3], [2, 1, 0]],可以理解依次取数组中0, 2,1, 1,3, 0这三个位置的数据,返回2, 4, 9。
布尔索引(Boolean Indexing)
用布尔数组作为掩码(mask),选取满足条件的元素。返回的是拷贝。
js
# np.array: 从列表创建 NumPy 数组
arr = np.array([10, 25, 3, 47, 8, 19])
mask = arr > 15
print('mask:', mask)
print('arr[arr > 15]:', arr[mask])
print('arr[arr % 2 == 0]:', arr[arr % 2 == 0])
vbnet
mask: [False True False True False True]
arr[arr > 15]: [25 47 19]
arr[arr % 2 == 0]: [10 8]
js
arr = np.array([5, 12, 18, 25, 30, 7, 42])
print('(>10) & (<30):', arr[(arr > 10) & (arr < 30)])
print('(<10) | (>30):', arr[(arr < 10) | (arr > 30)])
ini
(>10) & (<30): [12 18 25]
(<10) | (>30): [ 5 7 42]
变形与重塑
改变数组的形状而不改数据。总元素数必须一致,用 -1 让 NumPy 自动算。
js
# np.arange: 创建等差数组
arr = np.arange(12)
print('原始:', arr, '\n')
# reshape: 重塑数组形状
print('reshape(3, 4):\n', arr.reshape(3, 4), '\n')
# reshape: 重塑数组形状
print('reshape(2, -1):\n', arr.reshape(2, -1), '\n')
# reshape: 重塑数组形状
print('reshape(2, 2, 3):\n', arr.reshape(2, 2, 3))
lua
原始: [ 0 1 2 3 4 5 6 7 8 9 10 11]
reshape(3, 4):
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
reshape(2, -1):
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
reshape(2, 2, 3):
[[[ 0 1 2]
[ 3 4 5]]
[[ 6 7 8]
[ 9 10 11]]]
js
# np.array: 从列表创建 NumPy 数组
arr = np.array([1, 2, 3, 4])
# np.resize: 改变数组大小(可重复填充)
print('np.resize(arr, (2, 3)):\n', np.resize(arr, (2, 3)))
# np.resize: 改变数组大小(可重复填充)
print('np.resize(arr, (2, 2)):\n', np.resize(arr, (2, 2)))
lua
np.resize(arr, (2, 3)):
[[1 2 3]
[4 1 2]]
np.resize(arr, (2, 2)):
[[1 2]
[3 4]]
数学运算
NumPy 的所有运算符都是向量化的------对整个数组一次性操作,不用写循环。
js
# np.array: 从列表创建 NumPy 数组
arr = np.array([1, 2, 3, 4])
print('原数组:', arr)
print('arr + 10:', arr + 10)
print('arr * 2:', arr * 2)
print('arr ** 2:', arr ** 2)
print('arr > 2:', arr > 2)
yaml
原数组: [1 2 3 4]
arr + 10: [11 12 13 14]
arr * 2: [2 4 6 8]
arr ** 2: [ 1 4 9 16]
arr > 2: [False False True True]
js
# np.array: 从列表创建 NumPy 数组
a = np.array([10, 20, 30])
# np.array: 从列表创建 NumPy 数组
b = np.array([1, 2, 3])
print('a + b:', a + b)
print('a - b:', a - b)
print('a * b:', a * b)
print('a / b:', a / b)
less
a + b: [11 22 33]
a - b: [ 9 18 27]
a * b: [10 40 90]
a / b: [10. 10. 10.]
统计运算
对数组做聚合统计,可以用 axis 指定沿哪个方向算。
js
# np.arange: 创建等差数组
arr = np.arange(12).reshape(3, 4).astype(float)
print('数组:\n', arr, '\n')
# sum: 计算所有元素之和
print('sum:', arr.sum())
# mean: 计算算术平均值
print('mean:', arr.mean())
# sum: 计算所有元素之和
print('sum(axis=0) 按列求和:', arr.sum(axis=0))
# sum: 计算所有元素之和
print('sum(axis=1) 按行求和:', arr.sum(axis=1))
# min: 找出最小值
print('min:', arr.min(), ', max:', arr.max())
# argmin: 找出最小值所在的索引
print('argmin:', arr.argmin(), ', argmax:', arr.argmax())
ini
数组:
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
sum: 66.0
mean: 5.5
sum(axis=0) 按列求和: [12. 15. 18. 21.]
sum(axis=1) 按行求和: [ 6. 22. 38.]
min: 0.0 , max: 11.0
argmin: 0 , argmax: 11
标准差
标准差(Standard Deviation)是统计学中衡量数据波动程度(即离散程度)最常用的指标。简单来说,它告诉你数据围绕平均值有多"散"。
标准差越小:说明数据越集中,平均值越有代表性(大家成绩都差不多)。
标准差越大:说明数据越分散,平均值越不靠谱(有人考100分,有人考0分)。

js
print("arr", arr)
print("mean", arr.mean())
# std: 计算总体标准差
print('std总体标准差:', arr.std())
# std: 计算样本标准差
print('std(ddof=1)样本标准差:', arr.std(ddof=1))
# 变异系数
print('变异系数CV:', arr.std() / arr.mean())
ini
arr [[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
mean 5.5
std总体标准差: 3.452052529534663
std(ddof=1)样本标准差: 3.605551275463989
变异系数CV: 0.6276459144608478
| CV 范围 | 离散程度 | 直观感受 | 典型场景举例 |
|---|---|---|---|
| < 10% | 非常小 | 数据极度集中,几乎一模一样 | 工厂自动化生产的零件尺寸、精密仪器读数 |
| 10% ~ 20% | 较小 | 有波动但很稳定,平均值可靠 | 成年人的身高、体重、日常气温 |
| 20% ~ 30% | 中等 | 正常波动范围,尚可接受 | 学生考试成绩、员工绩效评分 |
| 30% ~ 50% | 较大 | 数据比较分散,平均值代表性下降 | 城市房价、不同地区的薪资水平 |
| > 50% | 非常大 | 极度分散,平均值几乎没意义 | 股票日收益率、创业公司估值、个人消费支出 |
条件筛选与 np.where
np.where(condition, x, y) ------ 如果条件成立取 x 的值,否则取 y 的值。类似向量化的 if-else。
js
# np.array: 从列表创建 NumPy 数组
arr = np.array([10, -5, 3, -8, 0, 15])
# np.where: 根据条件筛选或替换值
result = np.where(arr < 0, 0, arr)
print('原数组:', arr)
print('负数变 0:', result)
ini
原数组: [10 -5 3 -8 0 15]
负数变 0: [10 0 3 0 0 15]
拼接与分割
把多个数组合并,或把一个数组拆成多份。
js
# np.array: 从列表创建 NumPy 数组
a = np.array([[1, 2], [3, 4]])
# np.array: 从列表创建 NumPy 数组
b = np.array([[5, 6]])
# np.vstack: 垂直堆叠数组
print('vstack (垂直拼):\n', np.vstack([a, b]), '\n')
# np.concatenate: 沿指定轴拼接数组
print('concatenate axis=0:\n', np.concatenate([a, b], axis=0))
lua
vstack (垂直拼):
[[1 2]
[3 4]
[5 6]]
concatenate axis=0:
[[1 2]
[3 4]
[5 6]]
排序
排序在很多场景中都会用到。
js
# np.array: 从列表创建 NumPy 数组
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print('原数组:', arr)
# np.sort: 返回排序后的数组
print('np.sort(arr):', np.sort(arr))
# np.argsort: 返回排序后的索引数组
print('np.argsort(arr):', np.argsort(arr))
# np.unique: 返回唯一值
print('np.unique(arr):', np.unique(arr))
ini
原数组: [3 1 4 1 5 9 2 6]
np.sort(arr): [1 1 2 3 4 5 6 9]
np.argsort(arr): [1 3 6 0 2 4 7 5]
np.unique(arr): [1 2 3 4 5 6 9]
广播(Broadcasting)
广播是 NumPy 的特色:对不同形状的数组做运算时,NumPy 会自动把小数组「扩展」成和大数组一样的形状,无需手动复制。
规则:从尾部维度开始比,维度相同或有一个是 1 就能广播。
js
# np.array: 从列表创建 NumPy 数组
arr = np.array([1, 2, 3])
print('arr + 10:', arr + 10)
print('arr * 5:', arr * 5)
ini
arr + 10: [11 12 13]
arr * 5: [ 5 10 15]
js
# np.arange: 创建等差数组
matrix = np.arange(12).reshape(3, 4)
# np.array: 从列表创建 NumPy 数组
row = np.array([10, 20, 30, 40])
# np.array: 从列表创建 NumPy 数组
col = np.array([[100], [200], [300]])
print('matrix:\n', matrix, '\n')
print('matrix + row (行广播):\n', matrix + row, '\n')
#reshape: 重塑数组形状
print('matrix + col[:, None] (列广播):\n', matrix + col)
总结
本文只介绍了常规操作,大家应该能看出来Numpy更偏数学属性,与线性代数类似,不必纠结于API调用,理解它能为我们提供什么样的服务就可以。