pandas、numpy、tensorの処理スピードを比較してみた

5 min read読了の目安(約4900字

(2021/3/21 追記)こちらの記事を拝見し、GPU使用時の時間の計測方法を修整しました。
併せてCPU使用時の時間もその際のものに訂正しています。

今回は pandas(DataFrame)、numpy(nddaray)、tensor(GPUなし/あり)でどのくらい処理スピードが異なるのか比較してみました。

ECサイトのデータセットを数値データのみに加工して使用しています。

環境はGoogle Colab、GPUは Tesla T4、条件統一のため型はすべてfloat64とし、tensor化にはpytorchを使用しています。

コードはこちら

CustomerID、InvoiceNoでfor分を回し、各CustomerID、InvoiceNoごとの売上 UnitPrice × Quantity を計算して辞書型として保存する処理をおこなっています。

pandas、numpy、tensorの違い

一般的にPandasはindexやcolumns名を保持させたりと扱えるデータの幅が広いのですが、処理が遅い傾向にあるようです。

一方、numpyは関数呼び出しのオーバーヘッドが発生しない分、処理が早い傾向にあるようです。

tensorはnumpyとよく似ているが、GPUを演算に使用できるようです。

https://deepage.net/features/pandas-numpy.html

https://qiita.com/mathlive/items/241bfb42d852bb801b96

pandasのデータセットと処理

for i in tqdm(df["CustomerID"].unique()[~np.isnan(df["CustomerID"].unique())]):
    for j in df[df["CustomerID"]==i]["InvoiceNo"].unique():
        sale_dict[f"{i}_{j}_sale"] = df[(df["CustomerID"]==i) & (df["InvoiceNo"]==j)]["Quantity"] * df[(df["CustomerID"]==i) & (df["InvoiceNo"]==j)]["UnitPrice"]

numpyのデータセットと処理

array([[5.36365e+05, 1.78500e+04, 2.55000e+00, 6.00000e+00],
       [5.36365e+05, 1.78500e+04, 3.39000e+00, 6.00000e+00],
       [5.36365e+05, 1.78500e+04, 2.75000e+00, 8.00000e+00],
       ...,
       [5.81587e+05, 1.26800e+04, 4.15000e+00, 4.00000e+00],
       [5.81587e+05, 1.26800e+04, 4.15000e+00, 4.00000e+00],
       [5.81587e+05, 1.26800e+04, 4.95000e+00, 3.00000e+00]])
for i in tqdm(np.unique(df_np[:, 1][~np.isnan(df_np[:, 1])])):
    for j in np.unique(df_np[df_np[:, 1]==i][:, 0]):
        sale_dict[f"{i}_{j}_sale"] = df_np[(df_np[:, 1]==i) & (df_np[:, 0]==j)][:, 3] * df_np[(df_np[:, 1]==i) & (df_np[:, 0]==j)][:, 2]

tensor(GPUなし)のデータセットと処理

tensor([[5.3636e+05, 1.7850e+04, 2.5500e+00, 6.0000e+00],
        [5.3636e+05, 1.7850e+04, 3.3900e+00, 6.0000e+00],
        [5.3636e+05, 1.7850e+04, 2.7500e+00, 8.0000e+00],
        ...,
        [5.8159e+05, 1.2680e+04, 4.1500e+00, 4.0000e+00],
        [5.8159e+05, 1.2680e+04, 4.1500e+00, 4.0000e+00],
        [5.8159e+05, 1.2680e+04, 4.9500e+00, 3.0000e+00]], dtype=torch.float64)
for i in tqdm(df_tensor[:, 1][~torch.isnan(df_tensor[:, 1])].unique()):
    for j in df_tensor[df_tensor[:, 1]==i][:, 0].unique():
        sale_dict[f"{i}_{j}_sale"] = df_tensor[(df_tensor[:, 1]==i) & (df_tensor[:, 0]==j)][:, 3] * df_tensor[(df_tensor[:, 1]==i) & (df_tensor[:, 0]==j)][:, 2]

tensor(GPUあり)のデータセットと処理

tensor([[5.3636e+05, 1.7850e+04, 2.5500e+00, 6.0000e+00],
        [5.3636e+05, 1.7850e+04, 3.3900e+00, 6.0000e+00],
        [5.3636e+05, 1.7850e+04, 2.7500e+00, 8.0000e+00],
        ...,
        [5.8159e+05, 1.2680e+04, 4.1500e+00, 4.0000e+00],
        [5.8159e+05, 1.2680e+04, 4.1500e+00, 4.0000e+00],
        [5.8159e+05, 1.2680e+04, 4.9500e+00, 3.0000e+00]], device='cuda:0',
       dtype=torch.float64)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
df_tensor = df_tensor.to(device)

# 以下はGPUなしと共通
for i in tqdm(df_tensor[:, 1][~torch.isnan(df_tensor[:, 1])].unique()):
    for j in df_tensor[df_tensor[:, 1]==i][:, 0].unique():
        sale_dict[f"{i}_{j}_sale"] = df_tensor[(df_tensor[:, 1]==i) & (df_tensor[:, 0]==j)][:, 3] * df_tensor[(df_tensor[:, 1]==i) & (df_tensor[:, 0]==j)][:, 2]

まとめ

結果、tensor(GPU)が圧倒的に処理が早くなりました。

また、今回の処理ではnumpyに比べpandasのほうが処理が早くなりました。

pandas numpy tensor(GPUなし) tensor(GPUあり修正前) tensor(GPUあり修正後)
処理時間 約118秒 約149秒 約162秒 約13秒 N/A
処理時間(再検証) 約118秒 約154秒 約177秒 約7.8秒 約7.7秒

(2021/3/21 追記)Google Colab 上でやっているせいか、データセットに対する処理の負荷のせいなのか、それほど変わらなかったようです。

今後も色んな条件で試してみようと思います。

参考 pandas、numpy、tensor 処理の違い

今回、pandas、numpy、tensorで同じ処理を行いましたが、それぞれのコードの違いを比較しました。

# 各列のユニーク値の取得
df["xxx"].unique() # pandas、NaNが複数あっても1つとしてカウント
np.unique(df[:, x]) # numpy、NaNが複数あると個別にカウントされる
df[:, x].unique() # tensor、NaNが複数あると個別にカウントされる

# NaNを除外するためのbool判定
~df["xxx"].isnull() # pandas
~np.isnan(df[:, x]) # numpy
~torch.isnan(df[:, x]) # tensor

今回のブログには使用していませんが、配列と同じ項目が含まれているかどうかの判定もご紹介します。

Pytorchにはisin()が存在しないようなので、少し複雑な処理が必要のようです。

df["xxx"].isin([配列]) # pandas
np.isin(df[:, x], [配列]) # numpy
(df[:, x, None] == torch.tensor([配列])).any(-1) # tensor

https://stackoverflow.com/questions/60918304/get-indices-of-elements-in-tensor-a-that-are-present-in-tensor-b

以上になります、最後までお読みいただきありがとうございました。