在數(shù)據(jù)分析相關(guān)項目工作時,我通常使用Jupyter筆記本和pandas庫來處理和傳遞我的數(shù)據(jù)。對于中等大小的數(shù)據(jù)集來說,這是一個非常直接的過程,你甚至可以將其存儲為純文本文件而沒有太多的開銷。
然而,當(dāng)你的數(shù)據(jù)集中的觀測數(shù)據(jù)數(shù)量較多時,保存和加載數(shù)據(jù)回內(nèi)存的過程就會變慢,現(xiàn)在程序的重新啟動都會迫使你等待數(shù)據(jù)重新加載。所以最終,CSV文件或任何其他純文本格式都會失去吸引力。
我們可以做得更好。有很多二進制格式可以用來將數(shù)據(jù)存儲到磁盤上,其中有很多格式pandas都支持。我們怎么能知道哪一種更適合我們的目的呢?
來吧,我們嘗試其中的幾個,然后進行對比!這就是我決定在這篇文章中要做的:通過幾種方法將 pandas.DataFrame 保存到磁盤上,看看哪一種在I/O速度、內(nèi)存消耗和磁盤空間方面做的更好。
在這篇文章中,我將展示我的測試結(jié)果。
1.要比較的格式
我們將考慮采用以下格式來存儲我們的數(shù)據(jù):
- CSV -- 數(shù)據(jù)科學(xué)家的一個好朋友
- Pickle -- 一種Python的方式來序列化事物
- MessagePack -- 它就像JSON,但又快又小
- HDF5 -- 一種設(shè)計用于存儲和組織大量數(shù)據(jù)的文件格式
- Feather -- 一種快速、輕量級、易于使用的二進制文件格式,用于存儲數(shù)據(jù)框架
- Parquet -- Apache Hadoop的柱狀存儲格式
所有這些格式都是被廣泛使用的,而且(也許除了MessagePack)在你做一些數(shù)據(jù)分析的事情時非常經(jīng)常遇到。
為了追求找到最好的緩沖格式來存儲程序會話之間的數(shù)據(jù),我選擇了以下指標(biāo)進行比較。
- size_mb - 文件大小(Mb)。
- save_time - 將數(shù)據(jù)幀保存到磁盤上所需的時間量。
- load_time - 將之前轉(zhuǎn)儲的數(shù)據(jù)幀加載到內(nèi)存中所需要的時間量。
- save_ram_delta_mb - 數(shù)據(jù)幀保存過程中最大的內(nèi)存消耗增長量。
- load_ram_delta_mb - 數(shù)據(jù)幀加載過程中的最大內(nèi)存消耗增長量。
請注意,當(dāng)我們使用高效壓縮的二進制數(shù)據(jù)格式,如 Parquet 時,最后兩個指標(biāo)變得非常重要。它們可以幫助我們估計加載序列化數(shù)據(jù)所需的內(nèi)存量,此外還有數(shù)據(jù)大小本身。我們將在接下來的章節(jié)中更詳細(xì)地討論這個問題。
2.測試及結(jié)果
我決定使用一個合成數(shù)據(jù)集進行測試,以便更好地控制序列化的數(shù)據(jù)結(jié)構(gòu)和屬性。
另外,我在我的基準(zhǔn)中使用了兩種不同的方法:
(a) 將生成的分類變量保留為字符串。
(b) 在執(zhí)行任何I/O之前將它們轉(zhuǎn)換為 pandas.Categorical 數(shù)據(jù)類型。
函數(shù)generate_dataset顯示了我在基準(zhǔn)中是如何生成數(shù)據(jù)集的:
def generate_dataset(n_rows, num_count, cat_count, max_nan=0.1, max_cat_size=100):
"""
隨機生成具有數(shù)字和分類特征的數(shù)據(jù)集。
數(shù)字特征取自正態(tài)分布X ~ N(0, 1)。
分類特征則被生成為隨機的uuid4字符串。
此外,數(shù)字和分類特征的max_nan比例被替換為NaN值。
"""
dataset, types = {}, {}
def generate_categories():
from uuid import uuid4
category_size = np.random.randint(2, max_cat_size)
return [str(uuid4()) for _ in range(category_size)]
for col in range(num_count):
name = f'n{col}'
values = np.random.normal(0, 1, n_rows)
nan_cnt = np.random.randint(1, int(max_nan*n_rows))
index = np.random.choice(n_rows, nan_cnt, replace=False)
values[index] = np.nan
dataset[name] = values
types[name] = 'float32'
for col in range(cat_count):
name = f'c{col}'
cats = generate_categories()
values = np.array(np.random.choice(cats, n_rows, replace=True), dtype=object)
nan_cnt = np.random.randint(1, int(max_nan*n_rows))
index = np.random.choice(n_rows, nan_cnt, replace=False)
values[index] = np.nan
dataset[name] = values
types[name] = 'object'
return pd.DataFrame(dataset), types
我們將CSV文件的保存和加載性能作為一個基準(zhǔn)。
五個隨機生成的具有一百萬個觀測值的數(shù)據(jù)集被轉(zhuǎn)儲到CSV中,并讀回內(nèi)存以獲得平均指標(biāo)。
每種二進制格式都針對20個隨機生成的具有相同行數(shù)的數(shù)據(jù)集進行測試。
這些數(shù)據(jù)集包括15個數(shù)字特征和15個分類特征。你可以在這個資源庫中找到帶有基準(zhǔn)測試功能和所需的完整源代碼:
https://github.com/devforfu/pandas-formats-benchmark
或在Python實用寶典后臺回復(fù) **Pandas IO對比 **,下載完整代碼。
(a) 數(shù)據(jù)為字符串特征時的性能
下圖顯示了每種數(shù)據(jù)格式的平均I/O時間。一個有趣的觀察是,hdf顯示出比csv更慢的加載速度,而其他二進制格式的表現(xiàn)明顯更好。其中最令人印象深刻的是feather和parquet。
在保存數(shù)據(jù)和從磁盤上讀取數(shù)據(jù)時,內(nèi)存開銷如何?
下一張圖片告訴我們,hdf 的表現(xiàn)就不是那么好了??梢钥隙ǖ氖牵琧sv在保存/加載純文本字符串時不需要太多的額外內(nèi)存,而Feather和parquet則相當(dāng)接近:
最后,讓我們看看文件的大小。這次parquet顯示了一個令人印象深刻的結(jié)果,考慮到這種格式是為有效存儲大量數(shù)據(jù)而開發(fā)的,這并不令人驚訝。
(b) 字符串特征轉(zhuǎn)換為數(shù)字時的性能
在上一節(jié)中,我們沒有嘗試有效地存儲我們的分類特征而是使用普通的字符串。讓我們來彌補這個遺漏吧! 這一次我們使用一個專門的 pandas.Categorical 類型,轉(zhuǎn)字符串特征為數(shù)字特征。
看看現(xiàn)在與純文本的csv相比,它看起來如何!
現(xiàn)在所有的二進制格式都顯示出它們的真正力量。Csv的基準(zhǔn)結(jié)果已經(jīng)遠(yuǎn)遠(yuǎn)落后了,所以讓我們把它去掉,以便更清楚地看到各種二進制格式之間的差異:
Feather 和 Pickle 顯示了最好的 I/O 速度,而 hdf 仍然顯示了明顯的性能開銷。
現(xiàn)在是時候比較數(shù)據(jù)進程加載時的內(nèi)存消耗了。下面的柱狀圖顯示了我們之前提到的關(guān)于parquet格式的一個重要事實。
可以看到 parquet 讀寫時的內(nèi)存空間差距有多大,你有可能你無法將比較大的 parquet 文件加載到內(nèi)存中。
最后的圖顯示了各格式的文件大小。所有的格式都顯示出良好的效果,除了hdf仍然需要比其他格式多得多的空間:
3.結(jié)論
正如我們的測試所顯示的,似乎 feather 格式是存儲Python會話數(shù)據(jù)的理想候選者。它顯示了很快的I/O速度,在磁盤上不占用太多內(nèi)存,并且在加載回RAM時不需要消耗太大的內(nèi)存。
當(dāng)然,這種比較并不意味著你應(yīng)該在每個可能的情況下使用這種格式。例如,feather格式一般不會被用作長期文件存儲的格式。
另外,某些特定情況下也無法使用 feather,這由你的整個程序架構(gòu)決定。然而,就如本帖開頭所述的目的,它在不被任何特殊事項限制的情況下是一個很好的選擇。
-
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7077瀏覽量
89161 -
存儲
+關(guān)注
關(guān)注
13文章
4324瀏覽量
85938 -
磁盤
+關(guān)注
關(guān)注
1文章
379瀏覽量
25221
發(fā)布評論請先 登錄
相關(guān)推薦
評論