まくまくPythonノート
NumPy 配列 (ndarray) で行列を扱う
2017-01-01

NumPy 配列の生成

NumPy には配列(行列)を高速に扱うための ndarray クラスが用意されています。 ndarray インスタンスは、numpy.array 関数で生成することができます。 下記の例では、2行3列の行列を表現する ndarray インスタンスを生成しています。

>>> import numpy as np
>>> a = np.array([[1, 2, 3], [4, 5, 6]])

>>> a
array([[1, 2, 3],
       [4, 5, 6]])

>>> type(a)
<type 'numpy.ndarray'>

NumPy 配列の形状(各次元のサイズ)は、shape 属性を参照することで調べることができます。

>>> a.shape
(2, 3)    # 2行3列ということ

行列として扱うには、各行のサイズ(列数)は揃えておく必要があります。 次のように、異なるサイズで初期化してしまうと、リストオブジェクトを要素に持つ一次元の配列として扱われてしまいます。

>>> a = np.array([[1, 2, 3], [4, 5]])
>>> a
array([[1, 2, 3], [4, 5]], dtype=object)
>>> a.shape
(2,)     # 1次元で要素数2つということ

NumPy 配列は1つのデータタイプの要素のみを持つ

効率化のため、NumPy 配列に格納される要素の型(データタイプ)は統一されます。 データタイプは ndarray インスタンスの初期化時の要素の値によって自動的に設定されます。 ndarray インスタンスが保持している要素のデータタイプを調べるには、dtype 属性を参照します。

>>> a = np.array([[1, 2, 3], [4, 5, 6]])  # 整数値のみで初期化
>>> a.dtype
dtype('int64')

>>> a = np.array([[1, 2, 3], [1.5, 2.5, 3.5]])  # 浮動小数点数を含む値で初期化
>>> a.dtype
dtype('float64')

データタイプは、明示的に指定することもできます。 次の例では、整数値のみで NumPy 配列を初期化していますが、浮動小数点数の要素として扱うように指定しています。

>>> a = np.array([[1, 2, 3], [4, 5, 6]], dtype='float64')
>>> a.dtype
dtype('float64')
>>> a
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

零行列や単位行列を作成する

零行列や単位行列を作成したいときは、np.array 関数の代わりに、np.zerosnp.identity を使用します。

>>> np.zeros((2, 3))
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

>>> np.ones((2, 3))
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])

>>> np.identity(3)
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])

NumPy 配列に対する演算(ブロードキャスト)

NumPy 配列 (ndarray) に対して四則演算を行うと、全ての要素に対して演算した結果が NumPy 配列として返されます(この仕組みをブロードキャストと呼びます)。 これは非常に強力な仕組みで、この仕組みをうまく活用することで、行列演算を行う関数などを、ループ処理を使わずに実装することができます。

>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> a + 1
array([[2, 3, 4],
       [5, 6, 7]])

>>> a - 1
array([[0, 1, 2],
       [3, 4, 5]])

>>> a * 2
array([[ 2,  4,  6],
       [ 8, 10, 12]])

>>> a / 2
array([[ 0.5,  1. ,  1.5],
       [ 2. ,  2.5,  3. ]])  # Python2 ではデータタイプは int64 になる

>>> a ** 2
array([[ 1,  4,  9],
       [16, 25, 36]])

NumPy 配列同士の四則演算も、それぞれ対応する要素に対して演算が行われます。

>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> a + a
array([[ 2,  4,  6],
       [ 8, 10, 12]])

>>> a - a
array([[0, 0, 0],
       [0, 0, 0]])

>>> a * a
array([[ 1,  4,  9],
       [16, 25, 36]])

>>> a / a
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])

>>> a ** a
array([[    1,     4,    27],
       [  256,  3125, 46656]])

形状の異なる NumPy 配列同士で演算しようとすると、ValueError が発生します。

>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> b = np.array([[1, 2], [3, 4]])
>>> a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (2,3) (2,2)

ただし、1次元の NumPy 配列と演算処理を行うと、ブロードキャストの仕組みがうまく働いて演算処理が成功します(単独の数値との演算がうまくいくのと同じ仕組みです)。

>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> b = np.array([1, 2, 3])
>>> a * b
array([[ 1,  4,  9],
       [ 4, 10, 18],
       [ 7, 16, 27]])

行列演算を行う(内積、外積、転置)

内積、外積、転置などの演算用には、専用のメソッドが用意されています。

内積 (dot)

>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> b = np.array([1, 2, 3])
>>> a.dot(b)
array([14, 32])

外積 (cross)

>>> a = (1, 2, 3)
>>> b = (10, 10, 10)
>>> np.cross(a, b)
array([-10,  20, -10])

転置 (transpose)

>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> a.transpose()
array([[1, 4],
       [2, 5],
       [3, 6]])
2017-01-01