本文来探索一下python中提供的各种数据保存格式的性能如何。主要以一个 ndarray 格式的数据进行处理分析。包括下面几种方式:
- .bin格式, np.tofile() 和 np.fromfile()
- .npy格式,np.save() 和 np.load()
- .txt 或者 .csv格式,np.savetxt() 和 np.loadtxt()
- .h5 格式,h5py.File(,’r’ 或者 ‘w’)
- .pkl 格式, pickle.dump()和pickle.load()
import numpy as np
from __future__ import print_function
import time
a = np.random.randint(0, 100, size=(10000, 5000))
print(a.dtype, a.shape)
print(a[:2])
int64 (10000, 5000)
[[90 96 38 ..., 67 40 79]
[40 12 71 ..., 64 76 15]]
1. np.tofile() 和 np.fromfile()
%time a.tofile('data/a.bin')
%time b = np.fromfile('data/a.bin', dtype=np.int64)
print(b.shape)
print(b[:2])
CPU times: user 4 ms, sys: 392 ms, total: 396 ms
Wall time: 2.06 s
CPU times: user 4 ms, sys: 156 ms, total: 160 ms
Wall time: 160 ms
(50000000,)
[90 96]
读入数据的时候要正确设置 dtype 参数
读入的数据是一维的,还需要经过 reshape 处理。
2. np.save()和np.load()
%time np.save('data/a.npy', a)
%time b = np.load('data/a.npy')
print(b.shape)
print(b[:2])
CPU times: user 0 ns, sys: 272 ms, total: 272 ms
Wall time: 269 ms
CPU times: user 0 ns, sys: 116 ms, total: 116 ms
Wall time: 116 ms
(10000, 5000)
[[90 96 38 ..., 67 40 79]
[40 12 71 ..., 64 76 15]]
- NumPy专用的二进制格式保存数据,它们会自动处理元素类型和形状等信息.
使用 np.savez() 保存多个矩阵至一个文件中:
a = np.asarray([[1,2], [3,4]])
b = np.asarray([0,1])
np.savez('data.npz', X=a, y=b)
# 导入
data = np.load('data.npz')
a = data['X']
b = data['y']
3. np.savetxt()和np.loadtxt()
%time np.savetxt('data/a.txt', a, fmt='%d', delimiter=',') # 设置以整数形式存储,以逗号隔开
%time b = np.loadtxt('data/a.txt', delimiter=',')
print(b.shape)
print(b[:2])
CPU times: user 18.4 s, sys: 60 ms, total: 18.5 s
Wall time: 18.4 s
CPU times: user 55.9 s, sys: 476 ms, total: 56.4 s
Wall time: 56.3 s
(10000, 5000)
[[ 90. 96. 38. ..., 67. 40. 79.]
[ 40. 12. 71. ..., 64. 76. 15.]]
- 读写1维和2维数组的文本文件
4.h5py.File()
import h5py
time0 = time.time()
f = h5py.File('data/a.h5','w') #创建一个h5文件,文件指针是f
f['data'] = a #将数据写入文件的主键data下面
f.close() #关闭文件
print('saving time %.2fs' % (time.time() - time0))
time cost 0.26s
time0 = time.time()
f = h5py.File('data/a.h5','r') #打开h5文件
# print f.keys() #可以查看所有的主键
b = f['data'][:] #取出主键为data的所有的键值
f.close()
print('loading time %.2fs' % (time.time() - time0))
print(b.shape)
print(b[:2])
loading time 0.15s
(10000, 5000)
[[90 96 38 ..., 67 40 79]
[40 12 71 ..., 64 76 15]]
5.pickle.dump()和pickle.load()
import pickle
with open('data/a.pkl', 'wb') as outp:
%time pickle.dump(a, outp)
with open('data/a.pkl', 'rb') as inp:
%time b = pickle.load(inp)
print(b.shape)
print(b[:2])
CPU times: user 30.1 s, sys: 5.92 s, total: 36.1 s
Wall time: 36.1 s
CPU times: user 6.84 s, sys: 10.4 s, total: 17.2 s
Wall time: 17.2 s
(10000, 5000)
[[90 96 38 ..., 67 40 79]
[40 12 71 ..., 64 76 15]]
结果统计
方法 | 文件格式 | 大小 | 写入耗时 | 读入耗时 | 需要处理类型? | 需要处理形状? |
---|---|---|---|---|---|---|
np.tofile() | .bin | 381.47MB | 2.06 s | 160 ms | 是 | 是 |
np.save() | .npy | 381.47MB | 269 ms | 116 ms | 否 | 否 |
np.savetxt() | .txt | 138.28MB | 18.4 s | 56.3 s | 是 | 否 |
h5py.File() | .h5 | 381.47MB | 260ms | 150ms | 否 | 否 |
pickle.dump() | .pkl | 1.39GB | 36.1 s | 17.2 s | 否 | 否 |
需要说明的是,第一次导入数据后在一段时间内会保存在缓存中,这时候再次导入会非常非常快。所以在训练神经网络的时候,使用 np.load() 的方式导入数据,除了第一个 epoch 速度会慢一些,后面读取数据的速度非常快。
- 总体上最优的方法是使用 np.save() 和 h5py.File(, ‘r’ or ‘w’);np.savez() 可以存储多个值, 而 h5py 提供字典的方式存储更是非常方便。
- txt 和 csv 文本文件占用的空间是最少的,但是耗时非常大
- np.tofile() 方法要处理数据的类型和形状,很烦,可以放弃了
- pickle() 的 .pkl 文件非常非常占空间,而且巨慢,直接放弃!!
非对齐的 ndarray 数据存储
import h5py
list_a = [[1,2,3], [4,5], [77]]
arr_a = np.asarray(list_a)
arr_a
array([[1, 2, 3], [4, 5], [77]], dtype=object)
# 无法保存长度不同的 ndarray
f = h5py.File('data/arr_a.h5', 'w')
f['arr_a'] = arr_a # 报错,每个元素都是 Object。
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-0a946c2e9c3c> in <module>()
1 f = h5py.File('data/arr_a.h5', 'w')
----> 2 f['arr_a'] = arr_a # 报错, 无法保存长度不同的 ndarray
...
TypeError: Object dtype dtype('O') has no native HDF5 equivalent
np.save('data/arr_a.npy', arr_a)
b = np.load('data/arr_a.npy')
print(b)
[[1, 2, 3] [4, 5] [77]]
通过上面的比较,我们可以看到 np.save() 非常强大呀,没办法,只好选择它了。