Как хранить данные с помощью Pandas


317

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


2
Да, это одна из моих основных претензий при использовании Python - нет простого способа сохранить и получить фреймы данных. R и SAS намного более удобны в этом отношении.
RobertF

Ответы:


481

Самый простой способ - засолить его, используя 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 ), который предлагает очень быстрое время доступа для больших наборов данных:

store = HDFStore('store.h5')

store['df'] = df  # save it
store['df']  # load it

Более продвинутые стратегии обсуждаются в кулинарной книге .


Начиная с версии 0.13 есть также msgpack, который может быть лучше для обеспечения взаимодействия, как более быстрая альтернатива JSON, или если у вас есть данные, связанные с объектами Python / text-heavy (см. Этот вопрос ).


8
@geekazoid save не рекомендуется использовать to_pickle (который создает рассол, а не CSV, который намного быстрее / другой объект).
Энди Хейден,

9
@geekazoid В случае, если данные должны быть преобразованы после загрузки (т. е. строка / объект в datetime64), это необходимо будет сделать снова после загрузки сохраненного CSV, что приведет к снижению производительности. Pickle сохраняет фрейм данных в его текущем состоянии, таким образом данные и его формат сохраняются. Это может привести к значительному увеличению производительности.
harbun

4
Как pickle, так и HDFStore не могут сохранять данные более 8 ГБ. Есть ли альтернативы?
user1700890

1
@ user1700890 попробуйте сгенерировать из случайных данных (текст и массивы) и опубликовать новый вопрос. Я не думаю, что это может быть правильно / подозреваю, что мы что-то упустили. Новый вопрос получит больше глаз, но попробуйте включить / сгенерировать DataFrame, который воспроизводит :)
Andy Hayden

1
@YixingLiu, вы можете изменить режим после факта stackoverflow.com/a/16249655/1240268
Энди Хейден

100

Хотя уже есть некоторые ответы, я нашел хорошее сравнение, в котором они попробовали несколько способов сериализации Pandas DataFrames: Эффективное хранение Pandas DataFrames .

Они сравнивают:

  • pickle: оригинальный формат данных ASCII
  • cPickle, библиотека C
  • pickle-p2: использует более новый двоичный формат
  • json: стандартная библиотека json
  • json-no-index: как json, но без индекса
  • msgpack: двоичная альтернатива JSON
  • CSV
  • hdfstore: формат хранения HDF5

В своем эксперименте они сериализуют DataFrame из 1 000 000 строк, причем два столбца проверяются отдельно: один с текстовыми данными, другой с числами. Их отказ от ответственности говорит:

Вы не должны доверять тому, что следует обобщению ваших данных. Вы должны посмотреть на свои собственные данные и запустить тесты самостоятельно

Исходный код теста, на который они ссылаются, доступен онлайн . Поскольку этот код не работал напрямую, я внес некоторые незначительные изменения, которые вы можете получить здесь: serialize.py Я получил следующие результаты:

результаты сравнения времени

Они также упоминают, что с преобразованием текстовых данных в категориальные данные сериализация намного быстрее. В их тесте примерно в 10 раз быстрее (см. Также тестовый код).

Изменить : более высокие времена для рассола, чем CSV, можно объяснить используемым форматом данных. По умолчанию pickleиспользуется печатное представление ASCII, которое генерирует большие наборы данных. Однако, как видно из графика, для консервирования с использованием более нового формата двоичных данных (версия 2 pickle-p2) время загрузки значительно меньше.

Некоторые другие ссылки:


1
Я обновил свой ответ, чтобы объяснить ваш вопрос. Подводя итог: по умолчанию pickle хранит данные в формате ASCII.
agold

1
Ах, спасибо за это объяснение! Как примечание, панды DataFrame .to_pickle, кажется, используют pkl.HIGHEST_PROTOCOL (должно быть 2)
нтг

2
Похоже, что блог, на который есть ссылка ( Эффективно хранить Pandas DataFrames , был удален. Я провел свои собственные сравнения с .to_pickle()(который использует двоичное хранилище) с .to_hdf()(без сжатия). Целью была скорость, размер файла для HDF был 11x Pickle, и время загрузки было 5x Pickle. Мои данные были ~ 5k файлов по ~ 7k строк по 6 столбцов каждый, в основном числовые.
hamx0r

1
Страница все еще существует, вам просто нужно удалить косую черту: эффективно хранить Pandas DataFrames
IanSR

2
@ Майк Уильямсон, в моем тесте, pickle загружался в 5 раз быстрее, чем HDF, а также занимал 1/11 дискового пространства (т. Е. Hdf был в 11 раз больше на диске и занимал в 5 раз больше времени для загрузки с диска, чем это делал pickle). это было все на питоне 3 с пандами 0.22.0.
hamx0r

35

Если я правильно понимаю, вы уже используете, pandas.read_csv()но хотели бы ускорить процесс разработки, чтобы вам не приходилось загружать файл каждый раз, когда вы редактируете свой скрипт, не так ли? У меня есть несколько рекомендаций:

  1. вы можете загрузить только часть файла CSV, используя pandas.read_csv(..., nrows=1000)только верхний бит таблицы, пока вы занимаетесь разработкой

  2. используйте ipython для интерактивного сеанса, чтобы таблица панд сохранялась в памяти при редактировании и перезагрузке скрипта.

  3. преобразовать CSV в таблицу HDF5

  4. обновленное использование DataFrame.to_feather()и pd.read_feather()хранение данных в R-совместимом двоичном формате с пером , который очень быстрый (в моих руках, немного быстрее, чем pandas.to_pickle()на числовых данных и намного быстрее на строковых данных).

Вы также можете быть заинтересованы в этом ответе на stackoverflow.


Знаете ли вы, почему to_featherбы хорошо работать со строковыми данными? Я установил бенчмаркинг to_pickleи to_featureна моем числовом кадре данных и рассола примерно в 3 раза быстрее.
Zyxue

@zyxue хороший вопрос, я, честно говоря, не слишком много играл с перьями, поэтому у меня нет ответа
Ной,

20

Рассол работает хорошо!

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

8
Обратите внимание, что сгенерированные файлы не являются CSV-файлами, возможно, лучше использовать расширение, .pklпредложенное в ответе @Andy Haydens.
agold

5

Вы можете использовать файл формата пера. Это очень быстро.

df.to_feather('filename.ft')

И данные могут быть использованы непосредственно с Rпомощью featherбиблиотеки.
Джеймс Хиршорн

4

У DataFrames Pandas есть 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

4

Как уже упоминалось, существуют различные параметры и форматы файлов ( HDF5 , JSON , CSV , паркет , SQL ) для хранения фрейма данных. Тем pickleне менее, это не первоклассный гражданин (в зависимости от вашей настройки), потому что:

  1. pickleэто потенциальный риск для безопасности. Сформируйте Python документацию для pickle :

Предупреждение В pickleмодуле не защищено от ошибочных или злонамеренно построенных данных. Никогда не извлекайте данные, полученные из ненадежных или не прошедших проверку подлинности источников.

  1. pickleмедленный. Найти здесь и здесь ориентиры.

В зависимости от вашей настройки / использования оба ограничения не применяются, но я бы не рекомендовал pickleв качестве сохранения по умолчанию для кадров данных pandas.


1

Numpy форматы файлов довольно быстро для числовых данных

Я предпочитаю использовать NumPy файлы, так как с ними быстро и легко работать. Вот простой тест для сохранения и загрузки фрейма данных с 1 столбцом в 1 миллион точек.

import numpy as np
import pandas as pd

num_dict = {'voltage': np.random.rand(1000000)}
num_df = pd.DataFrame(num_dict)

используя %%timeitмагическую функцию ipython

%%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, а затем пытаетесь открыть с помощью Python 3 (или наоборот).


6
обратите внимание, что это решение удалит все ваши имена столбцов и изменит все ваши целочисленные данные на плавающие :(
Джозеф Гарвин

0

https://docs.python.org/3/library/pickle.html

Форматы протокола маринования:

Протокол версии 0 является исходным «читаемым человеком» протоколом и обратно совместим с более ранними версиями Python.

Протокол версии 1 - это старый двоичный формат, который также совместим с более ранними версиями Python.

Протокол версии 2 был представлен в Python 2.3. Это обеспечивает намного более эффективное травление классов нового стиля. Обратитесь к PEP 307 за информацией об улучшениях, внесенных протоколом 2.

Протокол версии 3 был добавлен в Python 3.0. Он имеет явную поддержку байтовых объектов и не может быть распакован Python 2.x. Это протокол по умолчанию и рекомендуемый протокол, когда требуется совместимость с другими версиями Python 3.

Протокол версии 4 был добавлен в Python 3.4. Добавлена ​​поддержка очень больших объектов, выборка большего количества типов объектов и некоторые оптимизации форматов данных. Обратитесь к PEP 3154 за информацией об улучшениях, внесенных протоколом 4.


0

pyarrow совместимость между версиями

Общий ход был в сторону pyarrow / пера (предупреждения об устаревании от pandas / msgpack). Однако у меня есть проблема с pyarrow с переходным процессом в спецификации. Данные, сериализованные с pyarrow 0.15.1, не могут быть десериализованы с 0.16.0 ARROW-7961 . Я использую сериализацию, чтобы использовать Redis, поэтому приходится использовать двоичную кодировку.

Я перепробовал различные варианты (используя ноутбук Jupyter)

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.


Это не отвечает на вопрос
Джейсон С

0

Формат зависит от вашего варианта использования

  • Сохраняйте DataFrame между сеансами ноутбука - перо , если вы привыкли к засолке - тоже хорошо.
  • Сохраните DataFrame в наименьшем возможном размере файла - паркет или pickle.gz (проверьте, что лучше для ваших данных)
  • Сохранить очень большой DataFrame (более 10 миллионов строк) - hdf
  • Уметь читать данные на другой платформе (не Python), которая не поддерживает другие форматы - csv , csv.gz , проверьте, есть ли паркет , поддерживается
  • Уметь просматривать своими глазами / используя Excel / Google Sheets / Git diff - csv
  • Сохраните DataFrame, который занимает почти всю оперативную память - CSV

Сравнение форматов файлов панд в этом видео .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.