高级数组输入输出
我在第4章中讲过,np.save和np.load可用于读写磁盘上以二进制格式存储的数组。其实还有一些工具可用于更为复杂的场景。尤其是内存映像(memory map),它使你能处理在内存中放不下的数据集。
内存映像文件
内存映像文件是一种将磁盘上的非常大的二进制数据文件当做内存中的数组进行处理的方式。NumPy实现了一个类似于ndarray的memmap对象,它允许将大文件分成小段进行读写,而不是一次性将整个数组读入内存。memmap也拥有跟普通数组一样的方法,因此,基本上只要是能用于ndarray的算法就也能用于memmap。
使用函数np.memmap并传入一个文件路径、数据类型、形状以及文件模式,即可创建一个新的memmap:
- In [216]: mmap = np.memmap('mymmap', dtype='float64', mode='w+', shape=(10000, 10000))
- In [217]: mmap
- Out[217]:
- memmap([[ 0., 0., 0., ..., 0., 0., 0.],
- [ 0., 0., 0., ..., 0., 0., 0.],
- [ 0., 0., 0., ..., 0., 0., 0.],
- ...,
- [ 0., 0., 0., ..., 0., 0., 0.],
- [ 0., 0., 0., ..., 0., 0., 0.],
- [ 0., 0., 0., ..., 0., 0., 0.]])
对memmap切片将会返回磁盘上的数据的视图:
- In [218]: section = mmap[:5]
如果将数据赋值给这些视图:数据会先被缓存在内存中(就像是Python的文件对象),调用flush即可将其写入磁盘。
- In [219]: section[:] = np.random.randn(5, 10000)
- In [220]: mmap.flush()
- In [221]: mmap
- Out[221]:
- memmap([[-0.1614, -0.1768, 0.422 , ..., -0.2195, -0.1256, -0.4012],
- [ 0.4898, -2.2219, -0.7684, ..., -2.3517, -1.0782, 1.3208],
- [-0.6875, 1.6901, -0.7444, ..., -1.4218, -0.0509, 1.2224],
- ...,
- [ 0. , 0. , 0. , ..., 0. , 0. , 0. ],
- [ 0. , 0. , 0. , ..., 0. , 0. , 0. ],
- [ 0. , 0. , 0. , ..., 0. , 0. , 0. ]])
- In [222]: del mmap
只要某个内存映像超出了作用域,它就会被垃圾回收器回收,之前对其所做的任何修改都会被写入磁盘。当打开一个已经存在的内存映像时,仍然需要指明数据类型和形状,因为磁盘上的那个文件只是一块二进制数据而已,没有任何元数据:
- In [223]: mmap = np.memmap('mymmap', dtype='float64', shape=(10000, 10000))
- In [224]: mmap
- Out[224]:
- memmap([[-0.1614, -0.1768, 0.422 , ..., -0.2195, -0.1256, -0.4012],
- [ 0.4898, -2.2219, -0.7684, ..., -2.3517, -1.0782, 1.3208],
- [-0.6875, 1.6901, -0.7444, ..., -1.4218, -0.0509, 1.2224],
- ...,
- [ 0. , 0. , 0. , ..., 0. , 0. , 0. ],
- [ 0. , 0. , 0. , ..., 0. , 0. , 0. ],
- [ 0. , 0. , 0. , ..., 0. , 0. , 0. ]])
由于内存映像其实就是一个存放在磁盘上的ndarray,所以完全可以使用前面介绍的结构化dtype。
HDF5及其他数组存储方式
PyTables和h5py这两个Python项目可以将NumPy的数组数据存储为高效且可压缩的HDF5格式(HDF意思是“层次化数据格式”)。你可以安全地将好几百GB甚至TB的数据存储为HDF5格式。很遗憾,这些库的用法超出了本书的范围。
PyTables提供了一些用于结构化数组的高级查询功能,而且还能添加列索引以提升查询速度。这跟关系型数据库所提供的表索引功能非常类似。