如何可逆地将 Pandas 数据框存储到磁盘或从磁盘加载
- 2025-02-18 09:23:00
- admin 原创
- 149
问题描述:
现在,CSV
每次运行脚本时,我都会导入一个相当大的数据框。有没有一个好的解决方案可以让这个数据框在运行之间始终可用,这样我就不必花费那么多时间等待脚本运行?
解决方案 1:
最简单的方法是使用以下方法进行腌制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_pickle
和read_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 对象/文本密集型数据(请参阅这个问题)。
解决方案 2:
虽然已经有一些答案,但我发现一个很好的比较,其中他们尝试了几种方法来序列化 Pandas DataFrames:有效地存储 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 的加载时间要短得多。
其他一些参考资料:
在“读取 CSV 文件最快的 Python 库”问题中,有一个非常详细的答案,它使用基准比较了不同的库来读取 csv 文件。结果是读取 csv 文件
numpy.fromfile
最快的是哪个。另一项序列化测试
表明msgpack,ujson和 cPickle 的序列化速度最快。
解决方案 3:
如果我理解正确的话,您已经在使用了pandas.read_csv()
,但想加快开发过程,这样您就不必在每次编辑脚本时都加载文件,对吗?我有几点建议:
你可以只加载 CSV 文件的一部分
pandas.read_csv(..., nrows=1000)
,在开发过程中只加载表格的顶部部分使用ipython进行交互式会话,这样在编辑和重新加载脚本时将 pandas 表保存在内存中。
将 csv 转换为HDF5 表
更新使用
DataFrame.to_feather()
并以超快的pd.read_feather()
R 兼容羽毛pandas.to_pickle()
二进制格式存储数据(在我看来,比数字数据稍快,比字符串数据快得多)。
您可能还对 stackoverflow 上的这个答案感兴趣。
解决方案 4:
泡菜效果很好!
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
解决方案 5:
您可以使用feather格式的文件。它非常快。
df.to_feather('filename.ft')
解决方案 6:
如前所述,有多种选项和文件格式(HDF5、JSON、CSV、parquet、SQL)可用于存储数据框。但是,pickle
它并不是最佳选择(取决于您的设置),因为:
pickle
存在潜在的安全风险。参见pickle 的 Python 文档:
警告该
pickle
模块无法抵御错误或恶意构建的数据。切勿对来自不受信任或未经身份验证的来源的数据进行反解析。
pickle
很慢。在此处和此处查看基准测试。pickle
仅适用于 Python。您不能简单地使用其他工具或编程语言读取结果。
根据您的设置/使用情况,这两种限制均不适用,但我不建议将其pickle
作为 pandas 数据框的默认持久性。
解决方案 7:
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
解决方案 8:
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 打开(或反之亦然),则会出现问题。
解决方案 9:
另一项相当新鲜的测试to_pickle()
。
我总共有25 个 文件需要处理,最终大约包含200 万个项目。.csv
`dataframe`
(注意:除了加载 .csv 文件之外,我还处理一些数据并通过新列扩展数据框。)
浏览所有25 个 .csv
文件并创建数据框大约需要14 sec
。
从文件加载整个数据框pkl
需要不到1 sec
解决方案 10:
https://docs.python.org/3/library/pickle.html
pickle 协议格式:
协议版本 0 是原始的“人类可读”协议,并且向后兼容早期版本的 Python。
协议版本 1 是一种旧的二进制格式,它也与早期版本的 Python 兼容。
协议版本 2 是在 Python 2.3 中引入的。它提供了更高效的新式类 pickling。有关协议 2 带来的改进的信息,请参阅 PEP 307。
协议版本 3 是在 Python 3.0 中添加的。它明确支持字节对象,并且无法被 Python 2.x 取消封存。这是默认协议,也是需要与其他 Python 3 版本兼容时的推荐协议。
协议版本 4 是在 Python 3.4 中添加的。它增加了对非常大对象的支持、pickle 更多类型的对象以及一些数据格式优化。有关协议 4 带来的改进的信息,请参阅 PEP 3154。
解决方案 11:
Arctic是用于 Pandas、numpy 和其他数字数据的高性能数据存储。它位于 MongoDB 之上。对于 OP 来说可能有点过分,但对于偶然发现这篇文章的其他人来说,值得一提
解决方案 12:
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>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
feather 和 parquet 不适用于我的数据框。我将继续使用 pyarrow。不过,我将补充 pickle(无压缩)。写入缓存时,存储 pyarrow 和 pickle 序列化形式。从缓存读取时,如果 pyarrow 反序列化失败,则回退到 pickle。
解决方案 13:
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)
这两行将打开保存的 pickle 文件
解决方案 14:
这里有很多很好且充分的答案,但我想发布一个我在 Kaggle 上使用过的测试,其中大型 df 由不同的 pandas 兼容格式保存和读取:
https://www.kaggle.com/pedrocouto39/fast-reading-w-pickle-feather-parquet-jay
我不是这篇文章的作者或作者的朋友,但是,当我读到这个问题时,我认为值得一提。
CSV:1 分 42 秒 Pickle:4.45 秒 Feather:4.35 秒 Parquet:8.31 秒 Jay:8.12 毫秒或 0.0812 秒(速度超快!)
扫码咨询,免费领取项目管理大礼包!