406

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

4

13 回答 13

603

最简单的方法是使用以下方法腌制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


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

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

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

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


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

于 2013-06-13T23:13:34.110 回答
122

虽然已经有了一些答案,但我发现了一个很好的比较,他们尝试了几种序列化 Pandas DataFrames 的方法:Efficiently Store Pandas DataFrames

他们比较:

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

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

您不应该相信以下内容可以概括为您的数据。您应该查看自己的数据并自己运行基准测试

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

时间比较结果

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

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

其他一些参考资料:

于 2015-11-06T15:24:10.203 回答
39

如果我理解正确,您已经在使用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 上的这个答案感兴趣。

于 2013-06-13T23:28:07.657 回答
29

泡菜效果很好!

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
于 2015-10-10T04:52:42.097 回答
11

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

df.to_feather('filename.ft')
于 2017-10-17T06:28:51.640 回答
6

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

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

警告pickle模块对错误或恶意构建的数据不安全。永远不要取消从不受信任或未经身份验证的来源收到的数据。

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

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

于 2019-04-11T06:31:32.317 回答
5

Pandas DataFrames 具有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
于 2015-11-12T21:46:29.957 回答
2

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

我更喜欢使用 numpy 文件,因为它们快速且易于使用。这是一个简单的基准,用于保存和加载具有 1 列 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 打开(反之亦然),则会出现问题。

于 2017-10-13T18:25:24.230 回答
2

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

我总共有25 个 .csv文件要处理,最终dataframe包含大约200万个项目。

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

浏览所有25 个 .csv 文件并创建数据框大约需要14 sec.

从文件加载整个数据帧pkl需要少于1 sec

于 2020-08-13T07:52:52.787 回答
1

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

pickle 协议格式:

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

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

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

Python 3.0 中添加了协议版本 3。它对字节对象有明确的支持,并且不能被 Python 2.x 解压。这是默认协议,当需要与其他 Python 3 版本兼容时推荐使用的协议。

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

于 2019-04-12T06:18:43.193 回答
1

北极是 Pandas、numpy 和其他数字数据的高性能数据存储。它位于 MongoDB 之上。也许对于 OP 来说有点矫枉过正,但对于其他偶然发现这篇文章的人来说,值得一提

于 2020-09-26T09:47:13.150 回答
0

跨版本的 pyarrow 兼容性

整体转移到 pyarrow/feather(来自 pandas/msgpack 的弃用警告)。但是,我对 pyarrow的规范有一个挑战 用 pyarrow 0.15.1序列化的数据不能用 0.16.0 ARROW-7961反序列化。我正在使用序列化来使用redis,所以必须使用二进制编码。

我重新测试了各种选项(使用 jupyter notebook)

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 序列化表单时。如果 pyarrow 反序列化失败,则从缓存回退读取到 pickle 时。

于 2020-02-29T14:03:03.017 回答
-1

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

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

但是,我不是此书的作者或作者的朋友,当我阅读此问题时,我认为值得一提。

CSV:1 分钟 42 秒泡菜:4.45 秒羽毛:4.35 秒镶木地板:8.31 秒杰伊:8.12 毫秒或 0.0812 秒(超快!)

于 2021-03-11T10:34:29.003 回答