Як зберігати кадр даних за допомогою 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 ( піблетів ), який пропонує дуже швидкий час доступу для великих наборів даних:

store = HDFStore('store.h5')

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

Більш прогресивні стратегії обговорюються в кулінарній книзі .


Оскільки 0,13 є також msgpack, який може бути кращим для сумісності, як більш швидка альтернатива JSON, або якщо у вас є дані з об'єкта / важкого тексту (див. Це питання ).


8
Збереження @geekazoid застаріле на to_pickle (що створює соління, а не csv, що набагато швидше / інший об'єкт).
Енді Хайден

9
@geekazoid У випадку, якщо дані потрібно трансформувати після завантаження (тобто рядок / об'єкт до datetime64), це потрібно буде зробити ще раз після завантаження збереженого CSV, що призводить до втрати продуктивності. pickle зберігає кадр даних у його поточному стані, таким чином, дані та його формат зберігається. Це може призвести до значного підвищення продуктивності.
харбун

4
Як соління, так і HDFStore не можуть зберегти кадр даних більше 8 Гб. Чи є альтернативи?
користувач1700890

1
@ user1700890 спробуйте створити з випадкових даних (текст та масиви) та опублікуйте нове запитання. Я не думаю, що це може бути правильним / підозрюючим, що ми щось пропускаємо. Нове питання отримає більше очей, але спробуйте включити / створити DataFrame, який відтворює :)
Енді Хейден

1
@YixingLiu ви можете змінити режим після факту stackoverflow.com/a/16249655/1240268
Енді Хейден

100

Хоча вже є кілька відповідей, я знайшов приємне порівняння, в якому вони спробували декілька способів серіалізувати Pandas DataFrames: Ефективно зберігати DataFrames Pandas .

Вони порівнюють:

  • соління: оригінальний формат даних ASCII
  • cPickle, бібліотека С
  • 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
Я оновив свою відповідь, щоб пояснити ваше запитання. Підсумовуючи: за замовчуванням соління зберігає дані у форматі ASCII.
agold

1
Ах, дякую за це пояснення! Як зауваження, панди DataFrame .to_pickle, здається, використовують pkl.HIGHEST_PROTOCOL (має бути 2)
ntg

2
Здається, що щоденник, пов’язаний вище ( Ефективно зберігати Pandas DataFrames видалено. Я робив свої власні порівняння з .to_pickle()(який використовує двійкове сховище) проти .to_hdf()(без стиснення). Мета - швидкість, розмір файлу для HDF - 11x Pickle, і час завантаження був 5x Pickle. Мої дані складали ~ 5k файлів ~ 7k рядків x 6 cols кожен, в основному числовий.
hamx0r

1
Сторінка все ще існує, вам просто потрібно буде зняти проділ
IanSR

2
@Mike Williamson, в моєму тесті, маринований набір в 5 разів швидше завантажувався, ніж HDF, а також займав 1/11 дискового простору (тобто hdf був на диск на 11 разів більший і забирав 5x стільки часу, скільки завантажувався з диска, скільки робив соління). все це було на python 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 мають 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модулі не захищене від помилкових або зловмисно побудованих даних. Ніколи не зніміть дані, отримані від недовіреного або несанкціонованого джерела.

  1. pickleповільно. Знайдіть тут і тут орієнтири.

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


1

Обчислювальні формати файлів досить швидкі для числових даних

Я вважаю за краще використовувати нудні файли, оскільки з ними швидко і просто працювати. Ось простий орієнтир для збереження та завантаження фрейму даних з 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

НЕПОГАНО!

КОНС

Виникла проблема, якщо ви збережете файл numpy за допомогою 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 для різних версій

Загальний крок був спрямований на голову / перо (попередження про депрекацію від панд / msgpack). Однак у мене є завдання з головоломкою з перехідними в специфікації Дані, серіалізовані з пірроу 0,15.1, не можна десеріалізувати за допомогою 0,16,0 СТРІК -7961 . Я використовую серіалізацію для використання redis, тому доводиться використовувати двійкове кодування.

Я повторно перевіряв різні варіанти (використовуючи ноутбук "Юпітер")

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>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

пір'я та паркет не працюють для моєї кадри даних. Я продовжую використовувати пірроу. Однак я доповнимо солінням (без компресії). Під час запису в кеш зберігають серіализовані форми пірроу і маринованих солінь. Під час читання з кеш-пам’яті резервне підсолювання, якщо деріаріалізація пірра не виходить.


Це не відповідає на питання
Jason S

0

Формат залежить від Вашого випадку використання

  • Збережіть DataFrame між сеансами ноутбука - перо , якщо ви звикли маринувати - також добре.
  • Збережіть DataFrame в найменшому розмірі файлу - паркет або мариновані.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.