CSV無可厚非的是一種良好的通用檔案儲存方式,幾乎任何一款工具或者程式語言都能對其進行讀寫,但是當檔案特別大的時候,CSV這種儲存方式就會變得十分緩慢且低效。本文將介紹幾種在Python中能夠代替CSV這種格式的其他檔案格式,並對比每種檔案儲存的時間與大小。

先說結論,parquet是最好的檔案儲存格式,具體對比見下文。

生成隨機資料

匯入依賴

import random
import string
import pickle
# 以下需要自行安裝
import numpy as np
import pandas as pd
import tables
import pyarrow as pa
import pyarrow.feather as feather
import pyarrow.parquet as pq

生成隨機資料

這裡使用pandas的dataframe來儲存資料

# 變數定義
row_num = int(1e7)
col_num = 5
str_len = 4
str_nunique = 10 # 字串組合數量
# 生成隨機數
int_matrix = np.random.randint(0, 100, size=(row_num, col_num))
df = pd.DataFrame(int_matrix, columns=['int_%d' % i for i in range(col_num)])
float_matrix = np.random.rand(row_num, col_num)
df = pd.concat(
(df, pd.DataFrame(float_matrix, columns=['float_%d' % i for i in range(col_num)])), axis=1)
str_list = [''.join(random.sample(string.ascii_letters, str_len))
for _ in range(str_nunique)]
for i in range(col_num):
sr = pd.Series(str_list*(row_num//str_nunique)
).sample(frac=1, random_state=i)
df['str_%d' % i] = sr print(df.info())

生成100w行資料,其中整型,浮點型和字串各5列,資料大小在記憶體裡大概為1GB+

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000000 entries, 0 to 9999999
Data columns (total 15 columns):
# Column Dtype
--- ------ -----
0 int_0 int64
1 int_1 int64
2 int_2 int64
3 int_3 int64
4 int_4 int64
5 float_0 float64
6 float_1 float64
7 float_2 float64
8 float_3 float64
9 float_4 float64
10 str_0 object
11 str_1 object
12 str_2 object
13 str_3 object
14 str_4 object
dtypes: float64(5), int64(5), object(5)
memory usage: 1.1+ GB

儲存檔案

csv

CSV的儲存方式很簡單,直接使用pandas自帶的to_csv() 方法即可

# 寫入
df.to_csv('./df_csv.csv', index=False)
# 讀取
df = pd.read_csv('./df_csv.csv')

寫入時間花費:78 s

讀取時間花費:11.8 s

所需儲存空間:1.3GB

pkl

pkl檔案需要用到built-inpickle

# 寫入
with open('./df_pkl.pkl', 'wb') as f:
pickle.dump(df, f)
# 讀取
with open('./df_pkl.pkl', 'rb') as f:
df = pickle.load(f)

寫入時間花費:2.89 s

讀取時間花費:2.61 s

所需儲存空間:858M

npy

npy是numpy自帶的一種儲存格式,唯一的缺點是隻能儲存numpy的格式,所以需要將pandas先轉成numpy才行,為了公平,這裡我們會算上轉換的時間

# 寫入
with open('./df_npy.npy', "wb") as f:
np.save(f, arr=df.values)
# 讀取
with open('./df_npy.npy', "rb") as f:
df_array = np.load(f, allow_pickle=True)
df = pd.DataFrame(df_array)

寫入時間花費:21 s

讀取時間花費:14.8 s

所需儲存空間:620M

hdf

層次資料格式(HDF)是自描述的,允許應用程式在沒有外部資訊的情況下解釋檔案的結構和內容。一個HDF檔案可以包含一系列相關物件,這些物件可以作為一個組或單個物件進行訪問。

這裡將使用pandas自帶的to_hdf()方法,該方法預設是用的HDF5格式

# 寫入
df.to_hdf('df_hdf.h5', key='df')
# 讀取
df = pd.read_hdf('df_hdf.h5', key='df')

寫入時間花費:3.96 s

讀取時間花費:4.13 s

所需儲存空間:1.5G

已廢棄 msgpack

pandas支援msgpack格式的物件序列化。他是一種輕量級可移植的二進位制格式,同二進位制的JSON類似,具有高效的空間利用率以及不錯的寫入(序列化)和讀取(反序列化)效能。

從0.25版本開始,不推薦使用msgpack格式,並且之後的版本也將刪除它。推薦使用pyarrow對pandas物件進行線上的轉換。

read_msgpack() (opens new window)僅在pandas的0.20.3版本及以下版本相容。

parquet

Apache Parquet為資料幀提供了分割槽的二進位制柱狀序列化。它的設計目的是使資料幀的讀寫效率,並使資料共享跨資料分析語言容易。Parquet可以使用多種壓縮技術來儘可能地縮小檔案大小,同時仍然保持良好的讀取效能。

這裡需要使用到pyarrow裡面的方法來進行操作

# 寫入
pq.write_table(pa.Table.from_pandas(df), 'df_parquet.parquet')
# 讀取
df = pq.read_table('df_parquet.parquet').to_pandas()

寫入時間花費:3.47 s

讀取時間花費:1.85 s

所需儲存空間:426M

feature

Feather是一種可移植的檔案格式,用於儲存內部使用Arrow IPC格式的Arrow表或資料幀(來自Python或R等語言)。Feather是在Arrow專案早期建立的,作為Python和R的快速、語言無關的資料幀儲存概念的證明。

這裡需要使用到pyarrow裡面的方法來進行操作

# 寫入
feather.write_feather(df, 'df_feather.feather')
# 讀取

寫入時間花費:1.9 s

讀取時間花費:1.52 s

所需儲存空間:715M

總結

對比表格

檔案型別 讀取時間(s) 寫入時間(s) 儲存空間(MB)
csv 78.00 11.80 1,300
pickle 2.89 2.61 858
npy 21.00 14.80 620
hdf 3.96 4.13 1,500
parquet 3.47 1.85 426
feature 1.90 1.52 715

時間對比

空間對比

可以看出parquet會是一個儲存檔案的最好選擇,雖然時間上比feature略慢一點,但空間上有著更大的優勢。