我一直在尝试寻找在 Python 中访问大型数据集的最快方法。
在我的真实案例中,我有一个大约 10,000 x 10,000 的 csv 文件,我将其加载到 pandas MultiIndex DataFrame 中,因为我主要采用点积并对级别求和。
由于将此 csv 加载到 pandas 大约需要一分钟,我一直在寻找提高速度的方法。
经过调查,我遇到了这个线程:为什么在 python 中保存/加载数据比 matlab 占用更多的空间/时间?
我从这个线程得到的是 .mat 文件加载速度更快,因为它们存储为 hdf5。因此,我想通过 h5py、pandas 和 scypio(从 .mat 文件加载数据)来评估 pickle 和 hdf 的性能。
我的真实世界案例的结果如下:
pickle 65.48222637176514
h5py 65.20841789245605
pandas 65.45801973342896
mat 20.857333660125732
可以看出,加载 .mat 文件确实比通过 Python 生成的 pickle 和 hdfs 快 3 倍以上。因此,.mat 文件的更快加载时间似乎不是由于 hdf,因为加载通过 python 生成的 hdfs 没有这个优势。
基于此,我想在这里发布一个问题,询问将大型数据集加载到 python 中最快的方法是什么。为此,我用一些随机数据制作了一个玩具示例:
import numpy as np
import pickle
import h5py
import pandas as pd
#create random numpy ndarray
array_foo = np.random.rand(10000,10000)
#save array to pickle
pickle.dump(array_foo, open('array_foo.pkl', 'wb'))
#save array to hdf through h5py
h5py_hdf_store = h5py.File('array_foo.h5')
h5py_hdf_store['array_foo'] = array_foo
h5py_hdf_store.close()
#save pandas to hdf
df = pd.DataFrame(array_foo)
df.to_hdf('df_foo.h5', 'df_foo')
#save to csv for conversion to mat
df.to_csv('df_foo.csv')
在 MATLAB 中将 csv 转换为 mat 后,我进行了以下测试来评估加载时间:
import pickle
import h5py
import pandas as pd
import scipy.io as sio
import time
#time pickle load
start_time = time.time()
pkl_array_foo = pickle.load(open('array_foo.pkl', 'rb'))
end_time = time.time()
delta_time = end_time - start_time
print('pickle', delta_time)
#time h5py load
start_time = time.time()
h5py_hdf_store = h5py.File('array_foo.h5')
h5py_array_foo = h5py_hdf_store['array_foo'][:,:]
end_time = time.time()
delta_time = end_time - start_time
print('h5py', delta_time)
#time pandas load
start_time = time.time()
df_array_foo = pd.read_hdf('df_foo.h5')
end_time = time.time()
delta_time = end_time - start_time
print('pandas', delta_time)
#time mat load
start_time = time.time()
dict_df_foo = sio.loadmat('mat_df_foo.mat')
mat_array_foo = dict_df_foo['mat_df_foo']
end_time = time.time()
delta_time = end_time - start_time
print('mat', delta_time)
结果如下:
pickle 68.21923732757568
h5py 67.92283535003662
pandas 67.95403552055359
mat 67.09603023529053
有趣的是,.mat 文件似乎在这里失去了加载优势。经过调查,事实证明现实世界的数据非常稀疏。为了弄清楚有多稀疏,我将所有非零值替换为 1,将所有值相加并除以矩阵的大小。这产生了约0.28的密度。使用这个数字,我将玩具示例中的随机矩阵替换为稀疏矩阵:
array_foo_sparse = sparse.random(10000,10000, density = 0.28)
array_foo = array_foo_sparse.todense()
确实,这似乎起到了作用:
pickle 69.06890630722046
h5py 68.73687291145325
pandas 69.12291169166565
mat 22.53125286102295
因此,我想研究保存为稀疏是否可以加快 pickle、pandas 和 h5py 的加载时间。为此,我将稀疏版本直接保存到pickle,并保存了DataFrame的稀疏版本:
pickle.dump(array_foo_sparse, open('array_foo.pkl', 'wb'))
df_sparse = df.to_sparse()
df_sparse.to_hdf('df_foo.h5', 'df_foo')
尝试通过 h5py 保存时出现错误:
TypeError: Object dtype dtype('O') has no native HDF5 equivalent
经过一番调查,似乎我应该尝试使用分块存储,但我觉得通过进一步调查,我正在远离最初的目标。
结果如下:
pickle 38.300209283828735
pandas 470.5342836380005
虽然 pickle 似乎确实加快了速度(但仍然没有 .mat 快),但 pandas 受到了巨大的打击,加载需要将近 8 分钟。
最后我尝试直接从python保存到mat:
array_foo_sparse = sparse.random(10000,10000, density = 0.28)
array_foo = array_foo_sparse.todense()
sio.savemat('array_foo.mat', {'array_foo':array_foo})
加载这个产生:
mat 73.23888158798218
并保存稀疏版本:
array_foo_sparse = sparse.random(10000,10000, density = 0.28)
sio.savemat('array_foo.mat', {'array_foo':array_foo_sparse})
产量
mat 29.749581336975098
虽然速度更快,但它仍然比 MATLAB 的 mat 文件慢了将近 10 秒。
因此,我想知道从这里去哪里。有什么方法可以达到(或超越)MATLAB mat 文件的性能?如果可能的话,我想留在同一个环境中(即python、spyder)。