はじめに¶
僕は一応Pythonが母語で,C言語はできないことはない,という設定で生きています.
Pythonは書きやすいですが,実行速度にかなり難がありますよね.
スピードが肝になってくるプログラムだとCで書き直さなければいけない局面がでてくるかと思います.
しかし,できればPythonで完結したいというのが本音.
Pythonの利点の「書きやすさ」というのは時に,やたら冗長なコードを書いてしまう原因にもなるかと思います.
Pythonの高速化手法はいろいろ紹介されていますが,自分でコード書いて試してみるのが一番かなと思い,
今回から数回をかけて実際に手を動かして,速度比較実験をしていきたいと思います.
別に体系的にまとめるつもりはないので,思いついたら順次記事にしていく予定です.
今回は,map
関数系の処理速度について比較してみました.
実験内容¶
- リストの各要素に対して同じ関数を適用
- 関数の返り値を各要素とする配列を作成
float()
関数で実験
つまり,あるリストの各要素を全部float
型にする操作ってこと!!
さっそく思いつく限りの手を試す.
#純粋listだけでなく,numpy arrayも用いる
import numpy as np
#適当に用意
py_list = list(range(10**7)) #以下,「純粋list」と呼びます
np_list = np.arange(10**7) #以下,「numpy配列」と呼びます
#np_listというネーミングには突っ込まないで
純粋listに対し,list内包表記¶
%%time
hoge = [float(i) for i in py_list]
numpy配列に対し,list内包表記¶
%%time
hoge = [float(i) for i in np_list]
純粋listに対し,map関数¶
%%time
hoge = list(map(float, py_list))
numpy配列に対し,map関数¶
%%time
hoge = list(map(float, np_list))
numpy.frompyfunc()¶
任意の関数をnupmyのユニバーサル関数化する関数frompyfunc()
を利用する.
引数は,(func=関数,nin=その関数の引数の数,nout=その関数の返り値の数)
%%time
f = np.frompyfunc(float, 1, 1)
hoge = f(np_list)
numpy.vectorize()¶
frompyfunc
と同様に,任意の関数をユニバーサル関数化することができる.
詳しくは,numpy.vectorize — NumPy v1.15 Manual
ドキュメントには,
The vectorize function is provided primarily for convenience, not for performance. The implementation is essentially a for loop.
とある.
つまり,vectorize()
はあくまでコードの利便性を高めるだけで,パフォーマンスは高めないよと.本質的にはfor文を呼んでいるだけらしい.
%%time
f = np.vectorize(float)
hoge = f(np_list)
まとめ¶
方法 | 実行時間(s) | 順位 |
---|---|---|
純粋listに対し,list内包表記 | 1.38 | 4 |
numpy配列に対し,list内包表記 | 1.74 | 6 |
純粋listに対し,map関数 | 1.18 | 2 |
numpy配列に対し,map関数 | 1.47 | 5 |
numpy.frompyfunc() | 0.93 | 1 |
numpy.vectorize() | 1.31 | 3 |
numpy.frompyfunc()
を利用するもの一番高速という結果となった.
次点が純粋listに対するmap
関数の適用であるので,単純な操作については一概にnumpy
を使うのが良いとは言えないのかもしれない.
リストのサイズによる速度差については,python – Most efficient way to map function ver numpy array – Stack Overflow にまとめられている.
今回の実験は,あまり体系的になっていないので,今後より詳細に検討していくつもり.
書きやすいPythonだからこそ,各変数の型については常に意識しておくべきだと思った.