繁体   English   中英

如何可逆地存储和加载 Pandas dataframe 到磁盘/从磁盘加载

[英]How to reversibly store and load a Pandas dataframe to/from disk

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

最简单的方法是将腌制用它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 ( pytables ),它为大型数据集提供非常快的访问时间:

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

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

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


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

虽然已经有一些答案,但我找到了一个很好的比较,他们尝试了几种方法来序列化 Pandas DataFrames: Efficiently Store 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 倍(另请参阅测试代码)。

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

其他一些参考:

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

  1. 您可以使用pandas.read_csv(..., nrows=1000)仅加载 CSV 文件的一部分以仅加载表的顶部位,同时进行开发

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

  3. 将 csv 转换为HDF5 表

  4. 更新使用DataFrame.to_feather()pd.read_feather()pd.read_feather()的 R 兼容羽毛二进制格式存储数据(在我手中,在数字数据上比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

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

df.to_feather('filename.ft')

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

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

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

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

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

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

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

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。

另一个非常新鲜的to_pickle()测试。

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

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

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

pkl文件加载整个数据帧需要不到1 sec

Arctic是 Pandas、numpy 和其他数字数据的高性能数据存储。 它位于 MongoDB 之上。 对于 OP 来说可能有点矫枉过正,但值得一提的是其他在这篇文章中绊倒的人

import pickle

example_dict = {1:"6",2:"2",3:"g"}

pickle_out = open("dict.pickle","wb")
pickle.dump(example_dict, pickle_out)
pickle_out.close()

上面的代码将保存pickle文件

pickle_in = open("dict.pickle","rb")
example_dict = pickle.load(pickle_in)

这两行将打开保存的泡菜文件

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")

随着以下对我的数据帧的结果(在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 时。

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

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

我不是作者或作者的朋友,但是,当我读到这个问题时,我认为在那里值得一提。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM