Numpy memmap加载.npy文件数组损坏,如何正确使用?
正确使用numpy.memmap加载npy文件的方法
问题根源
直接用np.memmap加载.npy文件会出错,因为.npy文件并非纯二进制数据——文件开头包含元信息头部(存储了数组的 dtype、shape、版本等数据),直接memmap会把头部字节当成数组数据的一部分,导致形状混乱、数据损坏。
解决方法:跳过npy文件头部加载
要正确memmap .npy 文件,需要先计算头部的总长度,然后从头部结束的位置开始映射数据。
步骤1:获取npy文件的头部偏移量
以下函数可以读取npy文件的头部信息,计算出数据起始的字节偏移:
import numpy as np def get_npy_data_offset(file_path): with open(file_path, 'rb') as f: # 验证npy文件魔法数 magic = f.read(6) if magic != b'\x93NUMPY': raise ValueError("输入不是有效的npy文件") # 读取版本号 version_major, version_minor = np.frombuffer(f.read(2), dtype=np.uint8) # 读取头部长度 if version_major == 1: header_len = np.frombuffer(f.read(2), dtype=np.uint16)[0] elif version_major == 2: header_len = np.frombuffer(f.read(4), dtype=np.uint32)[0] else: raise ValueError(f"不支持的npy版本:{version_major}.{version_minor}") # 计算头部总长度(含魔法数、版本、长度标识、头部内容) base_header_len = 6 + 2 + (2 if version_major ==1 else 4) + header_len # numpy的npy文件数据部分按64字节对齐,需调整偏移量 alignment = 64 return ((base_header_len + alignment -1) // alignment) * alignment
步骤2:加载memmap数组
先获取偏移量,再结合已知的数组shape和dtype(可通过np.load小范围读取或提前记录),创建正确的memmap对象:
# 示例操作 offset = get_npy_data_offset('f.npy') # 若已知shape和dtype可直接指定,否则先读取元信息(避免加载整个数组) temp = np.load('f.npy', mmap_mode='r') # 临时加载仅读取头部,不占用大量内存 target_shape = temp.shape target_dtype = temp.dtype # 创建正确的memmap c = np.memmap('f.npy', dtype=target_dtype, mode='r', shape=target_shape, offset=offset) # 验证结果 assert np.array_equal(c, temp)
替代方案:保存为纯二进制文件
如果可以重新保存数据,可直接将数组保存为无头部的纯二进制文件,这样memmap时无需处理偏移:
# 保存纯二进制 a = np.arange(65536) a.tofile('f.bin') # 直接memmap加载 c = np.memmap('f.bin', dtype=np.int64, mode='r', shape=a.shape)
注意:此方法需要自行记录数组的dtype和shape,否则无法正确加载。
针对真实数据的pickle问题说明
你的真实数据无法用np.load(mmap_mode='r')加载,是因为文件包含pickle序列化的内容,但np.memmap本身不处理pickle——只要你能提前获取到数组的shape和dtype(比如从数据文档、元数据文件,或用np.load读取头部而不加载整个数组),上述跳过头部的memmap方法依然有效。
内容的提问来源于stack exchange,提问作者maestro
相关产品推荐
相关产品推荐

