NumPy 不仅用于数组计算,也提供了轻量的数据读写、汇总统计和索引工具。本篇按“读写—统计—排序—去重—搜索”的路径组织常用方法,便于按任务快速查找。

一、NumPy 读取数据

(一)使用 NumPy 读写文本文件

1. 将一维或二维数组写入 TXT、CSV 文件

np.savetxt 把一维或二维数组写成文本。文件扩展名不会自动决定分隔方式;写 CSV 时仍需明确指定 delimiter=","

参数 解释
参数 含义
fname 文件名或可写的文本文件对象;以 .gz 结尾时可直接写 gzip 文件
X 要保存的一维或二维数组
fmt 数值格式,例如 %d%.2f,默认 %.18e
delimiter 列分隔符,默认空格;CSV 通常使用逗号
newline 行分隔符,默认换行符
headerfooter 文件首尾的说明文本
comments 写在 headerfooter 前的注释前缀,默认 #
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 保存一个数组,默认使用 .npynp.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)

(三)使用 tofilefromfile 读写原始数据

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.maxnp.amax 是同一归约操作的两个入口;包含 NaN 而又希望忽略它时使用 np.nanmaxnp.minnp.aminnp.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) # 9
column_max = np.max(a1, axis=0) # [5, 6, 9, 9]
row_max = np.max(a1, axis=1) # [4, 8, 9]

a2 = np.array([0.0, np.nan, 2.0])
minimum_without_nan = np.nanmin(a2) # 0.0
minimum_with_nan = np.min(a2) # nan

(二)求沿轴方向的极差

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) # [2, 3, 3]
row_range = np.ptp(arr1, axis=1) # [2, 3]
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) # 21
column_sum = np.sum(a3, axis=0) # [5, 7, 9]
row_sum = np.sum(a3, axis=1) # [6, 15]
weighted = np.average(a3, axis=1, weights=[1, 1, 2])

(六)算术平均数

算术平均数是元素总和除以元素数量。np.mean 可以对整个数组或指定轴计算;np.nanmean 会忽略 NaN。

1
2
3
overall_mean = np.mean(a3)          # 3.5
column_mean = np.mean(a3, axis=0) # [2.5, 3.5, 4.5]
row_mean = np.mean(a3, axis=1) # [2.0, 5.0]

(七)标准差

标准差衡量数据围绕平均值的离散程度。NumPy 默认计算总体标准差,即除以 n;需要样本标准差时传入 ddof=1

1
2
3
4
#标准差
arr1 = np.std(a3) #结果1.707825127659933
arr2 = np.std(a3,axis=0)#按垂直方向,结果[1.5 1.5 1.5]
arr3 = np.std(a3,axis=1)#按水平方向,结果[0.81649658 0.81649658]

(八)方差

方差是各元素与平均值之差的平方的平均数,即 mean((x - x.mean()) ** 2)。NumPy 默认计算总体方差;样本方差同样使用 ddof=1

1
2
3
4
#方差
arr1 = np.var(a3) #结果2.9166666666666665
arr2 = np.var(a3,axis=0)#按垂直方向,结果[2.25 2.25 2.25]
arr3 = np.var(a3,axis=1)#按水平方向,结果[0.66666667 0.66666667]

三、NumPy 排序

np.sort 返回排好序的值,np.argsortnp.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) # [[1, 2, 5], [3, 4, 6]]
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] # [76, 82, 95]

(三)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))) # 3 行,每行重复两次原数组

(三)repeat

np.repeat 重复元素;提供 axis 时沿指定轴重复切片。它与重复整个数组的 tile 含义不同。

1
2
3
4
5
6
7
a3 = np.array([1, 2])
print(np.repeat(a3, 3)) # [1, 1, 1, 2, 2, 2]
print(np.repeat(a3, (3, 2))) # [1, 1, 1, 2, 2]

a4 = np.array([[1, 2], [4, 6]])
rows = np.repeat(a4, 3, axis=0)
columns = np.repeat(a4, 3, axis=1) # [[1, 1, 1, 2, 2, 2], ...]

五、搜索和计数

(一)argminnanargminargmax

argminargmax 返回最小、最大元素的索引;不指定轴时,索引对应展平后的数组。nanargmin 会忽略 NaN。

1
2
3
4
a4 = np.array([[2, 5, 5, 1], [8, 4, 6, 0]])
print(np.argmax(a4)) # 4:展平后最大值的位置
print(np.argmax(a4, axis=0)) # [1, 0, 1, 0]
print(np.argmax(a4, axis=1)) # [1, 0]

(二)nonzero

返回输入数组中非零元素的索引。

1
2
3
rows, columns = np.nonzero(a4)
print(rows) # [0, 0, 0, 0, 1, 1, 1]
print(columns) # [0, 1, 2, 3, 0, 1, 2]

(三)where

只传条件时,np.where 返回满足条件的索引;同时传入 xy 时,它会逐元素选择结果。

1
2
3
a5 = np.array([2, 3, 1, 8, 4, 6])
positions = np.where(a5 > 5) # (array([3, 5]),)
replaced = np.where(a5 > 5, a5, 0) # [0, 0, 0, 8, 0, 6]

(四)extract

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)) # [2, 0, 8, 4, 6]

(五)count_nonzero

np.count_nonzero 统计非零元素个数,也可以沿指定轴统计。

1
2
print(np.count_nonzero(a5))          # 5
print(np.count_nonzero(a4, axis=1)) # [4, 3]

六、容易混淆的边界

场景 建议
文本里有缺失值或混合类型 使用 genfromtxt 或表格工具,不要强行套 loadtxt
需要保存 dtype 与 shape 使用 .npy / .npz,不要用裸 tofile 归档
数据含 NaN 明确选择普通归约还是 nan* 版本
计算样本方差、样本标准差 显式写 ddof=1
ptp 处理窄整数 先转换到足够宽的 dtype,避免溢出
argmax 未指定轴 返回的是展平数组索引,不是行列坐标
lexsort 使用多个键 最后一个键是主键

本文示例面向 NumPy 2.x。继续学习数组形状、广播、视图与向量化,可阅读 NumPy 现代基础

参考资料