6

Numpy Tutorial

7 min read

Numpyは行列計算などの数値計算をを効率的に行うことができるライブラリです。機械学習では必須となる知識なので,このnotebookで基本的な使い方を学びましょう。

ライブラリをインポートします。numpyは慣例的にnpとして扱います。

import numpy as np

1. Pythonの配列とnumpy.ndarrayの違い

まず,以下の2つのPythonの配列uu, vvを考えます。

u = [1, 2, 3, 4, 5]
v = [1, 4, 9, 16, 25]

この2つの配列をベクトルとして操作してみましょう。

これらのベクトルに対して,内積や和を計算する場合,標準のPythonでは自分で内積や和を返す関数を実装する必要があります。しかし,numpyにはすでにこれらの演算を行う関数が実装されています。

配列はnumpyのnp.array()という関数を用いることでnp.ndarrayに変換できます。np.ndarrayはベクトル,行列,テンソルに対して用意された型で,np.ndarrayに対しnumpyの各種関数を適用することで演算を行います。

具体的には,np.dot()で内積,+で和を求めることができます。

u = np.array(u)
v = np.array(v)

print(type(u), type(v))

print(np.dot(u, v))
print(u+v)
<class 'numpy.ndarray'> <class 'numpy.ndarray'>
225
[ 2  6 12 20 30]

行列は以下のようにして作成できます。np.ndarrayの形状はnp.ndarray.shapeで確認できます。ここでは2×3の行列を作成します。

a = np.array(
    [
     [1, 2, 3],
     [4, 5, 6]
    ]
)
print(a.shape)
(2, 3)

2. numpy.ndarrayの基本的な演算

u = np.array([1, 2, 3, 4, 5])
v = np.array([1, 4, 9, 16, 25])
print(u.shape)
print(v.shape)
(5,)
(5,)

2.1. ユニバーサル関数

np.ndarrayの全要素に対して要素ごとに処理を行い,np.ndarrayを返す関数をユニバーサル関数と言います。

2.1.1. 四則演算

np.ndarrayに対してもpythonの基本的な演算子を使って計算することができます。

print(u + v)
print(u - v)
print(u * v)
print(u / v)
print(u**3)
[ 2  6 12 20 30]
[  0  -2  -6 -12 -20]
[  1   8  27  64 125]
[1.         0.5        0.33333333 0.25       0.2       ]
[ 1  4  9 16 25]

計算結果からわかるように,これらは要素ごと,つまりユニバーサルに計算されています。

2.2.2. 条件演算

print(u==3)
print(u==v)
print(u < v)
print(type(u < v))
[False False  True False False]
[ True False False False False]
[False  True  True  True  True]
<class 'numpy.ndarray'>

四則演算と同じように,これらもユニバーサルに計算されています。よって,返り値はbool値のnp.ndarrayとなります。

2.2.3. その他のユニバーサル関数

print(np.sin(u))
print(np.log(u))
print(np.exp(u))
[ 0.84147098  0.90929743  0.14112001 -0.7568025  -0.95892427]
[0.         0.69314718 1.09861229 1.38629436 1.60943791]
[  2.71828183   7.3890561   20.08553692  54.59815003 148.4131591 ]

numpyにはこれら以外にも多数のユニバーサル関数が用意されています。興味があれば調べてみましょう。

2.2. ブロードキャスト

np.ndarrayの二項演算では,2つのnp.ndarrayの次元や長さを自動的に揃えて計算をします。これをブロードキャストといいます。

print(u)
print(3*u)
[1 2 3 4 5]
[ 3  6  9 12 15]

このように,3をnp.ndarray([3, 3, 3, 3, 3])と自動的に変換して計算します。

c = np.array([3, 3, 3, 3, 3])
print(3 * u == c * u)
[ True  True  True  True  True]

ただし,常に自動的に揃えられるわけではないので注意してください。

w = np.array([1, 2])
print(w.shape)
print(u.shape)
print(w * u)
(2,)
(5,)



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-30-9222f8abbfec> in <module>()
      2 print(w.shape)
      3 print(u.shape)
----> 4 print(w * u)


ValueError: operands could not be broadcast together with shapes (2,) (5,) 

長さが2と5のnp.ndarrayはどう揃えればいいか分からないのでエラーとなります。

行列とベクトルに対してもブロードキャストは有効です。

a = np.array(
    [
     [1, 2, 3, 4, 5],
     [6, 7, 8, 9, 0]
    ]
)
print(a.shape)
print(u.shape)
print(a * u)
(2, 5)
(5,)
[[ 1  4  9 16 25]
 [ 6 14 24 36  0]]

2.3. その他の基本的な関数

print(np.max(u))
print(np.sum(u))
print(np.mean(u))
print(np.std(u))
# np.concatenateによってnp.ndarrayを結合できます。
print(np.concatenate([u, v]))
5
15
3.0
1.4142135623730951
[ 1  2  3  4  5  1  4  9 16 25]

3. numpy.ndarrayの操作

u_list = [1, 2, 3, 4, 5]
u = np.array(u_list)
a = np.array(
    [
     [1, 2, 3, 4, 5],
     [6, 7, 8, 9, 10],
     [11, 12, 13, 14, 15]
    ]
)
print(u.shape)
print(a.shape)
(5,)
(3, 5)

3.1. インデックスを用いた値の取得

np.ndarrayでもpythonの配列と同じように[]を使うことによって,任意の位置の値を取得できます。行列では行の位置と列の位置を指定することで値を取得できます。インデックスは0から始まることに注意してください。

print(u[2])
print(u[-2])

print(a[0, 4])
3
4
5

np.ndarrayでは,複数のインデックスをリストにして指定することで複数の値を同時に取得できます。これはpythonの配列ではエラーとなります。

print(u[[0, 1, 3]])
print(u_list[[0, 1, 3]])
[1 2 4]



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-43-68bebaed8f33> in <module>()
      1 print(u[[0, 1, 3]])
----> 2 print(u_list[[0, 1, 3]])


TypeError: list indices must be integers or slices, not list

二次元配列(行列)では,np.ix_を用いることで各軸のインデックスを別々に指定できます。以下の例では[0, 1], [0, 3], [2, 1], [2, 3]に対応する要素を部分行列として抽出できます。

print(a[np.ix_([0, 2], [1, 3])])
[[ 2  4]
 [12 14]]

3.2. スライス

np.ndarrayは基本的に,pythonの配列と同じようにstart: end: stepでスライスすることができます。

print(u)
print('[:3:2]', u[:3:2])
print('[1::2]', u[1::2])
print('[1:3:]', u[1:3:])
print('[1:3]', u[1:3])
print('[1:]', u[1:])
[1 2 3 4 5]
[:3:2] [1 3]
[1::2] [2 4]
[1:3:] [2 3]
[1:3] [2 3]
[1:] [2 3 4 5]

二次元配列(行列)では,それぞれの軸(行と列)に対するスライスを指定します。

print(a[0:2, 0:3])
[[1 2 3]
 [6 7 8]]

3.3. ブールインデックス参照

インデックスにbool値のnp.ndarrayや配列を指定すると,Trueに対応する要素だけを抽出したnp.ndarrayを返します。

bool_idx_1d = [False, False, True, False, True]
print(u[bool_idx_1d])

bool_idx_2d = np.ix_([True, False, True], [False, False, True, False, True])
print(a[bool_idx_2d])
[3 5]
[[ 3  5]
 [13 15]]

条件演算子と合わせて用いることで,特定の条件を満たす要素のみを抽出できます。

print(u[u > 3])
[4 5]

4. おわりに

ここで紹介したnumpyの機能はnumpyでできることのうちのほんの一部です。numpyは長い開発の歴史があり,すべての機能を網羅するには公式のドキュメントを読む必要があります。

しかし,実際にはnumpyの機能をすべて覚える必要は全くありません。なぜなら,分からなければその都度調べれば良いからです。データ分析などを実行するにはnumpy以外にもたくさんのライブラリを使えるようになる必要があります。そのため,滅多に使わないようなnumpyの細かい機能を学ぶよりも前に,その他の各種ライブラリの基本的な使い方を学ぶことをおすすめします。

5. 参考書籍

Pythonデータサイエンスハンドブック

Discussion

コメントにはログインが必要です。