まるっとワーク

データ分析・電子工作など気になることを残していきます

Python: mapやapply、applymapメソッドを使う

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メソッドについてまとめました。
データ数が膨大にあると、処理に時間がかかりますので、コードの組み方次第でだいぶ変わりますね。