2018/02/01
2020/04/14
numpyの何がすごいのか?【Python3】
「Pythonでデータ解析をするなら、とりあえずnumpy使う」というのは、データサイエンティスト達の間では割と一般常識になりつつあります。でも、実のところnumpyがどういうのものなのか分からない、という方は一定数いるのではないでしょうか。そこで「numpyってすごい有名だけど、何がすごいの?」とか「なんでnumpyをマスターしなきゃいけないんだ?」という疑問を抱いている初心者たち向けに、この記事を書きました。numpyの良さをわかりやすく伝えられるように、通常のPythonコードとnumpyを使った場合のコードを比較しながら解説していきます。
目次
numpyとは何か?
numpyは配列(リスト)計算を高速にするためのPythonのライブラリです。Pythonには標準の機能としてリスト型変数がありますが、numpyはこれを強化したnumpy配列というものを扱います。では、numpy配列のどこがPythonのリストよりも優れているのでしょうか?それは、次の3点に尽きます。
1.処理速度が早くなる。
2.配列の扱い方が柔軟。
3.コードがシンプルで綺麗になる。
1について:そもそもPythonはC言語やJAVAに比べてfor文によるループ処理が非常に遅いのです。C言語はPythonの約100倍早いなんていう報告もされています。つまりCなら1分で終わる処理でもPythonは約2時間もかかってしまう。そこで、numpyを使うことでfor文を削減することが出来、結果としてプログラムの高速化が実現できます。
2について:numpyの配列はPython標準のリストと非常に似ていますが、numpy配列の方が参照したい要素を簡単に参照できたりと使い勝手が良いです。これについては、後ほど詳しく説明します。
3について:for文を使わなくていい、柔軟な配列操作が可能、という特徴によってコードがシンプルで綺麗になります。また、簡単に書けるので頭の中で考えているアルゴリズムをスムーズにコードに起こすことが出来ます。
では、実際に例を交えてnumpyの良さを解説していきます。
二つのリストの和を通してnumpyの便利さを知る
長さが等しいPythonのリスト2つを用意して、対応する要素の和を計算した新しいリストを作ることを考えます。(下図)
これを以下のようにやろうとするとうまくいきません。
listA = [4,5,6,7,8] listB = [1,2,3,4,5] listC = listA + listB #リストをくっつけることになる print(listC) #[4, 5, 6, 7, 8, 1, 2, 3, 4, 5]
標準搭載のリストの和は、要素同士の和ではなく、リストの合体です。今回の目的を達成するには、リストから要素を一つ一つ取り出して、和を取る必要があります。
listA = [4,5,6,7,8] listB = [1,2,3,4,5] listC = [] #リストの初期化 for i in range(5): listC.append(listA[i] + listB[i]) #最後尾に新しく計算した要素を追加 print(listC) #[5, 7, 9, 11, 13]
しかしこの方法だとプログラムが長くて、複雑になってしまいます。また、Pythonはfor文の処理がC言語やJAVAと比べて非常に遅いので、出来ればfor文は使いたくありません。そこでnumpy配列の出番なのです。numpyを使って書くとこのプログラムは次のようになります。
import numpy as np #numpyをnpとしてimport listA = np.array([4,5,6,7,8]) listB = np.array([1,2,3,4,5]) listC = listA + listB #そのまま配列同士を足せば良い print(listC) #[ 5 7 9 11 13]
もちろん和だけでなく、積や差など他の演算も出来ます。
リストの各要素に同じ数を足す
リストの各要素に同じ数を足して、新しいリストを作ることを考えます(下図)。
Pythonリストを使った場合次のようなプログラムになるかと思います。
listA = [4,5,6,7,8] listC = [] for i in range(5): listC.append(listA[i] + 5) print(listC) #[9, 10, 11, 12, 13]
もしくは、もっとスマートに内包表現を使うでしょう。
listA = [4,5,6,7,8] listC = [listA[i] + 5 for i in range(5)] print(listC) #[9, 10, 11, 12, 13]
Pythonリストでも十分シンプルですが、numpyを使った場合はもっとシンプルになります。
import numpy as np #numpyをnpとしてimport listA = np.array([4,5,6,7,8]) listC = listA + 5 #そのまま5を足すだけで良い print(listC) #[ 9 10 11 12 13]
numpyは配列に数値を足すことが出来るのです。また、for文を使う必要がないので処理速度も桁違いに早くなります。
内包表現とnumpy配列の速度の違いを計測
速度の違いがわかりやすく出るように、長さが1億のリストないし配列に対して上記と同じ処理を行います。
Pythonリストの内包表現の場合。
import time #長さが1億のリストを作る listA = list(range(100000000)) startTime = time.time() #測定開始 listC = [listA[i] + 5 for i in range(100000000)] endTime = time.time() #測定終了 print(endTime - startTime) #処理速度を表示 #20.251144886016846
numpy配列の場合。
import numpy as np #numpyをnpとしてimport import time #長さが1億の配列を作る listA = np.array(range(100000000)) startTime = time.time() #測定開始 listC = listA + 5 endTime = time.time() #測定終了 print(endTime - startTime) #処理速度を表示 #1.0118558406829834
numpy配列を使うと約20倍速くなりました。
最大値や標準偏差などの計算が早い
numpy.sum()やnumpy.max()、numpy.std()などを使えば簡単に合計値、最大値、標準偏差などが求められます。
これらは一部Pythonのリストにもある機能ですが、numpyの方が段違いに速い。ではどれくらい速いのか計測してみましょう。
今回は最大値を求めます。まず、通常のPythonリストの場合。
import numpy as np from random import random #乱数を生成するためのモジュール import time #とりあえず乱数が1千万個入ったリストを生成 randList = [random() for i in range(10000000)] #処理 startTime = time.time() #開始時刻 randList_max = max(randList) #最大値を計算 endTime = time.time() #終了時刻 print(endTime - startTime) #処理時間を表示
処理時間は、0.24081087112426758秒でした。
次にnumpy配列の場合。
import numpy as np from random import random #乱数を生成するためのモジュール import time #とりあえず乱数が1千万個入ったリストを生成 randList = [random() for i in range(10000000)] #numpyの配列にする randList = np.array(randList) #処理 startTime = time.time() #開始時刻 randList_max = randList.max() #最大値を計算 endTime = time.time() #終了時刻 print(endTime - startTime) #処理時間を表示
処理時間は0.011199951171875秒にまで短縮。約20倍速くなっています。
numpy配列の要素の参照は便利!
ここまで速さを強調してきましたが、numpyは便利でもあります。特に私が個人的に便利だと思うのが、numpy配列の要素の参照法です。とても柔軟性があり取り出したい要素をすぐに取り出すことが出来ます。感覚としてはR言語を使っている時に近いかもしれません。
例えば、numpyは真偽リストによって配列の要素を参照することが出来るのです。これは一体どういうのことなのか、実際にプログラムを見てみましょう。
import numpy as np listA = np.array([4,5,6,7,8]) cond = np.array([True,False,True,True,False]) print(listA[cond]) #[4 6 7]
上のプログラムはlistAのcondにおけるTRUEの部分だけを表示しているので[4 6 7]になります。
これを普通のPythonリストでやると以下のエラーに弾かれます。
listA = [4,5,6,7,8] cond = [True,False,True,True,False] listA[cond] #TypeError: list indices must be integers or slices, not list
condの記述には比較演算子を使うこともできます。
import numpy as np listA = np.array([4,5,6,7,8]) cond = listA > 6 print(listA[cond]) #[7 8]
condを数字のリストにすることも可能。
import numpy as np listA = np.array([4,5,6,7,8]) cond = [0,4,2] print(listA[cond]) #[4 8 6]
配列の0番目、4番目、2番目の順に参照しています。
行列を扱うのも簡単
numpyを使えば配列の形が自由自在です。1次元の配列を2次元にして行列のようにすることが出来ます。
要素数10個の一次元配列を2行5列の行列に変えるには、.reshape(2,5)をつけるだけです。
import numpy as np listA = np.array([1,2,3,4,5,6,7,8,9,10]) matrix = listA.reshape(2,5) print(matrix) #[[ 1 2 3 4 5] #[ 6 7 8 9 10]]
行列を縦、横に足して和を求めるのも簡単。
import numpy as np listA = np.array([1,2,3,4,5,6,7,8,9,10]) matrix = listA.reshape(2,5) print(matrix.sum(axis=0)) #[ 7 9 11 13 15]
import numpy as np listA = np.array([1,2,3,4,5,6,7,8,9,10]) matrix = listA.reshape(2,5) print(matrix.sum(axis=1)) #[15 40]
axis=0を指定すると行同士の和。1を指定すると列同士の和になります。
まとめ
いかがでしたでしょうか。ここまで読んでくださった方は、numpyがどれだけ便利でどれだけ有用性の有るものかおわかりいただけたかと思います。
実は、numpyは我々がプログラムを書く時に便利なだけではありません。numpyは多くのPythonの主要ライブラリ中でも使われているのです。例えば、データフレームを扱うためのライブラリとして有名なpandasもnumpyで作られています。よってpandasのデータフレーム にもnumpy.sum()などが使えます。
numpyを使うことでfor文によるループとおさらばして、シンプルで高速なプログラムを書けるようになりましょう!
Recommended