如何可逆地存储和加载一个熊猫数据帧到/从磁盘

现在每次运行脚本时,我都会导入一个相当大的CSV作为数据框架。是否有一个好的解决方案来保持数据帧在运行之间不断可用,这样我就不必花费所有的时间等待脚本运行?

459957 次浏览

最简单的方法是使用to_pickle 泡菜:

df.to_pickle(file_name)  # where to save it, usually as a .pkl

然后你可以使用以下命令将其加载回来:

df = pd.read_pickle(file_name)

注意:在0.11.1之前,saveload是做到这一点的唯一方法(现在已弃用,分别支持to_pickleread_pickle)。


另一个流行的选择是使用HDF5 (巴拉圭报表),它为大型数据集提供非常快访问时间:

import pandas as pd
store = pd.HDFStore('store.h5')


store['df'] = df  # save it
store['df']  # load it

更高级的策略在msgpack,它可能更适合互操作性,作为JSON的更快替代品,或者如果你有python对象/文本较多的数据(参见这个问题)。

如果我理解正确的话,你已经在使用pandas.read_csv(),但想要加快开发过程,这样你就不必每次编辑脚本时都加载文件,对吗?我有一些建议:

  1. 当你在做开发时,你可以使用pandas.read_csv(..., nrows=1000)只加载CSV文件的一部分,只加载表的顶部位

  2. 在交互式会话中使用ipython,这样在编辑和重新加载脚本时,就可以将pandas表保存在内存中。

  3. 将csv转换为HDF5表

  4. updated使用DataFrame.to_feather()pd.read_feather()存储r兼容的数据feather二进制格式,超快(在我的手上,在数值数据上比pandas.to_pickle()略快,在字符串数据上快得多)。

你可能也会对stackoverflow上的这个答案感兴趣。

泡菜很好!

import pandas as pd
df.to_pickle('123.pkl')    #to save the dataframe, df to 123.pkl
df1 = pd.read_pickle('123.pkl') #to load 123.pkl back to the dataframe df

虽然已经有一些答案,但我发现了一个很好的比较,其中他们尝试了几种方法来序列化Pandas dataframe: 有效地存储Pandas数据框架

他们比较:

  • pickle:原始ASCII数据格式
  • cPickle,一个C库
  • Pickle-p2:使用更新的二进制格式
  • Json: standardlib Json库
  • json-no-index:类似json,但没有索引
  • msgpack:二进制JSON替代品
  • CSV
  • hdfstore: HDF5存储格式

在他们的实验中,他们序列化了一个包含1,000,000行的DataFrame,并分别测试了两列:一列是文本数据,另一列是数字。他们的免责声明说:

您不应该相信以下内容适用于您的数据。您应该查看自己的数据并自己运行基准测试

它们所引用的测试的源代码是可用的在线。由于这段代码不能直接工作,我做了一些小改动,你可以在这里得到:序列化.py 我得到了以下结果:

time comparison results

他们还提到,通过将文本数据转换为分类数据,序列化要快得多。在他们的测试中,大约是10倍的速度(另见测试代码)。

编辑: pickle的时间比CSV的时间长可以用所使用的数据格式来解释。默认情况下,pickle使用可打印的ASCII表示,这会生成更大的数据集。然而,从图中可以看出,使用更新的二进制数据格式(版本2,pickle-p2)的pickle加载时间要低得多。

其他参考资料:

Pandas DataFrame有to_pickle函数,用于保存DataFrame:

import pandas as pd


a = pd.DataFrame({'A':[0,1,0,1,0],'B':[True, True, False, False, False]})
print a
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False


a.to_pickle('my_file.pkl')


b = pd.read_pickle('my_file.pkl')
print b
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False

Numpy文件格式对于数字数据来说非常快

我更喜欢使用numpy文件,因为它们快速且易于使用。 下面是一个简单的基准测试,用于保存和加载一个包含100万个点的列的数据框架
import numpy as np
import pandas as pd


num_dict = {'voltage': np.random.rand(1000000)}
num_df = pd.DataFrame(num_dict)

使用ipython的%%timeit魔法函数

%%timeit
with open('num.npy', 'wb') as np_file:
np.save(np_file, num_df)

输出为

100 loops, best of 3: 5.97 ms per loop

将数据加载回数据框架

%%timeit
with open('num.npy', 'rb') as np_file:
data = np.load(np_file)


data_df = pd.DataFrame(data)

输出为

100 loops, best of 3: 5.12 ms per loop

不坏!

缺点

如果您使用python 2保存numpy文件,然后尝试使用python 3打开,则会出现问题(反之亦然)。

您可以使用羽毛格式的文件。它非常快。

df.to_feather('filename.ft')

如前所述,有不同的选项和文件格式(HDF5JSONCSV拼花SQL)来存储数据帧。然而,pickle不是一级公民(取决于你的设置),因为:

  1. pickle是一个潜在的安全风险。形成pickle的Python文档:

警告 pickle模块不安全 恶意构造的数据。对象接收的数据永远不能解pickle

.

.
  1. pickle很慢。找到在这里在这里基准测试。

根据您的设置/使用情况,这两个限制都不适用,但我不建议将pickle作为pandas数据帧的默认持久性。

https://docs.python.org/3/library/pickle.html

pickle协议格式如下:

协议版本0是原始的“人类可读”协议,并向后兼容Python的早期版本。

协议版本1是一种旧的二进制格式,它也与早期版本的Python兼容。

协议版本2是在Python 2.3中引入的。它提供了更有效的新样式类的pickle。有关协议2带来的改进,请参阅PEP 307。

协议版本3是在Python 3.0中添加的。它显式支持bytes对象,不能被Python 2.x解封。这是默认协议,也是在需要与其他Python 3版本兼容时的推荐协议。

协议版本4是在Python 3.4中添加的。它增加了对非常大的对象、pickle更多类型的对象以及一些数据格式优化的支持。有关协议4带来的改进的信息,请参阅PEP 3154。

Pyarrow跨版本兼容性

总的来说,pyarrow/feather(来自pandas/msgpack的弃用警告)。然而,我在使用pyarrow的暂态规格时遇到了一个挑战,使用pyarrow 0.15.1序列化的数据不能使用0.16.0 箭- 7961反序列化。我使用序列化使用redis,所以必须使用二进制编码。

我重新测试了各种选择(使用jupyter笔记本电脑)

import sys, pickle, zlib, warnings, io
class foocls:
def pyarrow(out): return pa.serialize(out).to_buffer().to_pybytes()
def msgpack(out): return out.to_msgpack()
def pickle(out): return pickle.dumps(out)
def feather(out): return out.to_feather(io.BytesIO())
def parquet(out): return out.to_parquet(io.BytesIO())


warnings.filterwarnings("ignore")
for c in foocls.__dict__.values():
sbreak = True
try:
c(out)
print(c.__name__, "before serialization", sys.getsizeof(out))
print(c.__name__, sys.getsizeof(c(out)))
%timeit -n 50 c(out)
print(c.__name__, "zlib", sys.getsizeof(zlib.compress(c(out))))
%timeit -n 50 zlib.compress(c(out))
except TypeError as e:
if "not callable" in str(e): sbreak = False
else: raise
except (ValueError) as e: print(c.__name__, "ERROR", e)
finally:
if sbreak: print("=+=" * 30)
warnings.filterwarnings("default")

我的数据帧具有以下结果(在out jupyter变量中)

pyarrow before serialization 533366
pyarrow 120805
1.03 ms ± 43.9 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pyarrow zlib 20517
2.78 ms ± 81.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
msgpack before serialization 533366
msgpack 109039
1.74 ms ± 72.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
msgpack zlib 16639
3.05 ms ± 71.7 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
pickle before serialization 533366
pickle 142121
733 µs ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pickle zlib 29477
3.81 ms ± 60.4 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
feather ERROR feather does not support serializing a non-default index for the index; you can .reset_index() to make the index into column(s)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
parquet ERROR Nested column branch had multiple children: struct<x: double, y: double>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

羽毛和拼花不适合我的数据框架。我将继续使用pyarrow。但是我会补充腌黄瓜(没有压缩)。写入缓存时,存储pyarrow和pickle序列化表单。如果pyarrow反序列化失败,则从缓存读取回退到pickle时。

另一个相当新鲜的to_pickle()测试。

我总共有25 .csv文件要处理,最终的dataframe大致由2米项组成。

(注意:除了加载.csv文件,我还操作了一些数据,并通过新列扩展数据帧。)

遍历所有25 __abc0文件并创建大约14 sec的数据帧。

pkl文件加载整个数据帧所需的时间小于1 sec

北极是Pandas, numpy和其他数值数据的高性能数据存储。它位于MongoDB之上。也许对于OP来说有点过分了,但对于其他无意中看到这篇文章的人来说,值得一提

这里有很多很棒和充分的答案,但我想发布一个我在Kaggle上使用的测试,这个测试用不同的pandas兼容格式保存和读取大df:

https://www.kaggle.com/pedrocouto39/fast-reading-w-pickle-feather-parquet-jay

我不是作者,也不是作者的朋友,然而,当我读到这个问题时,我觉得值得一提。

CSV: 1分钟42秒泡菜:4.45秒羽毛:4.35秒拼花:8.31秒杰伦:8.12毫秒 或者0.0812s(非常快!)