Pythonプログラムにおいて、DataFrameの各カラムに対してforループで処理をする際、非常に時間がかかる場合があります。
今回は、処理時間低減にも有効なmapメソッド/applyメソッドの使い方についてまとめます。
開発環境について
$wmic os get caption >Microsoft Windows 10 Pro $wmic os get osarchitecture >64ビット $python -V Python 3.7.6
目次
mapメソッドとapplyメソッドの違い
対象となるデータ構造が違う
- mapメソッド: Serieseに対応 (DataFrame意外だとlistにも対応)
- applyメソッド: Seriese、DataFrame(各列/各行)に対応
- applymapメソッド: DataFrameに対応
SerieseはDataFrameの1つのカラムを指すデータ構造であり、DataFrameはSerieseの集合体であるため、1次元データ構造, 2次元のデータ構造のどちらか一方もしくはその両方に対応したメソッドであるという違いがあるようです。
それぞれの使い方
mapメソッド
- list 対象の書き方
map(関数, リスト)
- Seriese対象の書き方
Seriese.map(関数)
import pandas as pd import numpy as np # 対象データ作成 df = pd.DataFrame([[80,90], [50,40], [60,55], [70, 55], [90,95]], index=['Japanese', 'math', 'science', 'society', 'English'], columns=['A','B']) sample_list = [80, 50, 60, 70, 90] # 関数定義 def evaluate_score(x): if x >= 60: y = "合格" else: y = "不合格" return y # list対象の書き方 A_score_list = map(evaluate_score, sample_list) #Seriese対象の書き方 df['A_score'] = df['A'].map(evaluate_score) #結果出力 list (map型のオブジェクト: イテレータ) for i in A_score_list: print(i) #結果出力 Seriese print(df["A_score"])
出力結果は以下の通り
・list 合格 不合格 合格 合格 合格 ・Seriese Japanese 合格 math 不合格 science 合格 society 合格 English 合格
applyメソッド
- Seriese/DataFrame対象の書き方
DataFrame.apply(関数, axis=@)
※@=0 or 1 DataFrameを渡したときに列/行毎に計算するのかを分ける
>|memo|
# 対象データ作成
df = pd.DataFrame([[80,90], [50,40], [60,55], [70, 55], [90,95]],
index=['Japanese', 'math', 'science', 'society', 'English'],
columns=['A','B'])
sample_list = [80, 50, 60, 70, 90]
# 関数定義
def evaluate_score(x):
if np.mean(x) >= 60:
y = "合格"
else:
y = "不合格"
return y
#DataFrame対象の書き方
print(df.apply(evaluate_score,axis=0))
#Series対象の書き方
print(df['A'].apply(evaluate_score))
|
出力結果は以下の通り。2次元配列を渡した方は、各列/各行データが関数の引数として渡されるため、戻り値も変わる。
・DataFrame A 合格 B 合格 ・Seriese dtype: object Japanese 合格 math 不合格 science 合格 society 合格 English 合格
applymapメソッド
- DataFrame対象の書き方
DataFrame.apply(関数)
# 対象データ作成 df = pd.DataFrame([[80,90], [50,40], [60,55], [70, 55], [90,95]], index=['Japanese', 'math', 'science', 'society', 'English'], columns=['A','B']) sample_list = [80, 50, 60, 70, 90] # 関数定義 def evaluate_score(x): if np.mean(x) >= 60: y = "合格" else: y = "不合格" return y #Seriese対象の書き方 print(df.applymap(evaluate_score))
出力結果は以下の通り
A B Japanese 合格 合格 math 不合格 不合格 science 合格 不合格 society 合格 不合格 English 合格 合格
速度の違いについて
forループとapply等を使用した時の速度の違いを計算します。
上記サンプルデータはデータサイズが小さすぎるので、np.arangeで適当に1000万行分のデータを作成して時間を比較しました。
※処理時間計測にはTimeモジュールを使用
- For Loop 処理時間: 4.781sec
- Apply 処理時間: 2.62sec
検証用コード
import time # 対象データ作成 sample = pd.Series(np.arange(10000000)) # 関数定義 def evaluate_score(x): if x >= 60: y = "合格" else: y = "不合格" return y start = time.time() for i in sample: y = evaluate_score(i) elapsed_time = time.time() - start print ("ForLoop_elapsed_time:{0}".format(elapsed_time) + "[sec]") start = time.time() sample.apply(evaluate_score) elapsed_time = time.time() - start print ("apply_elapsed_time:{0}".format(elapsed_time) + "[sec]")
まとめ
今回はDataFrameの各カラムに対して一連の処理をするために使えるmapやapply, applymapメソッドについてまとめました。
データ数が膨大にあると、処理に時間がかかりますので、コードの組み方次第でだいぶ変わりますね。