在实际的数据处理和科学计算中,经常需要将数组保存到磁盘以便后续使用,或从外部文件加载数据。NumPy提供了多种文件输入输出函数,支持二进制格式和常见文本格式,能够高效地读写数组数据。
NumPy自定义的二进制格式(.npy 和 .npz)能够完整保存数组的形状、数据类型和数据,且读写速度非常快,是保存NumPy数组的首选方式。
np.save 和 np.load
np.save(file, arr) 将单个数组保存为 .npy 文件。np.load(file) 从 .npy 或 .npz 文件加载数组。
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# 保存到文件(默认扩展名 .npy)
np.save('my_array.npy', arr)
# 加载数组
loaded_arr = np.load('my_array.npy')
print("加载的数组:", loaded_arr)
输出:
加载的数组: [1 2 3 4 5]
np.savez 和 np.savez_compressed
np.savez 将多个数组保存到单个 .npz 文件(未压缩),可以通过关键字参数为数组命名。加载后得到一个类似字典的对象,通过数组名访问。np.savez_compressed 类似,但使用压缩减少文件大小。
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 保存多个数组(未压缩)
np.savez('arrays.npz', array1=a, array2=b)
# 压缩保存
np.savez_compressed('arrays_compressed.npz', array1=a, array2=b)
# 加载 .npz 文件
data = np.load('arrays.npz')
print("文件中的数组名:", list(data.keys()))
print("array1:", data['array1'])
print("array2:", data['array2'])
输出:
文件中的数组名: ['array1', 'array2']
array1: [1 2 3]
array2: [4 5 6]
np.load 加载 .npz 文件返回的对象在使用完后最好关闭(或使用上下文管理器),以释放文件句柄。例如:with np.load('arrays.npz') as data: ...
文本格式(如 CSV、TXT)便于人类阅读和与其他程序交换数据。NumPy 提供了 savetxt 和 loadtxt 以及更强大的 genfromtxt 函数。
np.savetxt 和 np.loadtxt
np.savetxt(fname, arr, delimiter=' ', ...) 将数组保存为文本文件。np.loadtxt(fname, delimiter=None, ...) 从文本文件加载数据,要求数据规则(每行列数相同)。
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 保存为 CSV 文件(逗号分隔)
np.savetxt('data.csv', arr, delimiter=',')
# 从 CSV 加载
loaded = np.loadtxt('data.csv', delimiter=',')
print("从 CSV 加载:\n", loaded)
# 可以指定跳过表头等
# 例如保存时添加表头
np.savetxt('data_with_header.csv', arr, delimiter=',', header='col1,col2,col3', comments='')
# 加载时跳过第一行
loaded_header = np.loadtxt('data_with_header.csv', delimiter=',', skiprows=1)
print("跳过表头:\n", loaded_header)
输出:
从 CSV 加载:
[[1. 2. 3.]
[4. 5. 6.]]
跳过表头:
[[1. 2. 3.]
[4. 5. 6.]]
np.genfromtxt - 处理复杂文本文件
genfromtxt 比 loadtxt 更强大,可以处理缺失值、自动检测数据类型、从多个来源读取等。
# 准备一个带缺失值的文本文件(例如 missing_data.csv)
# 内容:
# 1,2,3
# 4,,6
# 7,8,9
# 使用 genfromtxt 加载,缺失值变为 NaN
data = np.genfromtxt('missing_data.csv', delimiter=',', missing_values='', filling_values=np.nan)
print("带缺失值的数据:\n", data)
# 还可以指定列的数据类型
data_with_dtype = np.genfromtxt('data.csv', delimiter=',', dtype=None, names=True)
# 如果第一行是列名,设置 names=True 会将其作为字段名
print("结构化数组:\n", data_with_dtype)
输出:
带缺失值的数据:
[[ 1. 2. 3.]
[ 4. nan 6.]
[ 7. 8. 9.]]
结构化数组:
[(1., 2., 3.) (4., 5., 6.)]
对于超大数组(无法一次性装入内存),NumPy 提供了内存映射文件 np.memmap。它允许将磁盘上的大文件当作数组访问,数据按需加载到内存。
# 创建一个内存映射数组(写入模式)
mmap = np.memmap('large_array.dat', dtype='float32', mode='w+', shape=(1000, 1000))
# 像普通数组一样操作
mmap[0, :] = 1.0
mmap[:, 0] = 2.0
# 必须 flush 确保数据写入磁盘
mmap.flush()
# 以只读模式打开
mmap_read = np.memmap('large_array.dat', dtype='float32', mode='r', shape=(1000, 1000))
print(mmap_read[0, :5]) # 读取部分数据
输出:
[1. 1. 1. 1. 1.]
内存映射非常适合处理超出内存限制的大型数据集,但操作速度比内存数组慢。
除了上述基本函数,NumPy还提供了 np.fromfile 和 np.tofile 用于直接读写二进制数据(不带元数据),以及 np.fromstring 从字符串或缓冲区创建数组。
# tofile / fromfile 不保存形状和类型信息,需手动指定
arr = np.array([1, 2, 3])
arr.tofile('raw.bin')
# 加载时需要指定相同的数据类型和形状
loaded_raw = np.fromfile('raw.bin', dtype=int)
print("fromfile 加载:", loaded_raw) # 但形状丢失了,需要 reshape
输出:
fromfile 加载: [1 2 3]
dtype 参数指定。read_csv 和 to_csv 对于带标签的表格数据更方便,但底层也可调用 NumPy 函数。np.savez_compressed 通常能显著减小文件体积,特别是对于具有重复模式的数据。加载 .npz 文件后,可以用 data.files 查看所有数组名。
本章介绍了 NumPy 的文件输入输出功能,包括高效的二进制格式(save/load)、灵活的文本格式(savetxt/loadtxt/genfromtxt)以及处理超大数据集的内存映射。掌握这些工具,可以轻松地将数据持久化到磁盘,并在不同程序间共享。下一章我们将学习 NumPy 的结构化数组,用于处理混合类型的数据。