Можно ли указать протокол рассола при записи pandas в HDF5? - PullRequest
3 голосов
/ 05 февраля 2020

Есть ли способ указать Pandas использовать определенный c протокол рассола (например, 4) при записи файла HDF5 ?

Вот ситуация (очень упрощенная) ):

  • Клиент A использует python=3.8.1 (а также pandas=1.0.0 и pytables=3.6.1). A записывает некоторый DataFrame, используя df.to_hdf(file, key).

  • Клиент B использует python=3.7.1 (и, как оказалось, pandas=0.25.1 и pytables=3.5.2 - но это не имеет значения). B пытается прочитать данные, записанные A, используя pd.read_hdf(file, key), и завершается неудачно с 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
>>>

Примечание: я пытался также читать, используя pandas=1.0.0pytables=3.6.1) в python=3.7.4. Это тоже не получается, поэтому я считаю, что это просто Python версия (писатель 3.8 против читателя 3.7), которая вызывает проблему. Это имеет смысл, поскольку протокол 5 рассола был представлен как PEP-574 для Python 3.8.

Ответы [ 3 ]

2 голосов
/ 28 марта 2020

Обновление : Я ошибался, полагая, что это невозможно. На самом деле, основываясь на превосходном предложении @PiotrJurkiewicz «monkey-patch», вот простой контекстный менеджер, который позволяет нам временно изменять протокол наивысшего пикаля. Он:

  1. Скрывает исправления обезьян, а
  2. не имеет побочных эффектов вне контекста; его можно использовать в любое время, независимо от того, был ли ранее импортирован рассол, до или после pandas, независимо от того,

Вот код (например, в файле 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}')

Теперь попробуйте прочитать в py37 после написания в py38 мы получаем:

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 система отслеживания проблем.

1 голос
/ 29 марта 2020

PyTable использует самый высокий протокол по умолчанию, который здесь жестко задан: https://github.com/PyTables/PyTables/blob/50dc721ab50b56e494a5657e9c8da71776e9f358/tables/atom.py#L1216

В качестве обходного пути вы можете обезопасить модуль pickle на клиенте A, который может обезьяны. пишет файл HDF. Вы должны сделать это перед импортом pandas:

import pickle
pickle.HIGHEST_PROTOCOL = 4
import pandas

df.to_hdf(file, key)

Теперь файл HDF создан с использованием протокола 4 протокола рассола вместо версии 5.

0 голосов
/ 15 марта 2020

Я (был) столкнулся с той же проблемой ... Я "знаю", как ее решить, и я думаю, что вы тоже ... Решение состоит в том, чтобы обработать все данные в рассол (или CSV) и повторно преобразуйте его в python3 .7 в hdf5 (который знает только протокол 4).

поток выглядит примерно так: python3 .8 -> hdf5 -> python3 .8 -> csv / pickle -> python3 .7 -> hdf5 (совместимо с обеими версиями)

Я избежал этого маршрута, потому что у меня есть фрагменты данных экспортируемого фрейма данных, создавая большое количество файлов.

Вы действительно ограничены в использовании python3 .7? Я был ограничен тензорным потоком, который на данный момент поддерживает только до 3.7 (официально), но вы можете установить тензорный поток - ночная сборка, и он работает с python 3.8

Проверьте, можете ли вы перейти на 3.8, что наверняка решит вашу проблему. :)

...