有没有办法告诉熊猫在编写HDF5文件时使用特定的泡菜协议(例如4(?
这是情况(简化了很多(:
-
客户端 A 正在使用
python=3.8.1
(以及pandas=1.0.0
和pytables=3.6.1
(。A 使用df.to_hdf(file, key)
写入一些数据帧。 -
客户端 B 正在使用
python=3.7.1
(并且,碰巧的是,pandas=0.25.1
和pytables=3.5.2
- 但这无关紧要(。B 尝试使用pd.read_hdf(file, key)
读取 A 写入的数据,并失败并ValueError: unsupported pickle protocol: 5
。
请注意,这不会发生在纯数字数据帧中(例如pd.DataFrame(np.random.normal(size=(10,10)))
.所以这里有一个可重现的例子:
(base) $ conda activate py38
(py38) $ python
Python 3.8.1 (default, Jan 8 2020, 22:29:32)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas as pd
>>> df = pd.DataFrame(['hello', 'world']))
>>> df.to_hdf('foo', 'x')
>>> exit()
(py38) $ conda deactivate
(base) $ python
Python 3.7.4 (default, Aug 13 2019, 20:35:49)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas as pd
>>> df = pd.read_hdf('foo', 'x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/io/pytables.py", line 407, in read_hdf
return store.select(key, auto_close=auto_close, **kwargs)
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/io/pytables.py", line 782, in select
return it.get_result()
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/io/pytables.py", line 1639, in get_result
results = self.func(self.start, self.stop, where)
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/io/pytables.py", line 766, in func
return s.read(start=_start, stop=_stop, where=_where, columns=columns)
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/io/pytables.py", line 3206, in read
"block{idx}_values".format(idx=i), start=_start, stop=_stop
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/io/pytables.py", line 2737, in read_array
ret = node[0][start:stop]
File "/opt/anaconda3/lib/python3.7/site-packages/tables/vlarray.py", line 681, in __getitem__
return self.read(start, stop, step)[0]
File "/opt/anaconda3/lib/python3.7/site-packages/tables/vlarray.py", line 825, in read
outlistarr = [atom.fromarray(arr) for arr in listarr]
File "/opt/anaconda3/lib/python3.7/site-packages/tables/vlarray.py", line 825, in <listcomp>
outlistarr = [atom.fromarray(arr) for arr in listarr]
File "/opt/anaconda3/lib/python3.7/site-packages/tables/atom.py", line 1227, in fromarray
return six.moves.cPickle.loads(array.tostring())
ValueError: unsupported pickle protocol: 5
>>>
注意:我也尝试在python=3.7.4
中使用pandas=1.0.0
(和pytables=3.6.1
(进行阅读。这也失败了,所以我相信只是 Python 版本(3.8 编写器与 3.7 阅读器(导致了问题。这是有道理的,因为 pickle 协议 5 是作为 Python 3.8 的 PEP-574 引入的。
PyTable 默认使用最高协议,此处硬编码:https://github.com/PyTables/PyTables/blob/50dc721ab50b56e494a5657e9c8da71776e9f358/tables/atom.py#L1216
作为一种解决方法,您可以在写入 HDF 文件的客户端 A 上对pickle
模块进行猴子修补。您应该在导入pandas
之前执行此操作:
import pickle
pickle.HIGHEST_PROTOCOL = 4
import pandas
df.to_hdf(file, key)
现在HDF文件是使用泡菜协议版本4而不是版本5创建的。
更新:我错误地认为这是不可能的。事实上,基于 @PiotrJurkiewicz 出色的"猴子补丁"建议,这里有一个简单的上下文管理器,可以让我们暂时更改最高的泡菜协议。它:
- 隐藏猴子补丁,以及
- 在上下文之外没有副作用;它可以随时使用,无论泡菜以前是否进口,在熊猫之前或之后,无论。
这是代码(例如在文件pickle_prot.py
中(:
import importlib
import pickle
class PickleProtocol:
def __init__(self, level):
self.previous = pickle.HIGHEST_PROTOCOL
self.level = level
def __enter__(self):
importlib.reload(pickle)
pickle.HIGHEST_PROTOCOL = self.level
def __exit__(self, *exc):
importlib.reload(pickle)
pickle.HIGHEST_PROTOCOL = self.previous
def pickle_protocol(level):
return PickleProtocol(level)
编写器中的用法示例:
import pandas as pd
from pickle_prot import pickle_protocol
pd.DataFrame(['hello', 'world']).to_hdf('foo_0.h5', 'x')
with pickle_protocol(4):
pd.DataFrame(['hello', 'world']).to_hdf('foo_1.h5', 'x')
pd.DataFrame(['hello', 'world']).to_hdf('foo_2.h5', 'x')
并且,使用简单的测试阅读器:
import pandas as pd
from glob import glob
for filename in sorted(glob('foo_*.h5')):
try:
df = pd.read_hdf(filename, 'x')
print(f'could read {filename}')
except Exception as e:
print(f'failed on {filename}: {e}')
现在,在用 py38 编写后尝试在 py37 中读取,我们得到:
failed on foo_0.h5: unsupported pickle protocol: 5
could read foo_1.h5
failed on foo_2.h5: unsupported pickle protocol: 5
但是,使用相同的版本(37或38(进行读写,我们当然没有例外。
注意:问题 33087 仍在 Pandas 问题跟踪器上。
我(曾经(面临同样的问题...我"知道"如何解决它,我想你也这样做...... 解决方案是将整个数据重新处理为泡菜(或csv(,并在python3.7中将其重新转换为hdf5(仅知道协议4(。
流程如下所示: python3.8 -> hdf5 -> python3.8 -> csv/pickle -> python3.7 -> hdf5(与两个版本兼容(
我避免了这条路线,因为我有大量的数据帧被导出,创建了大量文件。
你真的被限制使用python3.7吗?我受到tensorflow的限制,到目前为止,它最多只支持3.7(官方(,但你可以安装tensorflow-nightly-build,它适用于python 3.8
检查您是否可以迁移到 3.8,这肯定会解决您的问题。 :)