如何使用熊猫存储数据框


317

现在,CSV每次运行脚本时,我都会导入一个相当大的数据框。是否有一个很好的解决方案,可以使数据帧在两次运行之间保持持续可用,因此我不必花费所有时间等待脚本运行?


2
是的,这是我使用Python的主要抱怨之一-没有简单的方法来保存和检索数据帧。在这方面,R和SAS更加用户友好。
RobertF

Answers:


481

最简单的方法是使用以下方法将其腌制to_pickle

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

然后您可以使用以下方法将其加载回:

df = pd.read_pickle(file_name)

注意:在0.11.1 save和之前,load这样做是唯一的方法(现在已弃用它们,to_pickleread_pickle分别赞成和)。


另一个流行的选择是使用HDF5pytables),它为大型数据集提供了非常快速的访问时间:

store = HDFStore('store.h5')

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

食谱中讨论了更高级的策略。


从0.13开始,还有msgpack,它可能对于互操作性更好,作为JSON的更快替代品,或者如果您有python对象/文本繁重的数据(请参阅此问题)。


8
@geekazoid保存不建议使用to_pickle(它会创建一个pickle而不是csv,这是一个更快/不同的对象)。
安迪·海登

9
@geekazoid如果加载后需要转换数据(例如,将字符串/对象转换为datetime64),则在加载保存的csv之后需要再次执行此操作,从而导致性能损失。pickle将数据框保存为当前状态,从而保留了数据及其格式。这会导致大量的性能提升。
哈本2015年

4
pickle和HDFStore都不能保存大于8GB的数据帧。有其他选择吗?
user1700890

1
@ user1700890尝试从随机数据(文本和数组)生成并发布新问题。我认为这可能是对的/怀疑我们遗漏了一些东西。新问题将引起更多关注,但请尝试包含/生成一个可复制的DataFrame :)
Andy Hayden


100

尽管已经有了一些答案,但是我发现它们之间进行了很好的比较,他们尝试了几种方法来序列化Pandas DataFrame:有效存储Pandas DataFrames

他们比较:

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

在他们的实验中,他们使用分别测试的两列来序列化1,000,000行的DataFrame:一个带有文本数据,另一个带有数字。他们的免责声明说:

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

他们所参考的测试源代码可在线获得。由于此代码无法直接运行,因此我做了一些小的更改,您可以在此处进行更改:serialize.py, 我得到以下结果:

时间比较结果

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

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

其他一些参考:


1
我更新了答案以解释您的问题。总结一下:默认情况下,pickle以ASCII格式存储数据。
agold 2015年

1
啊,谢谢那个解释!注意,pandas DataFrame .to_pickle似乎正在使用pkl.HIGHEST_PROTOCOL(应为2)
ntg

2
似乎上面链接的博客(高效存储熊猫数据帧已被删除。我.to_pickle().to_hdf()(未压缩)使用(使用二进制存储)进行了自己的比较。目标是速度,HDF的文件大小为11倍的Pickle,并且加载时间是5倍泡椒我的数据是的〜5K文件〜7K行×6周的cols每个,主要是数字。
hamx0r

1
该页面仍然存在,您只需要删除斜杠即可:高效存储Pandas DataFrames
IanSR

2
@Mike Williamson,在我的测试中,pickle的加载速度比HDF快5倍,并且还占用了磁盘空间的1/11(即hdf在磁盘上大了11倍,从磁盘加载的时间是pickle的5倍)。这一切都在python 3和pandas 0.22.0上完成。
hamx0r

35

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

  1. pandas.read_csv(..., nrows=1000)在进行开发时,您只能加载CSV文件的一部分,而仅用于加载表的最高位

  2. 使用ipython进行交互式会话,以便在编辑和重新加载脚本时将pandas表保留在内存中。

  3. 将csv转换为HDF5表

  4. 更新了用法,DataFrame.to_feather()pd.read_feather()以R兼容的羽毛二进制格式存储了数据,该格式超级快(在我手中,比pandas.to_pickle()数字数据要快一些,而字符串数据要快得多)。

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


您知道为什么to_feather在字符串数据上能很好地工作吗?我进行了基准测试to_pickleto_feature在我的数字数据框和泡菜上快了约3倍。
zyxue

@zyxue很好的问题,老实说,我并没有在羽毛材料上玩太多,所以我没有答案
Noah



4

熊猫数据框具有to_pickle对保存数据框有用的功能:

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

4

如前所述,有不同的选项和文件格式(HDF5JSONCSVparquetSQL)来存储数据帧。但是,pickle不是一流的公民(取决于您的设置),因为:

  1. pickle是潜在的安全风险。形成picklePython文档

警告pickle模块对于错误或恶意构建的数据并不安全。切勿挑剔从不可信或未经身份验证的来源收到的数据。

  1. pickle是慢的。在此处此处找到基准。

根据您的设置/用法,两个限制均不适用,但我不建议您pickle将其作为熊猫数据框的默认持久性。


1

数字数据的文件格式非常快

我更喜欢使用numpy文件,因为它们快速且易于使用。这是一个简单的基准,用于保存和加载具有1百万点的1列的数据框。

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打开(反之亦然),则会出现问题。


6
请注意,此解决方案将删除所有列名,并将所有整数数据更改为float :(
Joseph Garvin

0

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

泡菜协议格式:

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

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

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

协议版本3是在Python 3.0中添加的。它具有对字节对象的显式支持,并且不能被Python 2.x取消选择。这是默认协议,当需要与其他Python 3版本兼容时,建议使用该协议。

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


0

pyarrow跨版本的兼容性

总体上已经转向了pyarrow / feather(来自pandas / msgpack的弃用警告)。但是,我对规范中具有瞬时特性的 pyarrow提出了挑战,使用pyarrow 0.15.1序列化的数据无法使用0.16.0 ARROW-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")

对于我的数据框具有以下结果(在outjupyter变量中)

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序列化表格。如果从pypy反序列化失败,则从缓存回退到泡菜。


这没有回答问题
Jason S

0

格式取决于您的用例

  • 在笔记本会话之间保存DataFrame- 羽毛,如果您习惯于腌制 -也可以。
  • 保存数据帧在尽可能小的文件大小- 镶木地板pickle.gz(检查什么最好为您的数据)
  • 保存一个非常大的DataFrame(10+百万行)-HDF
  • 能够读取另一个平台上(而不是Python),不支持其他格式的数据- CSVcsv.gz,检查是否镶木支持
  • 能够用眼睛查看/使用Excel / Google表格/ Git diff- CSV
  • 保存占用几乎所有RAM的DataFrame- CSV

该视频中有熊猫文件格式的比较。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.