【Python】NumPyで行列操作を自由に使いこなす

NumPyはPythonで数値計算を行うための基盤ライブラリであり、特に行列操作は機械学習や科学技術計算で必須のスキルです。

今回は、NumPyで行列を操作する際に必要な基本操作から線形代数の計算まで、網羅的にまとめます。

本記事の内容

  • 行列の作成(単位行列、零行列、対角行列)
  • 行列の結合・分割
  • 行列の演算(積、転置、逆行列)
  • 固有値分解・特異値分解

単位行列を作成

NumPyで単位行列を作成するには、identity 関数と eye 関数を使います。

import numpy as np

# identity: 正方単位行列
I3 = np.identity(3)
print("identity(3):")
print(I3)

# eye: より柔軟(非正方行列も可、対角オフセット可)
E = np.eye(3, 4)
print("\neye(3, 4):")
print(E)

# 対角をずらした行列
E_k1 = np.eye(4, k=1)
print("\neye(4, k=1):")
print(E_k1)

特殊な行列の作成

import numpy as np

# 零行列
Z = np.zeros((3, 4))
print("zeros(3, 4):")
print(Z)

# 全要素が1の行列
O = np.ones((2, 3))
print("\nones(2, 3):")
print(O)

# 対角行列
D = np.diag([1, 2, 3, 4])
print("\ndiag([1, 2, 3, 4]):")
print(D)

# 既存行列から対角要素を抽出
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
diag_elements = np.diag(A)
print(f"\ndiag(A): {diag_elements}")  # [1, 5, 9]

# ランダム行列
R = np.random.randn(3, 3)
print("\nrandom matrix:")
print(R.round(3))

行列同士を結合

NumPyで行列同士を結合するためには、以下の関数を利用します。

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# vstack: 縦方向に結合
V = np.vstack([A, B])
print("vstack:")
print(V)

# hstack: 横方向に結合
H = np.hstack([A, B])
print("\nhstack:")
print(H)

# concatenate: axis指定で結合
C0 = np.concatenate([A, B], axis=0)  # vstack と同じ
C1 = np.concatenate([A, B], axis=1)  # hstack と同じ
print(f"\nconcatenate(axis=0):\n{C0}")
print(f"\nconcatenate(axis=1):\n{C1}")

# block: ブロック行列の作成
block_matrix = np.block([
    [A, B],
    [B, A]
])
print("\nblock matrix:")
print(block_matrix)

行列の分割

import numpy as np

M = np.arange(12).reshape(3, 4)
print("元の行列:")
print(M)

# 列方向に2分割
left, right = np.hsplit(M, 2)
print(f"\nhsplit left:\n{left}")
print(f"hsplit right:\n{right}")

# 行方向に指定位置で分割
top, bottom = np.vsplit(M, [1])
print(f"\nvsplit top:\n{top}")
print(f"vsplit bottom:\n{bottom}")

行列の転置

import numpy as np

A = np.array([[1, 2, 3], [4, 5, 6]])
print(f"A:\n{A}")
print(f"A.shape: {A.shape}")

# 転置(3通りの方法)
print(f"\nA.T:\n{A.T}")
print(f"np.transpose(A):\n{np.transpose(A)}")
print(f"A.T.shape: {A.T.shape}")

行列の積

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# 行列積(3通りの方法)
print(f"A @ B:\n{A @ B}")
print(f"np.dot(A, B):\n{np.dot(A, B)}")
print(f"np.matmul(A, B):\n{np.matmul(A, B)}")

# 要素ごとの積(アダマール積)
print(f"\nA * B (element-wise):\n{A * B}")

# ベクトルとの積
v = np.array([1, 2])
print(f"\nA @ v: {A @ v}")

@ 演算子と np.dot はどちらも行列積ですが、@ の方が読みやすく推奨されています。* は要素ごとの積(アダマール積)であることに注意してください。

逆行列と行列式

import numpy as np

A = np.array([[2, 1], [5, 3]])

# 逆行列
A_inv = np.linalg.inv(A)
print(f"A:\n{A}")
print(f"\nA_inv:\n{A_inv}")

# 検証: A @ A_inv = I
print(f"\nA @ A_inv:\n{(A @ A_inv).round(10)}")

# 行列式
det = np.linalg.det(A)
print(f"\ndet(A): {det:.4f}")

# ランク
rank = np.linalg.matrix_rank(A)
print(f"rank(A): {rank}")

# トレース
trace = np.trace(A)
print(f"trace(A): {trace}")

連立方程式を解く

$\bm{A}\bm{x} = \bm{b}$ の形の連立方程式を解きます。

$$ \begin{pmatrix} 2 & 1 \\ 5 & 3 \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \end{pmatrix} = \begin{pmatrix} 4 \\ 7 \end{pmatrix} $$

import numpy as np

A = np.array([[2, 1], [5, 3]])
b = np.array([4, 7])

# 連立方程式の解
x = np.linalg.solve(A, b)
print(f"解: x = {x}")
print(f"検証: A @ x = {A @ x}")

np.linalg.solve は逆行列を明示的に計算するよりも数値的に安定で高速です。

固有値分解

正方行列 $\bm{A}$ の固有値 $\lambda$ と固有ベクトル $\bm{v}$ は以下を満たします。

$$ \bm{A}\bm{v} = \lambda \bm{v} $$

import numpy as np
import matplotlib.pyplot as plt

A = np.array([[3, 1], [1, 2]])

# 固有値分解
eigenvalues, eigenvectors = np.linalg.eig(A)
print(f"固有値: {eigenvalues}")
print(f"固有ベクトル:\n{eigenvectors}")

# 検証: A @ v = lambda * v
for i in range(len(eigenvalues)):
    v = eigenvectors[:, i]
    lam = eigenvalues[i]
    lhs = A @ v
    rhs = lam * v
    print(f"\nlambda_{i} = {lam:.4f}")
    print(f"  A @ v = {lhs.round(4)}")
    print(f"  lambda * v = {rhs.round(4)}")

# 固有ベクトルの可視化
fig, ax = plt.subplots(figsize=(6, 6))

# 単位円上の点にAをかけて変形を可視化
theta = np.linspace(0, 2 * np.pi, 100)
circle = np.array([np.cos(theta), np.sin(theta)])
ellipse = A @ circle

ax.plot(circle[0], circle[1], 'b-', linewidth=1, alpha=0.3, label='Unit circle')
ax.plot(ellipse[0], ellipse[1], 'r-', linewidth=2, label='A @ circle')

# 固有ベクトル
for i in range(2):
    v = eigenvectors[:, i] * eigenvalues[i]
    ax.arrow(0, 0, v[0], v[1], head_width=0.1, head_length=0.05,
             fc='green', ec='green', linewidth=2)
    ax.text(v[0]*1.15, v[1]*1.15, f'lambda={eigenvalues[i]:.2f}', fontsize=11)

ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.legend()
ax.set_title("Eigenvalue Decomposition: A transforms unit circle to ellipse")
plt.tight_layout()
plt.show()

特異値分解(SVD)

任意の $m \times n$ 行列 $\bm{A}$ に対して、特異値分解は以下のように分解します。

$$ \bm{A} = \bm{U} \bm{\Sigma} \bm{V}^T $$

import numpy as np

A = np.array([[1, 2, 3], [4, 5, 6]])
U, S, Vt = np.linalg.svd(A)

print(f"A shape: {A.shape}")
print(f"U shape: {U.shape}")
print(f"S (singular values): {S}")
print(f"Vt shape: {Vt.shape}")

# 復元の検証
Sigma = np.zeros_like(A, dtype=float)
np.fill_diagonal(Sigma, S)
A_reconstructed = U @ Sigma @ Vt
print(f"\nReconstructed A:\n{A_reconstructed.round(10)}")

まとめ

本記事では、NumPyでの行列操作を網羅的に解説しました。

  • 行列の作成: identity, eye, zeros, ones, diag で各種行列を生成
  • 結合・分割: vstack, hstack, concatenate, block で柔軟に結合
  • 行列積: @ 演算子が最も推奨される書き方。* は要素ごとの積
  • 逆行列・行列式: np.linalg.inv, np.linalg.det で計算。連立方程式は solve が安定
  • 固有値分解・SVD: np.linalg.eig, np.linalg.svd で分解。機械学習の基盤