【发布时间】:2013-06-10 12:32:23
【问题描述】:
现在我每次运行脚本时都会导入一个相当大的CSV 作为数据框。是否有一个好的解决方案可以在两次运行之间保持该数据帧始终可用,这样我就不必花费所有时间等待脚本运行?
【问题讨论】:
-
是的,这是我对使用 Python 的主要抱怨之一 - 没有简单的方法来保存和检索数据帧。在这方面,R 和 SAS 对用户更加友好。
现在我每次运行脚本时都会导入一个相当大的CSV 作为数据框。是否有一个好的解决方案可以在两次运行之间保持该数据帧始终可用,这样我就不必花费所有时间等待脚本运行?
【问题讨论】:
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)。 p>
另一个流行的选择是使用HDF5 (pytables),它为大型数据集提供very fast 访问时间:
import pandas as pd
store = pd.HDFStore('store.h5')
store['df'] = df # save it
store['df'] # load it
cookbook 中讨论了更多高级策略。
从 0.13 开始,还有 msgpack 可能更好地实现互操作性,作为 JSON 的更快替代方案,或者如果您有 python 对象/文本大量数据(请参阅 this question)。
【讨论】:
HDFStore 方法已重命名为 to_hdf。
虽然已经有一些答案,但我发现了一个很好的比较,他们尝试了几种序列化 Pandas DataFrames 的方法:Efficiently Store Pandas DataFrames。
他们比较:
在他们的实验中,他们序列化了一个包含 1,000,000 行的 DataFrame,其中两列分别测试:一列是文本数据,另一列是数字。他们的免责声明说:
您不应相信以下内容可以概括为您的数据。您应该查看自己的数据并自己运行基准测试
他们引用的测试的源代码是可用的online。由于这段代码不能直接工作,我做了一些小的改动,你可以在这里得到:serialize.py 我得到了以下结果:
他们还提到,通过将文本数据转换为categorical 数据,序列化速度要快得多。在他们的测试中大约快 10 倍(另见测试代码)。
编辑:pickle 比 CSV 更高的时间可以通过使用的数据格式来解释。默认情况下,pickle 使用可打印的 ASCII 表示,它会生成更大的数据集。然而,从图中可以看出,使用较新的二进制数据格式(版本 2,pickle-p2)的 pickle 加载时间要短得多。
其他一些参考资料:
numpy.fromfile是最快的。【讨论】:
.to_pickle()(使用二进制存储)和.to_hdf()(不压缩)。目标是速度,HDF 的文件大小为 11x Pickle,加载时间为 5x Pickle。我的数据是约 5k 个文件,每行约 7k 行 x 6 列,大部分是数字。
如果我理解正确,您已经在使用pandas.read_csv(),但希望加快开发过程,这样您就不必在每次编辑脚本时都加载文件,对吗?我有一些建议:
在进行开发时,您可以使用 pandas.read_csv(..., nrows=1000) 仅加载 CSV 文件的一部分以仅加载表的最高位
使用ipython 进行交互式会话,以便在编辑和重新加载脚本时将 pandas 表保存在内存中。
将 csv 转换为 HDF5 table
更新使用DataFrame.to_feather()和pd.read_feather()以超快的R兼容feather二进制格式存储数据(在我手中,比pandas.to_pickle()稍快在数字数据上,在字符串数据上更快)。
您可能还对 stackoverflow 上的 this answer 感兴趣。
【讨论】:
to_feather 可以很好地处理字符串数据吗?我在我的数字数据帧上对 to_pickle 和 to_feature 进行了基准测试,pickle 的速度大约快了 3 倍。
泡菜效果很好!
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
【讨论】:
.pkl。
您可以使用羽毛格式的文件。它非常快。
df.to_feather('filename.ft')
【讨论】:
R 可以使用feather 库直接使用数据。
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 文件,因为它们快速且易于使用。 这是保存和加载具有 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 打开(反之亦然),则会出现问题。
【讨论】:
to_pickle() 的另一个非常新鲜的测试。
我总共有 25 个 .csv 文件要处理,最终的 dataframe 包含大约 2M 个项目。
(注意:除了加载 .csv 文件外,我还操作了一些数据并通过新列扩展数据框。)
遍历所有 25 .csv files 并创建数据框大约需要 14 sec。
从pkl 文件加载整个数据帧所需的时间少于1 sec
【讨论】:
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。
【讨论】:
Arctic 是用于 Pandas、numpy 和其他数字数据的高性能数据存储。它位于 MongoDB 之上。也许对于 OP 来说有点矫枉过正,但对于其他偶然发现这篇文章的人来说,值得一提
【讨论】:
整体迁移到 pyarrow/feather(来自 pandas/msgpack 的弃用警告)。但是我对 pyarrow 有一个挑战,transient in specification 用 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:1min 42s Pickle:4.45s Feather:4.35s Parquet:8.31s Jay:8.12ms 或 0.0812 秒(超快!)
【讨论】: