簡體   English   中英

如何可逆地存儲和加載 Pandas dataframe 到磁盤/從磁盤加載

[英]How to reversibly store and load a Pandas dataframe to/from disk

現在我每次運行腳本時都會導入一個相當大的CSV作為 dataframe 。 是否有一個好的解決方案可以讓 dataframe 在兩次運行之間始終可用,這樣我就不必花費所有時間等待腳本運行?

最簡單的方法是將腌制用它to_pickle

df.to_pickle(file_name)  # where to save it, usually as a .pkl

然后您可以使用以下方法加載它:

df = pd.read_pickle(file_name)

注意:在 0.11.1 之前, saveload是唯一的方法(它們現在已被棄用,分別支持to_pickleread_pickle )。


另一個流行的選擇是使用HDF5 ( pytables ),它為大型數據集提供非常快的訪問時間:

import pandas as pd
store = pd.HDFStore('store.h5')

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

食譜中討論了更高級的策略。


從 0.13 開始,還有msgpack可能更適合互操作性,作為 JSON 的更快替代品,或者如果您有 python 對象/文本重數據(請參閱此問題)。

雖然已經有一些答案,但我找到了一個很好的比較,他們嘗試了幾種方法來序列化 Pandas DataFrames: Efficiently Store Pandas DataFrames

他們比較:

  • pickle:原始ASCII數據格式
  • cPickle,一個 C 庫
  • pickle-p2:使用較新的二進制格式
  • json:standardlib json 庫
  • json-no-index:類似於 json,但沒有索引
  • msgpack:二進制 JSON 替代方案
  • CSV
  • hdfstore:HDF5 存儲格式

在他們的實驗中,他們序列化了一個包含 1,000,000 行的 DataFrame,其中兩列分別測試:一列包含文本數據,另一列包含數字。 他們的免責聲明說:

您不應該相信以下內容可以概括為您的數據。 您應該查看自己的數據並自己運行基准測試

他們所引用的測試的源代碼可在線獲得 由於這段代碼不能直接工作,我做了一些小改動,你可以在這里得到: serialize.py我得到了以下結果:

時間對比結果

他們還提到,通過將文本數據轉換為分類數據,序列化速度要快得多。 在他們的測試中大約快 10 倍(另請參閱測試代碼)。

編輯:pickle 比 CSV 更高的時間可以通過使用的數據格式來解釋。 默認情況下, pickle使用可打印的 ASCII 表示,從而生成更大的數據集。 然而,從圖中可以看出,使用較新的二進制數據格式(版本 2, pickle-p2 )的pickle-p2加載時間要低得多。

其他一些參考:

如果我理解正確,您已經在使用pandas.read_csv()但希望加快開發過程,這樣您就不必每次編輯腳本時都加載文件,對嗎? 我有幾點建議:

  1. 您可以使用pandas.read_csv(..., nrows=1000)僅加載 CSV 文件的一部分以僅加載表的頂部位,同時進行開發

  2. ipython用於交互式會話,以便在編輯和重新加載腳本時將Pandas表保留在內存中。

  3. 將 csv 轉換為HDF5 表

  4. 更新使用DataFrame.to_feather()pd.read_feather()pd.read_feather()的 R 兼容羽毛二進制格式存儲數據(在我手中,在數字數據上比pandas.to_pickle()略快,在字符串數據上快得多)。

您可能也對 stackoverflow 上的這個答案感興趣。

泡菜效果很好!

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

您可以使用羽毛格式文件。 它非常快。

df.to_feather('filename.ft')

如前所述,有不同的選項和文件格式( HDF5JSONCSVparquetSQL )來存儲數據框。 但是, pickle不是一等公民(取決於您的設置),因為:

  1. pickle是一種潛在的安全風險。 形成picklePython 文檔

警告pickle模塊對於錯誤或惡意構造的數據並不安全。 永遠不要解開從不受信任或未經身份驗證的來源收到的數據。

  1. pickle很慢。 在這里這里找到基准。

根據您的設置/使用情況,這兩個限制都不適用,但我不建議將pickle作為 Pandas 數據幀的默認持久性。

Pandas 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

Numpy 文件格式對於數值數據來說非常快

我更喜歡使用 numpy 文件,因為它們快速且易於使用。 這是保存和加載具有 1 列 100 萬個點的數據幀的簡單基准。

import numpy as np
import pandas as pd

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

使用 ipython 的%%timeit魔法函數

%%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 保存 numpy 文件,然后嘗試使用 python 3 打開(反之亦然),則會出現問題。

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

pickle 協議格式:

協議版本 0 是原始的“人類可讀”協議,向后兼容早期版本的 Python。

協議版本 1 是一種舊的二進制格式,它也與早期版本的 Python 兼容。

協議版本 2 是在 Python 2.3 中引入的。 它提供了更有效的新型類酸洗。 有關協議 2 帶來的改進的信息,請參閱 PEP 307。

Python 3.0 中添加了協議版本 3。 它對字節對象有明確的支持,並且不能被 Python 2.x 取消。 這是默認協議,當需要兼容其他 Python 3 版本時推薦使用的協議。

Python 3.4 中添加了協議版本 4。 它增加了對超大對象的支持,酸洗更多種類的對象,以及一些數據格式優化。 有關協議 4 帶來的改進的信息,請參閱 PEP 3154。

另一個非常新鮮的to_pickle()測試。

我總共有25 個.csv文件需要處理,最終的dataframe包含大約200 萬個項目。

(注意:除了加載 .csv 文件,我還操作一些數據並通過新列擴展數據框。)

瀏覽所有25 個.csv文件並創建數據框大約需要14 sec

pkl文件加載整個數據幀需要不到1 sec

Arctic是 Pandas、numpy 和其他數字數據的高性能數據存儲。 它位於 MongoDB 之上。 對於 OP 來說可能有點矯枉過正,但值得一提的是其他在這篇文章中絆倒的人

import pickle

example_dict = {1:"6",2:"2",3:"g"}

pickle_out = open("dict.pickle","wb")
pickle.dump(example_dict, pickle_out)
pickle_out.close()

上面的代碼將保存pickle文件

pickle_in = open("dict.pickle","rb")
example_dict = pickle.load(pickle_in)

這兩行將打開保存的泡菜文件

pyarrow 跨版本兼容性

總體轉移到 pyarrow/feather(來自 pandas/msgpack 的棄用警告)。 但是,我對 pyarrow 有一個挑戰,在規范中具有瞬態數據用 pyarrow 0.15.1 序列化不能用 0.16.0 ARROW-7961反序列化。 我正在使用序列化來使用 redis,因此必須使用二進制編碼。

我重新測試了各種選項(使用 jupyter notebook)

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。 不過我會補充泡菜(無壓縮)。 寫入緩存存儲 pyarrow 和 pickle 序列化表單時。 如果 pyarrow 反序列化失敗,則從緩存回退讀取到 pickle 時。

這里有很多很棒和足夠的答案,但我想發布一個我在 Kaggle 上使用的測試,其中大 df 由不同的 Pandas 兼容格式保存和讀取:

https://www.kaggle.com/pedrocouto39/fast-reading-w-pickle-feather-parquet-jay

我不是作者或作者的朋友,但是,當我讀到這個問題時,我認為在那里值得一提。

CSV:1 分鍾 42 秒泡菜:4.45 秒羽毛:4.35 秒鑲木地板:8.31 秒 Jay:8.12 毫秒或 0.0812 秒(極快!)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM