OverflowError при сохранении больших Pandas df в hdf

У меня есть большой кадр данных Pandas (~ 15 ГБ, 83 млн строк), который я хочу сохранить как файл h5 (или feather). Один столбец содержит длинные строки идентификаторов чисел, которые должны иметь тип строки/объекта. Но даже когда я гарантирую, что pandas анализирует все столбцы как object:

df = pd.read_csv('data.csv', dtype=object)
print(df.dtypes)  # sanity check
df.to_hdf('df.h5', 'df')

> client_id                object
  event_id                 object
  account_id               object
  session_id               object
  event_timestamp          object
  # etc...

Я получаю эту ошибку:

  File "foo.py", line 14, in <module>
    df.to_hdf('df.h5', 'df')
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/core/generic.py", line 1996, in to_hdf
    return pytables.to_hdf(path_or_buf, key, self, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 279, in to_hdf
    f(store)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 273, in <lambda>
    f = lambda store: store.put(key, value, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 890, in put
    self._write_to_group(key, value, append=append, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 1367, in _write_to_group
    s.write(obj=value, append=append, complib=complib, **kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 2963, in write
    self.write_array('block%d_values' % i, blk.values, items=blk_items)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/pytables.py", line 2730, in write_array
    vlarr.append(value)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/tables/vlarray.py", line 547, in append
    self._append(nparr, nobjects)
  File "tables/hdf5extension.pyx", line 2032, in tables.hdf5extension.VLArray._append
OverflowError: value too large to convert to int

По-видимому, он все равно пытается преобразовать это в int и терпит неудачу.

При запуске df.to_feather() у меня похожая проблема:

df.to_feather('df.feather')
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/core/frame.py", line 1892, in to_feather
    to_feather(self, fname)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pandas/io/feather_format.py", line 83, in to_feather
    feather.write_dataframe(df, path)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/feather.py", line 182, in write_feather
    writer.write(df)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/feather.py", line 93, in write
    table = Table.from_pandas(df, preserve_index=False)
  File "pyarrow/table.pxi", line 1174, in pyarrow.lib.Table.from_pandas
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/pandas_compat.py", line 501, in dataframe_to_arrays
    convert_fields))
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 586, in result_iterator
    yield fs.pop().result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
    return self.__get_result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/pandas_compat.py", line 487, in convert_column
    raise e
  File "/shared_directory/projects/env/lib/python3.6/site-packages/pyarrow/pandas_compat.py", line 481, in convert_column
    result = pa.array(col, type=type_, from_pandas=True, safe=safe)
  File "pyarrow/array.pxi", line 191, in pyarrow.lib.array
  File "pyarrow/array.pxi", line 78, in pyarrow.lib._ndarray_to_array
  File "pyarrow/error.pxi", line 85, in pyarrow.lib.check_status
pyarrow.lib.ArrowInvalid: ('Could not convert 1542852887489 with type str: tried to convert to double', 'Conversion failed for column session_id with type object')

So:

  1. Что-нибудь похожее на число принудительно преобразуется в число в памяти?
  2. Может ли присутствие NaN повлиять на то, что здесь происходит?
  3. Есть ли альтернативное решение для хранения? Что было бы лучше всего?

person Josh Friedlander    schedule 17.07.2019    source источник
comment
hdf5, вероятно, не подходит для этой проблемы. Что вы собираетесь делать с данными позже?   -  person Alex Fish    schedule 24.07.2019
comment
Я хотел бы немного поработать над фреймворком данных, сохранить его, а затем попросить другой скрипт подобрать его и продолжить работу над ним. Мне не нужно ни экспортировать его на другую платформу, ни долговременно хранить. Просто самое быстрое время ввода-вывода, которое я могу получить.   -  person Josh Friedlander    schedule 24.07.2019
comment
@JoshFriedlander вы рассматривали возможность использования JSON?   -  person skhan    schedule 24.07.2019
comment
У меня есть (см. мой ответ ниже) - это неплохая альтернатива, но у нее есть недостатки (т.е. она не сохраняет тип datetime). Я хотел бы услышать, какой должна быть лучшая практика.   -  person Josh Friedlander    schedule 24.07.2019
comment
Я думаю, что я бы использовал паркет в этом случае. hdf5 — лучшее решение, если у вас есть много фреймов данных, которые вы хотите хранить в одной структуре. Он имеет больше накладных расходов при открытии файла, а затем позволяет эффективно загружать каждый кадр данных, а также легко загружать срезы. паркет больше похож на сжатый CSV.   -  person Alex Fish    schedule 25.07.2019
comment
Спасибо @Alex, я этого не понял. Я рассматривал возможность использования feather, что теоретически равно parquet, но лучше, ">как говорит здесь Уэс. Если бы вы могли расширить это до ответа и добавить что-то в столбцы смешанного типа, я бы с радостью дал вам награду!   -  person Josh Friedlander    schedule 25.07.2019


Ответы (2)


Прочитав немного по этой теме, кажется, что проблема связана со столбцами типа string. Мои столбцы string содержат смесь строк, состоящих только из чисел, и строк с символами. Pandas имеет гибкую возможность хранить строки как object без объявленного типа, но при сериализации в hdf5 или feather содержимое столбца преобразуется в тип (скажем, str или double) и не может быть смешано. Обе эти библиотеки терпят неудачу при столкновении с достаточно большой библиотекой смешанного типа.

Принудительное преобразование моего смешанного столбца в строки позволило мне сохранить его в перьях, но в HDF5 файл раздувался, и процесс завершился, когда у меня закончилось место на диске.

Здесь приведен ответ в сопоставимом случае, когда комментатор отмечает (2 года назад): "Эта проблема очень стандартна, но решения их мало».

Немного фона:

Типы строк в Pandas называются object, но это не позволяет понять, что они могут быть либо чистыми строками, либо смешанными типами dtype (у numpy есть встроенные строковые типы, но Pandas никогда не использует их для текста). Итак, первое, что нужно сделать в подобном случае, это принудительно применить все строковые столбцы как строковый тип (с df[col].astype(str)). Но даже так, в достаточно большом файле (16гб, с длинными строками) это все равно не удалось. Почему?

Причина, по которой я столкнулся с этой ошибкой, заключалась в том, что у меня были данные, содержащие длинные и высокие энтропии (много разных уникальных значений) строки. (Для данных с низкой энтропией, возможно, было бы целесообразно переключиться на categorical dtype.) В моем случае я понял, что мне нужны эти строки только для идентификации строк, поэтому я мог заменить их уникальными целыми числами!

df[col] = df[col].map(dict(zip(df[col].unique(), range(df[col].nunique()))))

Другие решения:

Для текстовых данных существуют другие рекомендуемые решения, кроме hdf5/feather, в том числе:

  • json
  • msgpack (обратите внимание, что в Pandas 0.25 read_msgpack устарело)
  • pickle (у которого были проблемы с безопасностью, поэтому будьте осторожны, но он должен быть в порядке для внутреннего хранения/передачи кадров данных)
  • parquet, часть экосистемы Apache Arrow.

Вот ответ Мэтью Роклина (один из разработчиков dask), сравнивающий msgpack и pickle. Более подробное сравнение он написал в своем блоге.

person Josh Friedlander    schedule 21.07.2019
comment
(обратите внимание, что sklearn LabelEncoder делает то же самое, что и мой код в сером поле) - person Josh Friedlander; 15.12.2019

HDF5 не является подходящим решением для этого варианта использования. hdf5 — лучшее решение, если у вас есть много фреймов данных, которые вы хотите хранить в одной структуре. Он имеет больше накладных расходов при открытии файла, а затем позволяет эффективно загружать каждый кадр данных, а также легко загружать их фрагменты. Его следует рассматривать как файловую систему, в которой хранятся кадры данных.

В случае одного кадра данных события временного ряда рекомендуемыми форматами будут один из форматов проекта Apache Arrow, то есть feather или parquet. Следует думать о них как о файлах csv на основе столбцов (сжатых). Конкретный компромисс между этими двумя хорошо изложен в разделе различия между пером и паркетом?.

Одной из конкретных проблем, которую следует рассмотреть, являются типы данных. Поскольку feather не предназначен для оптимизации дискового пространства за счет сжатия, он может поддерживать большое разнообразие типов данных. . Хотя parquet пытается обеспечить очень эффективное сжатие, он может поддерживать только ограниченное подмножество, которое позволяет чтобы лучше обрабатывать сжатие данных.

person Alex Fish    schedule 27.07.2019