Pythonで行列とベクトルの積を計算する

行列とリストの積

Pythonで行列とリストの積の計算をします。線形写像を扱うときには必要になるものです。

リストを使った行列の積の計算

はじめに、行列とリストを定義します。行列m_xは3×3の行列とします。一方、ベクトルはm_vのように3×1の行列(2次元の配列)として定義する方法と、単純にv_vのように1次元の配列として定義する方法があります。

リストを使った行列とベクトルを定義
  1. m_x =[[1, 2, 3],
  2. [4, 5, 6],
  3. [7, 8, 9]]
  4. m_v=[[1],[2],[3]]
  5. v_v=[1,2,3]

行列とベクトルの積と計算

行列とベクトルの積を計算する際に、行列の積のプログラムを使う場合は、2次元の配列であるm_vを使います。なお、行列の積の計算の詳細は次の通りです。

ベクトルを2次元の配列として計算する方法

行列m_xと2次元の配列m_vの積を計算します。

2次元の行列と単位行列の積の計算
  1. l_mult=[]
  2. for row in m_x:
  3. l_temp=[]
  4. for col in zip(*m_v):
  5. elm=0
  6. for row_nth,col_nth in zip(row,col):
  7. elm+=row_nth*col_nth
  8. l_temp.append(elm)
  9. l_mult.append(l_temp)
  10. l_mult

[[14], [32], [50]]

正しく計算されましたが、2次元の配列になっているのでみづらいところがあります。

ベクトルを1次元の配列として計算する方法

行列m_xと1次元の配列v_vの積を計算するときはプログラムを変更する必要があります。

1次元の行列と単位行列の積の計算
  1. l_mult=[]
  2. for row in m_x:
  3. elm=0
  4. for row_nth,col_nth in zip(row,v_v):
  5. elm+=row_nth*col_nth
  6. l_mult.append(elm)
  7. l_mult

[14, 32, 50]

簡単なプログラムになりますが、行列×行列、行列×べjy地ryと使い分けるのは手間がかかります。

n×1の行列と1次元のベクトルを相互に変換する

前節の面倒を避けるために、行列とベクトルの相互変換を考えます。

1次元のベクトルから2次元の行列への変換

v_vのように1次元のベクトルに対して#2のプログラムを適用すると上手く動きません。そこで、v_vをいったんn×1の2次元の配列に変換することを考えます。

for文による方法

はじめに最も簡単なプログラムを作成します。

1次元のベクトルから2次元の行列への変換
  1. m_v2=[]
  2. for elm in v_v:
  3. m_v2.append([elm])
  4. m_v2

[[1], [2], [3]]

リスト内包表記、ラムダ式による方法

リスト内包表記によると1行で変換することができます。

リスト内包表記による方法
  1. m_v3=[[elm] for elm in v_v]
  2. m_v3

ラムダ式でも計算することができます。

ラムダ式による方法
  1. list(map(lambda l :[l] , v_v))

結果は同じです。

2次元の行列から1次元のベクトルへの変換

#2のプログラムの結果は2次元の配列で返されるので、次の処理を考え1次元の配列に変換します。

for文による方法

for文で単純にリストを順次取り出して、新たにリストを作成します。

1次元のベクトルから2次元の行列への変換
  1. v_v2=[]
  2. for inner in m_v:
  3. for num in inner:
  4. v_v2.append(num)
  5. v_v2

[1, 2, 3]

リスト内包表記による方法

上記の方法に対してリスト内包表記を使います。

リスト内包表記による方法
  1. v_v3=[elm for inner in m_v for elm in inner]
  2. v_v3

1行で計算できたうえに、高速化が見込めます。

sum関数を使った裏技

sum関数を使うと簡単に計算することができます。

sum関数の引数に2次元のベクトルと空のリストを適用すると、1次元の配列になります。空のリストは必ず右側に指定します。

sum関数による方法
  1. v_v4=sum(m_v,[])
  2. v_v4

とても不思議な感じがしますが、次の方法の応用です。

extendメソッドによる方法
  1. v_v5 = []
  2. for inner in m_v:
  3. v_v5.extend(inner)
  4. v_v5

さらに裏技になりますが、extendメソッドをあらかじめ定義しておくとさらに高速化が期待されます。

  1. v_v6=[]
  2. ex=v_v6.extend
  3. for inner in m_v:
  4. ex(inner)
  5. v_v6

NumPyによる行列とベクトルの積の計算

NumPyのndarray形式の行列とベクトルの積を計算します。行列はnm_aは2次元の配列、nm_vは2次元のベクトル、mv_vは1次元の配列として定義します。NumPyの場合は@演算子とdot関数で計算結果が異なります。

NumPyのndarray形式での行列とベクトルの定義

NumPyでも行列nm_aと、2次元のベクトルnm_vおよび1次元のベクトルnv_vを定義します。

  1. import numpy as np
  2. nm_a = np.array([[1, 2, 3],
  3. [4, 5 ,6],
  4. [7, 8 ,9]])
  5. nm_v= np.array([[1],[2],[3]])
  6. nv_v= np.array([1,2,3])

@演算子による方法

  1. nm_a@nm_v

array([14, 32, 50])

2次元の配列との積を計算しても1次元のベクトルとして計算してくれます。

  1. nm_a@nv_v

array([14, 32, 50])

1次元の配列との積を計算した場合、1次元のベクトルとして計算されるので結果は同じです。

dot関数による方法

  1. np.dot(nm_a,nm_v)

array([[14],
       [32],
       [50]])

dot関数を使った場合、2次元の配列として計算されるので@と異なります。

  1. np.dot(nm_a,nv_v)

array([14, 32, 50])

1次元のベクトルを使うと当然、結果も1次元のベクトルになります。

SymPyによる行列とベクトルの積の計算

SymPyによっても行列とベクトルの積を計算します。

SymPyモジュールによる定義

SymPyモジュールでは、dotメソッドを使うと行列とベクトルの積を1次元のリストで返します。

  1. import sympy
  2. sm_x = sympy.Matrix([[1, 2, 3],
  3. [4, 4 ,6],
  4. [7, 8 ,9]])
  5. sm_v= sympy.Matrix([[1], [2], [3]])
  6. sv_v= sympy.Matrix([1, 2, 3])

dotメソッド

  1. sm_x.dot(sm_v)

[14, 30, 50]

  1. sm_x.dot(sv_v)

[14, 30, 50]