NumPy 不仅用于数组计算,也提供了轻量的数据读写、汇总统计和索引工具。本篇按“读写—统计—排序—去重—搜索”的路径组织常用方法,便于按任务快速查找。
一、NumPy 读取数据
(一)使用 NumPy 读写文本文件
1. 将一维或二维数组写入 TXT、CSV 文件
np.savetxt 把一维或二维数组写成文本。文件扩展名不会自动决定分隔方式;写 CSV 时仍需明确指定 delimiter=","。
| 参数 |
解释 |
| 参数 |
含义 |
| — |
— |
fname |
文件名或可写的文本文件对象;以 .gz 结尾时可直接写 gzip 文件 |
X |
要保存的一维或二维数组 |
fmt |
数值格式,例如 %d、%.2f,默认 %.18e |
delimiter |
列分隔符,默认空格;CSV 通常使用逗号 |
newline |
行分隔符,默认换行符 |
header、footer |
文件首尾的说明文本 |
comments |
写在 header、footer 前的注释前缀,默认 # |
encoding |
文本编码 |
1 2 3 4 5 6 7 8 9 10 11 12
| import numpy as np
arr = np.arange(6).reshape(2, 3) np.savetxt("values.txt", arr) np.savetxt( "values.csv", arr, fmt="%d", delimiter=",", header="a,b,c", comments="", )
|
2. 读取 TXT、CSV 文件
CSV 是以逗号分隔字段的文本格式,通常一行表示一条记录。结构整齐、各列可以转换为同一数据类型时,可以使用 np.loadtxt:
1 2 3 4 5 6 7 8
| np.loadtxt( fname, dtype=float, delimiter=None, skiprows=0, usecols=None, unpack=False, )
|
| 参数 |
解释 |
| 参数 |
含义 |
| — |
— |
fname |
文件名、文件对象、字符串列表或逐行产生文本的生成器 |
dtype |
结果的数据类型,默认是 Python 的 float |
delimiter |
字段分隔符,默认按空白分隔;CSV 通常使用逗号 |
skiprows |
跳过开头的若干行,默认 0 |
usecols |
只读取指定列,例如 (1, 2) |
unpack |
为 True 时转置结果,便于把各列分别赋给变量 |
1 2 3 4 5 6 7 8
| values = np.loadtxt("values.txt") csv_values = np.loadtxt( "values.csv", dtype=np.int64, delimiter=",", skiprows=1, ) print(csv_values)
|
dtype 决定内存中的数据类型,不决定打印时是否使用科学计数法。unpack=True 会转置读入结果;它常用于 x, y = np.loadtxt(..., unpack=True),但不会改变源文件。
含缺失值、混合文本列或复杂引号规则的数据更适合使用 np.genfromtxt 或表格处理工具;loadtxt 适合结构规则的数值文本。
(二)使用 NumPy 的 .npy 与 .npz 格式
np.save 保存一个数组,默认使用 .npy;np.savez 把多个数组装进一个未压缩的 .npz 归档。需要压缩时应明确使用 np.savez_compressed。
1 2 3 4
| arr = np.arange(6).reshape(2, 3) np.save("one.npy", arr) np.savez("many.npz", values=arr, doubled=arr * 2) np.savez_compressed("many-compressed.npz", values=arr)
|
2. 使用 load 读取数组
1 2 3 4 5 6
| one = np.load("one.npy") with np.load("many.npz") as archive: values = archive["values"] doubled = archive["doubled"]
print(one)
|
(三)使用 tofile 与 fromfile 读写原始数据
tofile 默认写入没有元数据的原始二进制流,fromfile 必须使用完全相同的 dtype,读回后还要自行恢复形状。它适合受控环境中的快速临时读写;文件不保存字节序和数据类型信息,不适合跨机器归档。长期保存优先使用 .npy。
1 2 3 4
| a = np.arange(30, dtype=np.int64).reshape(3, 5, 2) a.tofile("values.dat") restored = np.fromfile("values.dat", dtype=np.int64).reshape(a.shape) assert np.array_equal(a, restored)
|
二、常用统计函数
| 方法 |
说明 |
| sum |
对数组中全部或某轴向的元素求和,零长度的数组的sum为0 |
| mean |
算术平均数;空数组的结果为 NaN,并产生运行时警告 |
| std、var |
标准差和方差,自由度可调(默认为n) |
| min、max |
最小值和最大值 |
| argmin、argmax |
分别为最小和最大元素的索引 |
| cumsum |
所有元素的累计和 |
| cumprod |
所有元素的累计积 |
(一)求最大值和最小值
np.max 与 np.amax 是同一归约操作的两个入口;包含 NaN 而又希望忽略它时使用 np.nanmax。np.min、np.amin 与 np.nanmin 的关系相同。axis=0 沿行方向归约,得到每列结果;axis=1 沿列方向归约,得到每行结果。keepdims=True 会保留长度为 1 的归约轴,方便后续广播。
1 2 3 4 5 6 7 8
| a1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [3, 0, 9, 9]]) overall_max = np.max(a1) column_max = np.max(a1, axis=0) row_max = np.max(a1, axis=1)
a2 = np.array([0.0, np.nan, 2.0]) minimum_without_nan = np.nanmin(a2) minimum_with_nan = np.min(a2)
|
(二)求沿轴方向的极差
np.ptp 计算指定轴上的最大值减最小值。它会保留输入 dtype;窄整数可能在相减时溢出,因此不确定范围时应先转换为较宽的类型。
1 2 3
| arr1 = np.array([[0, 1, 2], [2, 4, 5]], dtype=np.int64) column_range = np.ptp(arr1, axis=0) row_range = np.ptp(arr1, axis=1)
|
1 2 3 4 5 6 7
| # 原数组 [[0 1 2] [2 4 5]] # 每列的极差 [2 3 3] # 每行的极差 [2 3]
|
(三)求百分位数
np.percentile(a, q, axis=None, method="linear") 计算百分位数,q 位于 0 到 100 之间,也可以传入多个位置,例如 [25, 75]。包含 NaN 而希望忽略它们时使用 np.nanpercentile。较早版本的 interpolation 参数已经改名为 method。
1 2 3 4 5 6 7 8 9 10 11
| r = np.array([ [13, 10, 4, 7, 9], [15, 16, 2, 1, 12], [14, 17, 14, 11, 7], [13, 11, 2, 11, 11], ])
quartiles = np.percentile(r, [25, 75]) overall_median = np.percentile(r, 50) column_median = np.percentile(r, 50, axis=0) row_median = np.percentile(r, 50, axis=1)
|
(四)求中位数
np.median 可以沿指定轴求中位数;需要忽略 NaN 时,对应函数是 np.nanmedian,不是 np.nanmean。
1 2 3
| print(np.median(r)) print(np.median(r, axis=0)) print(np.median(r, axis=1))
|
(五)求和与加权平均值
np.sum 计算总和,np.average 可以额外接收 weights 计算加权平均值。没有传入权重时,average 与普通算术平均数相同。
1 2 3 4 5
| a3 = np.arange(1, 7).reshape(2, 3) total = np.sum(a3) column_sum = np.sum(a3, axis=0) row_sum = np.sum(a3, axis=1) weighted = np.average(a3, axis=1, weights=[1, 1, 2])
|
(六)算术平均数
算术平均数是元素总和除以元素数量。np.mean 可以对整个数组或指定轴计算;np.nanmean 会忽略 NaN。
1 2 3
| overall_mean = np.mean(a3) column_mean = np.mean(a3, axis=0) row_mean = np.mean(a3, axis=1)
|
(七)标准差
标准差衡量数据围绕平均值的离散程度。NumPy 默认计算总体标准差,即除以 n;需要样本标准差时传入 ddof=1。
1 2 3 4
| arr1 = np.std(a3) arr2 = np.std(a3,axis=0) arr3 = np.std(a3,axis=1)
|
(八)方差
方差是各元素与平均值之差的平方的平均数,即 mean((x - x.mean()) ** 2)。NumPy 默认计算总体方差;样本方差同样使用 ddof=1。
1 2 3 4
| arr1 = np.var(a3) arr2 = np.var(a3,axis=0) arr3 = np.var(a3,axis=1)
|
三、NumPy 排序
np.sort 返回排好序的值,np.argsort 与 np.lexsort 返回排序后的索引。后两者适合让多组相关数据保持同步。
(一)sort
np.sort 默认沿最后一个轴排序并返回副本;指定 axis=None 时会先展平。结构化数组可以用 order 指定字段。
1 2 3 4 5 6 7
| a4 = np.array([[2, 1, 5], [3, 6, 4]]) row_sorted = np.sort(a4) column_sorted = np.sort(a4, axis=0)
dt = np.dtype([("name", "U10"), ("age", "i4")]) people = np.array([("Liu", 40), ("Wang", 18), ("Feng", 55)], dtype=dt) by_name = np.sort(people, order="name")
|
(二)argsort
np.argsort 返回能把数组排好序的索引,可用这些索引重排相关数组。
1 2 3
| scores = np.array([82, 95, 76]) order = np.argsort(scores) sorted_scores = scores[order]
|
(三)lexsort
np.lexsort 使用多个键做稳定的间接排序,传入的最后一个键是主键。
1 2 3 4 5
| names = np.array(["Li", "Wang", "Chen", "Zhao"]) classes = np.array([2, 1, 2, 1]) scores = np.array([90, 85, 85, 90]) order = np.lexsort((-scores, classes)) print(names[order])
|
四、去重与重复
(一)unique
np.unique 默认返回排好序的不同值。return_index 给出这些值首次出现的位置,return_inverse 给出重建原数组所需的索引,return_counts 给出出现次数。
1 2 3 4 5 6
| a2 = np.array([[2, 5, 5, 1], [8, 4, 6, 8]]) values = np.unique(a2) values, first_positions = np.unique(a2, return_index=True) values, inverse = np.unique(a2, return_inverse=True) values, counts = np.unique(a2, return_counts=True) restored = values[inverse].reshape(a2.shape)
|
(二)tile
np.tile 按给定次数重复整个数组。
1 2 3
| print(np.tile("a", 3)) a3 = np.array([1, 2]) print(np.tile(a3, (3, 2)))
|
(三)repeat
np.repeat 重复元素;提供 axis 时沿指定轴重复切片。它与重复整个数组的 tile 含义不同。
1 2 3 4 5 6 7
| a3 = np.array([1, 2]) print(np.repeat(a3, 3)) print(np.repeat(a3, (3, 2)))
a4 = np.array([[1, 2], [4, 6]]) rows = np.repeat(a4, 3, axis=0) columns = np.repeat(a4, 3, axis=1)
|
五、搜索和计数
(一)argmin、nanargmin 与 argmax
argmin 和 argmax 返回最小、最大元素的索引;不指定轴时,索引对应展平后的数组。nanargmin 会忽略 NaN。
1 2 3 4
| a4 = np.array([[2, 5, 5, 1], [8, 4, 6, 0]]) print(np.argmax(a4)) print(np.argmax(a4, axis=0)) print(np.argmax(a4, axis=1))
|
(二)nonzero
返回输入数组中非零元素的索引。
1 2 3
| rows, columns = np.nonzero(a4) print(rows) print(columns)
|
(三)where
只传条件时,np.where 返回满足条件的索引;同时传入 x 与 y 时,它会逐元素选择结果。
1 2 3
| a5 = np.array([2, 3, 1, 8, 4, 6]) positions = np.where(a5 > 5) replaced = np.where(a5 > 5, a5, 0)
|
np.extract 返回布尔条件为真的元素,与一维化后的布尔索引相近。
1 2 3
| a5 = np.array([2, 3, 0, 8, 4, 6]) is_even = np.mod(a5, 2) == 0 print(np.extract(is_even, a5))
|
(五)count_nonzero
np.count_nonzero 统计非零元素个数,也可以沿指定轴统计。
1 2
| print(np.count_nonzero(a5)) print(np.count_nonzero(a4, axis=1))
|
六、容易混淆的边界
| 场景 |
建议 |
| 文本里有缺失值或混合类型 |
使用 genfromtxt 或表格工具,不要强行套 loadtxt |
| 需要保存 dtype 与 shape |
使用 .npy / .npz,不要用裸 tofile 归档 |
| 数据含 NaN |
明确选择普通归约还是 nan* 版本 |
| 计算样本方差、样本标准差 |
显式写 ddof=1 |
ptp 处理窄整数 |
先转换到足够宽的 dtype,避免溢出 |
argmax 未指定轴 |
返回的是展平数组索引,不是行列坐标 |
lexsort 使用多个键 |
最后一个键是主键 |
本文示例面向 NumPy 2.x。继续学习数组形状、广播、视图与向量化,可阅读 NumPy 现代基础。
参考资料