Yuta NakataのBlog

Python / AWS / ITについて役立つ情報を発信します

【1000倍早くなるPython高速化】for文とベクトル志向演算で計算比較してみた

背景

Python高速化のテクニック、ベクトル志向演算でどれだけ高速ができるか検証してみました。

高速化の題材は、for文ループを用いて、どれだけ差がでるかを検証してみました。

Python高速化については、以下を参照してください。

www.yuta-nakata.net

やってみた

テーマ設定

10万行のデータに対して、①2倍して②10引いて③半分にします

手法1

各行ごとに計算を実行します。

STEP = 100000

def method1():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)

    for i in range(len(df)):
        df.loc[i, 'A'] *= 2
        df.loc[i, 'A'] -= 10
        df.loc[i, 'A'] /= 2

手法2

データフレームに対して、ベクトル志向で計算します。

STEP = 100000

def method2():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)
    df['A'] *= 2
    df['A'] -= 10
    df['A'] /= 2

実行結果比較

計算結果を比較してみます

import time

import numpy as np
import pandas as pd


STEP = 100000


def method1():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)

    for i in range(len(df)):
        df.loc[i, 'A'] *= 2
        df.loc[i, 'A'] -= 10
        df.loc[i, 'A'] /= 2


def method2():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)
    df['A'] *= 2
    df['A'] -= 10
    df['A'] /= 2



if __name__ == '__main__':
    start = time.time()
    method1()
    result_time1 = time.time() - start
    print(f'finish time of method1() is {result_time1} (sec)')

    start = time.time()
    method2()
    result_time2 = time.time() - start
    print(f'finish time of method2() is {result_time2} (sec)')

    print(f'処理時間は、約{int(result_time1 / result_time2)}倍異なります')

この結果、、、

finish time of method1() is 9.090620040893555 (sec)
finish time of method2() is 0.003556966781616211 (sec)
処理時間は、約2555倍異なります

と、1000倍以上高速化させることができることがわかりました。

改めて、ベクトル志向で計算させることの重要性がわかる内容です。

おまけ

www.yuta-nakata.net

で紹介したiterrows()itertuples()でも実行速度の比較をしてみました。

import time

import numpy as np
import pandas as pd


STEP = 100000


def method1():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)

    for i in range(len(df)):
        df.loc[i, 'A'] *= 2
        df.loc[i, 'A'] -= 10
        df.loc[i, 'A'] /= 2


def method2():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)
    df['A'] *= 2
    df['A'] -= 10
    df['A'] /= 2


def method3():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)
    for _, item in df.iterrows():
        item['A'] = (item['A'] * 2 - 10) / 2


def method4():
    df = pd.DataFrame()
    df['A'] = np.arange(STEP)
    for item in df.itertuples():
        df.A = (df.A * 2 - 10) / 2


if __name__ == '__main__':
    start = time.time()
    method1()
    result_time1 = time.time() - start
    print(f'finish time of method1() is {result_time1} (sec)')

    start = time.time()
    method2()
    result_time2 = time.time() - start
    print(f'finish time of method2() is {result_time2} (sec)')

    start = time.time()
    method3()
    result_time3 = time.time() - start
    print(f'finish time of method3() is {result_time3} (sec)')

    start = time.time()
    method4()
    result_time4 = time.time() - start
    print(f'finish time of method4() is {result_time4} (sec)')

結果は、、、、、

finish time of method1() is 9.206869125366211 (sec)
finish time of method2() is 0.003290891647338867 (sec)
finish time of method3() is 1.6420342922210693 (sec)
finish time of method4() is 16.61213994026184 (sec)

でした。