我有一个脚本可生成numpy
array
个dtype=float
,形状为(1e3, 1e6)
。现在我正在使用np.save
和np.load
来执行数组的IO操作。但是,每个阵列的这些功能需要几秒钟。是否有更快的方法来保存和加载整个数组(即,不对其内容进行假设并减少它们)?只要数据完全保留,我就可以在保存之前将array
转换为另一种类型。
答案 0 :(得分:33)
对于非常大的阵列,我听说过几种解决方案,而且他们主要是在I / O上懒惰:
ndarray
(任何接受ndarray的类接受memmap
)对HDF5使用Python绑定,HDF5是一种支持大数据的文件格式,如PyTables或h5py
Python's pickling系统(在比赛中,为Pythonicity而非速度提及)
来自NumPy.memmap的文档:
为存储在磁盘上的二进制文件中的数组创建内存映射。
内存映射文件用于访问磁盘上的大段文件,而无需将整个文件读入内存
memmap对象可以在接受ndarray的任何地方使用。给定任何memmap
fp
,isinstance(fp, numpy.ndarray)
将返回True。
来自h5py doc
允许您存储大量数值数据,并轻松地从NumPy处理该数据。例如,您可以切片存储在磁盘上的多TB数据集,就好像它们是真正的NumPy数组一样。数以千计的数据集可以存储在一个文件中,按照您的需要进行分类和标记。
格式支持以各种方式压缩数据(为相同的I / O读取加载更多位),但这意味着数据变得不容易单独查询,但在您的情况下(纯粹加载/转储数组)它可能高效率
答案 1 :(得分:18)
这是与PyTables的比较。
由于内存限制,我无法升级到(int(1e3), int(1e6)
。
因此,我使用了一个较小的数组:
data = np.random.random((int(1e3), int(1e5)))
NumPy save
:
%timeit np.save('array.npy', data)
1 loops, best of 3: 4.26 s per loop
NumPy load
:
%timeit data2 = np.load('array.npy')
1 loops, best of 3: 3.43 s per loop
PyTables写作:
%%timeit
with tables.open_file('array.tbl', 'w') as h5_file:
h5_file.create_array('/', 'data', data)
1 loops, best of 3: 4.16 s per loop
PyTables阅读:
%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
data2 = h5_file.root.data.read()
1 loops, best of 3: 3.51 s per loop
数字非常相似。因此PyTables在这里没有真正的好处。 但我们非常接近我的SSD的最大写入和读取速度。
写作:
Maximum write speed: 241.6 MB/s
PyTables write speed: 183.4 MB/s
读:
Maximum read speed: 250.2
PyTables read speed: 217.4
由于数据的随机性,压缩并没有真正帮助:
%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
h5_file.create_carray('/', 'data', obj=data)
1 loops, best of 3: 4.08 s per loop
读取压缩数据会慢一点:
%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
data2 = h5_file.root.data.read()
1 loops, best of 3: 4.01 s per loop
这与常规数据不同:
reg_data = np.ones((int(1e3), int(1e5)))
写作速度明显加快:
%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
h5_file.create_carray('/', 'reg_data', obj=reg_data)
1个循环,最佳3:849 ms /循环
阅读同样如此:
%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
reg_data2 = h5_file.root.reg_data.read()
1 loops, best of 3: 1.7 s per loop
结论:您的数据越频繁,使用PyTable就越快。
答案 2 :(得分:5)
根据我的经验,到目前为止,在硬盘和内存之间传输数据时,np.save()& np.load()是最快的解决方案。 在我意识到这个结论之前,我在数据库和HDFS系统上严重依赖我的数据加载。 我的测试表明: 数据库数据加载(从硬盘到内存)带宽可能约为50 MBps(Byets / Second),但np.load()带宽几乎与我的硬盘最大带宽相同:2GBps(Byets / Second)。两个测试环境都使用最简单的数据结构。
我不认为使用几秒钟加载具有形状的阵列是一个问题:(1e3,1e6)。例如。 你的数组形状是(1000,1000000),它的数据类型是float128,那么纯数据大小是(128/8)* 1000 * 1,000,000 = 16,000,000,000 = 16GBytes 如果需要4秒钟, 然后您的数据加载带宽为16GBytes / 4Seconds = 4GBps。 SATA3最大带宽为600MBps = 0.6GBps,您的数据加载带宽已经是它的6倍,您的数据加载性能几乎可以与DDR's maximum bandwidth竞争,您还想要什么呢?
所以我的最终结论是:
不要使用python的Pickle,不要使用任何数据库,不要使用任何大数据系统将数据存储到硬盘中,如果可以使用的话np.save()和np.load()。到目前为止,这两个函数是在硬盘和内存之间传输数据的最快解决方案。
我还测试了HDF5,发现它比np.load()和np.save()慢,所以使用np.save()& np .load()如果你的平台上有足够的DDR内存。
答案 3 :(得分:0)
我已经比较了使用perfplot的几种方法(我的一个项目)。结果如下:
对于大型数组,所有方法都差不多快。文件大小也相等,这是可以预期的,因为输入数组是随机的双精度数,因此几乎不可压缩。
用于复制情节的代码:
import perfplot
import pickle
import numpy
import h5py
import tables
import zarr
def npy_write(data):
numpy.save("npy.npy", data)
def hdf5_write(data):
f = h5py.File("hdf5.h5", "w")
f.create_dataset("data", data=data)
def pickle_write(data):
with open("test.pkl", "wb") as f:
pickle.dump(data, f)
def pytables_write(data):
f = tables.open_file("pytables.h5", mode="w")
gcolumns = f.create_group(f.root, "columns", "data")
f.create_array(gcolumns, "data", data, "data")
f.close()
def zarr_write(data):
zarr.save("out.zarr", data)
perfplot.save(
"write.png",
setup=numpy.random.rand,
kernels=[npy_write, hdf5_write, pickle_write, pytables_write, zarr_write],
n_range=[2 ** k for k in range(28)],
xlabel="len(data)",
logx=True,
logy=True,
equality_check=None,
)
用于复制情节的代码:
import perfplot
import pickle
import numpy
import h5py
import tables
import zarr
def setup(n):
data = numpy.random.rand(n)
# write all files
#
numpy.save("out.npy", data)
#
f = h5py.File("out.h5", "w")
f.create_dataset("data", data=data)
f.close()
#
with open("test.pkl", "wb") as f:
pickle.dump(data, f)
#
f = tables.open_file("pytables.h5", mode="w")
gcolumns = f.create_group(f.root, "columns", "data")
f.create_array(gcolumns, "data", data, "data")
f.close()
#
zarr.save("out.zip", data)
def npy_read(data):
return numpy.load("out.npy")
def hdf5_read(data):
f = h5py.File("out.h5", "r")
out = f["data"][()]
f.close()
return out
def pickle_read(data):
with open("test.pkl", "rb") as f:
out = pickle.load(f)
return out
def pytables_read(data):
f = tables.open_file("pytables.h5", mode="r")
out = f.root.columns.data[()]
f.close()
return out
def zarr_read(data):
return zarr.load("out.zip")
perfplot.show(
setup=setup,
kernels=[
npy_read,
hdf5_read,
pickle_read,
pytables_read,
zarr_read,
],
n_range=[2 ** k for k in range(28)],
xlabel="len(data)",
logx=True,
logy=True,
)