BETA

Pythonによる行列の転置速度の比較

投稿日:2020-01-19
最終更新:2020-01-20

1. 背景

Python標準のリスト型による2次元配列から,行と列を入れ替えた2次元配列(転置行列)を得たいと思い,自作したコードがめちゃくちゃ遅かったため,どの転置行列方法が最も速いのか気になったので比較してみました.

転置行列 - Wikipedia

筆者はプログラミング歴半年以下の初心者のため,間違いや改善方法などがありましたら,ご指摘いただけると幸いです.
速度の比較結果へ

2. 環境

name version
Python 3.7.4
Jupyter Notebook 6.0.1
NumPy 1.16.5
Pandas 0.25.1

3. 条件

  • 入力はPython標準のリスト型による2次元配列
  • 出力はPython標準のリスト型,numpy.arrayまたはpandas.DataFrame

3-1. 転置する行列

転置する行列名はin_matrixとし,今回は100×100の行列を生成します.

#転置する行列in_matrixの作成   
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

3-2. 処理時間の測定

Jupyter Notebook (IPython) の%timeitを用いて行います.
Built-in magic commands — %timeit
ループ回数は-n 10000,繰り返しは-r 10としました.

#処理時間の測定  
%timeit -n 10000 -r 10 turn_matrix(in_matrix)  

4. 検討した転置方法

上記の内,組み込み関数zip(),NumPy,Pandasを用いた転置は以下を参考にさせていただきました.
Pythonリスト型の二次元配列の行と列を入れ替える(転置) | note.nkmk.me

5. 速度の比較結果

転置速度の比較では,NumPyによる転置圧倒的に速く(1.67 µs),次いでPandasによる転置(86.3 µs),関数zip()+リスト内包表記(99.4 µs)となりました.

No. def description 転置速度
1 turn_matrix1() 二重ループ1 777 µs ± 43.6 µs
2 turn_matrix2() 二重ループ2 654 µs ± 83 µs
3 turn_matrix3() 関数zip() 105 µs ± 4.22 µs
4 turn_matrix4() 関数zip()+リスト内包表記 99.4 µs ± 1.36 µs
5 turn_matrix5() NumPy 1.67 µs ± 38.9 ns
6 turn_matrix6() Pandas 86.3 µs ± 4.54 µs


しかしながら,NumPyによる転置Pandasによる転置では,リスト型からnumpy.arrayへの変換は486 µs,pandas.DataFrameへの変換は6.19 ms掛かるため,
リスト型からの変換を含めた時間は,NumPyによる転置487.67 µs),Pandasによる転置6.2763 ms)となりました.
そのため,与えられる行列がリスト型である場合,リスト型の行列から転置行列を得るためのトータルの処理時間は,関数zip()+リスト内包表記(99.4 µs)が最も速いと考えられました.

No. def description 転置速度 np.arrayまたはpd.DataFrameへの変換
1 turn_matrix1() 二重ループ1 777 µs ± 43.6 µs
2 turn_matrix2() 二重ループ2 654 µs ± 83 µs
3 turn_matrix3() 関数zip() 105 µs ± 4.22 µs
4 turn_matrix4() 関数zip()+リスト内包表記 99.4 µs ± 1.36 µs
5 turn_matrix5() NumPy 1.67 µs ± 38.9 ns 486 µs ± 10.1 µs
6 turn_matrix6() Pandas 86.3 µs ± 4.54 µs 6.19 ms ± 43.1 µs

6. 転置関数

6-1. 二重ループによる転置1

関数名:turn_matrix1()
何も参考にせず作成した二重ループによる転置.
引数matrixの行数xと列数yをforループに渡し,行数を列数,列数を行数として引数matrixから取り出す.

#転置する行列in_matrixの作成  
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

#転置関数turn_matrix1()  
def turn_matrix1(matrix):  
    x = len(matrix)  
    y = len(matrix[0])      
    turned = []  
    for i in range(y):  
        tmp = []  
        for j in range(x):  
            tmp.append(matrix[j][i])  
        turned.append(tmp)  
    return turned  

#処理時間の測定  
%timeit -r 10 -n 10000 turn_matrix1(in_matrix)  

実行結果
777 µs ± 43.6 µs per loop (mean ± std. dev. of 10 runs, 10000 loops each)    

6-2. 二重ループによる転置2

関数名:turn_matrix2()
6-1. 二重ループによる転置1では引数matrixの行数xと列数yを取得していましたが,引数matrixからforループで1行ずつ取り出し,同じ列番号の値をtmp行を作成し,それをturnedに追加していく.

#転置する行列in_matrixの作成  
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

#転置関数turn_matrix2()  
def turn_matrix2(matrix):  
    y = len(matrix[0])  
    turned = []  
    for i in range(y):  
        tmp = []  
        for j in matrix:  
            tmp.append(j[i])  
        turned.append(tmp)  
    return turned  

#処理時間の測定  
%timeit -r 10 -n 10000 turn_matrix2(in_matrix)  

実行結果
654 µs ± 83 µs per loop (mean ± std. dev. of 10 runs, 10000 loops each)    

6-3. 組み込み関数zip()による転置

関数名:turn_matrix3()

#転置する行列in_matrixの作成  
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

#転置関数turn_matrix3(matrix)  
def turn_matrix3(matrix):  
    turned = []  
    for i in zip(*matrix):  
        turned.append(list(i))  
    return turned  

#処理時間の測定  
%timeit -r 10 -n 10000 turn_matrix3(in_matrix)  

実行結果
105 µs ± 4.22 µs per loop (mean ± std. dev. of 10 runs, 10000 loops each)    

6-4. 組み込み関数zip()+リスト内包表記による転置

関数名:turn_matrix4()

#転置する行列in_matrixの作成  
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

#転置関数turn_matrix4()  
def turn_matrix4(matrix):  
    return [list(x) for x in zip(*matrix)]  

#処理時間の測定  
%timeit -r 10 -n 10000 turn_matrix3(in_matrix)  

実行結果
99.4 µs ± 1.36 µs per loop (mean ± std. dev. of 10 runs, 10000 loops each)

※関数化しない場合

turned_matrix = [list(x) for x in zip(*in_matrix)]   

Pythonリスト型の二次元配列の行と列を入れ替える(転置) | note.nkmk.me      

6-5. NumPyによる転置

関数名:turn_matrix5()

import numpy as np  

#転置する行列in_matrixの作成  
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

#in_matrixからnumpy.arrayのnumpy_in_matrixの作成  
%timeit numpy_in_matrix = np.array(in_matrix)  

#転置関数turn_matrix5()  
def turn_matrix5(matrix):  
    return matrix.T  

#処理時間の測定  
%timeit -r 10 -n 10000 turn_matrix5(numpy_in_matrix)  

実行結果
in_matrixからnumpy.arrayへの変換
486 µs ± 10.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

転置
1.67 µs ± 38.9 ns per loop (mean ± std. dev. of 10 runs, 10000 loops each)

※関数化しない場合

import numpy as np  

turned_matrix = np.array(in_matrix).T  

Pythonリスト型の二次元配列の行と列を入れ替える(転置) | note.nkmk.me       

6-6. Pandasによる転置

関数名:turn_matrix6()

import pandas as pd  

#転置する行列in_matrixの作成  
n = 100  
m = 100  
in_matrix = [[i for i in range(n)] for j in range(m)]  

#in_matrixからpandas.DataFrameのpandas_in_matrixの作成  
%timeit pandas_in_matrix = pd.DataFrame(in_matrix)  

#転置関数turn_matrix6()  
def turn_matrix5(matrix):  
    return matrix.T  

#処理時間の測定  
%timeit -r 10 -n 10000 turn_matrix5(pandas_in_matrix)  

実行結果
・in_matrixからpandas.DataFrameへの変換
6.19 ms ± 43.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

・転置
86.3 µs ± 4.54 µs per loop (mean ± std. dev. of 10 runs, 10000 loops each)

※関数化しない場合

import pandas as pd  

turned_matrix = pd.DataFrame(in_matrix).T  

Pythonリスト型の二次元配列の行と列を入れ替える(転置) | note.nkmk.me

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@totang8mの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう