NumPy配列の対角成分を完全マスター!diag・diagonalの使い方と対角行列作成術

 

NumPyで行列の対角成分を扱う操作は、線形代数や機械学習において頻繁に使用されます。本記事では、numpy.diag()numpy.diagonal()を使った対角成分の抽出と対角行列の作成方法を、実践的なサンプルコードとともに詳しく解説します。

対角成分・対角行列とは?

  • 対角成分: 行列の主対角線上の要素(行番号と列番号が等しい要素)
  • 対角行列: 主対角線以外のすべての要素が0である正方行列

1. numpy.diag() – 万能な対角操作関数

numpy.diag()は、入力によって2つの異なる動作をする多機能な関数です。

1-1. 行列から対角成分を抽出

import numpy as np

# 3x3行列を作成
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 主対角成分を抽出
diagonal = np.diag(matrix)
print(diagonal)  # [1 5 9]

1-2. 1次元配列から対角行列を作成

# 1次元配列から対角行列を生成
arr = np.array([1, 5, 9])
diag_matrix = np.diag(arr)
print(diag_matrix)
# [[1 0 0]
#  [0 5 0]
#  [0 0 9]]

1-3. オフセット(k)による副対角線の操作

# 上の副対角線を抽出(k=1)
upper_diag = np.diag(matrix, k=1)
print(upper_diag)  # [2 6]

# 下の副対角線を抽出(k=-1)
lower_diag = np.diag(matrix, k=-1)
print(lower_diag)  # [4 8]

1-4. オフセット指定での対角行列作成

# 上の副対角線に値を配置
arr = np.array([2, 6])
upper_diag_matrix = np.diag(arr, k=1)
print(upper_diag_matrix)
# [[0 2 0]
#  [0 0 6]
#  [0 0 0]]

# 下の副対角線に値を配置
arr = np.array([4, 8])
lower_diag_matrix = np.diag(arr, k=-1)
print(lower_diag_matrix)
# [[0 0 0]
#  [4 0 0]
#  [0 8 0]]

2. numpy.diagonal() – 対角成分抽出専用関数

numpy.diagonal()は対角成分の抽出に特化した関数で、より高度な操作が可能です。

2-1. 基本的な使用方法

# 基本的な対角成分抽出
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

diagonal = np.diagonal(matrix)
print(diagonal)  # [1 5 9]

2-2. 多次元配列での対角成分抽出

# 3次元配列での対角成分抽出
tensor = np.random.rand(2, 3, 3)
print("3次元配列の形状:", tensor.shape)

# 最後の2次元で対角成分を抽出
diagonals = np.diagonal(tensor, axis1=-2, axis2=-1)
print("対角成分の形状:", diagonals.shape)  # (2, 3)

2-3. 軸指定による柔軟な抽出

# 4x4行列での軸指定
large_matrix = np.arange(16).reshape(4, 4)
print("元の行列:")
print(large_matrix)

# axis1=0, axis2=1で対角成分抽出
diag_01 = np.diagonal(large_matrix, axis1=0, axis2=1)
print("対角成分:", diag_01)  # [0 5 10 15]

3. 実践的な活用例

3-1. 単位行列の作成

# 単位行列を作成
identity = np.diag(np.ones(4))
print(identity)
# [[1. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [0. 0. 1. 0.]
#  [0. 0. 0. 1.]]

# より簡潔な方法
identity_simple = np.eye(4)
print(np.array_equal(identity, identity_simple))  # True

3-2. 対角成分の操作

# 対角成分の値を変更
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# 対角成分を2倍にする
np.fill_diagonal(matrix, np.diag(matrix) * 2)
print(matrix)
# [[ 2  2  3]
#  [ 4 10  6]
#  [ 7  8 18]]

3-3. トレース(対角和)の計算

# 行列のトレースを計算
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

trace_manual = np.sum(np.diag(matrix))
trace_builtin = np.trace(matrix)
print(f"手動計算: {trace_manual}")    # 15
print(f"組み込み関数: {trace_builtin}")  # 15

3-4. 対角優勢行列の作成

# 対角優勢行列(対角成分が他の要素の和より大きい)
n = 4
base_matrix = np.random.rand(n, n)
# 各行の非対角成分の和を計算し、対角成分をそれより大きくする
for i in range(n):
    row_sum = np.sum(np.abs(base_matrix[i])) - np.abs(base_matrix[i, i])
    base_matrix[i, i] = row_sum + 1

print("対角優勢行列:")
print(base_matrix)

4. 非正方行列での対角操作

4-1. 長方形行列の対角成分

# 3x5の長方形行列
rect_matrix = np.arange(15).reshape(3, 5)
print("長方形行列:")
print(rect_matrix)

# 対角成分を抽出(最小次元まで)
diag_rect = np.diag(rect_matrix)
print("対角成分:", diag_rect)  # [0 6 12]

4-2. 異なるサイズでの対角行列作成

# 4x6の対角行列を作成
arr = np.array([1, 2, 3, 4])
diag_46 = np.zeros((4, 6))
np.fill_diagonal(diag_46, arr)
print("4x6対角行列:")
print(diag_46)

5. パフォーマンス比較とベストプラクティス

5-1. 大規模行列での性能比較

import time

# 大規模行列での性能テスト
large_matrix = np.random.rand(1000, 1000)

# numpy.diag()での抽出
start = time.time()
diag_result1 = np.diag(large_matrix)
time1 = time.time() - start

# numpy.diagonal()での抽出
start = time.time()
diag_result2 = np.diagonal(large_matrix)
time2 = time.time() - start

print(f"np.diag(): {time1:.6f}秒")
print(f"np.diagonal(): {time2:.6f}秒")

5-2. メモリ効率的な操作

# インプレース操作での対角成分変更
matrix = np.random.rand(5, 5)
original_matrix = matrix.copy()

# 対角成分を直接変更(メモリ効率的)
matrix.flat[::matrix.shape[1]+1] *= 2

print("対角成分が2倍になった行列:")
print(matrix)

6. よくあるエラーと対処法

6-1. 次元エラーの対処

# 1次元配列での誤った使用例
arr_1d = np.array([1, 2, 3])
try:
    # これはエラーにならず、対角行列を作成
    result = np.diag(arr_1d)
    print("1次元配列 → 対角行列:")
    print(result)
except Exception as e:
    print(f"エラー: {e}")

6-2. オフセット範囲外の対処

# オフセットが範囲外の場合
matrix = np.array([[1, 2], [3, 4]])
large_offset = np.diag(matrix, k=5)  # 空の配列が返される
print("範囲外オフセット結果:", large_offset)  # []

7. 応用例:固有値問題での活用

# 対称行列の固有値分解での対角化
symmetric_matrix = np.array([[4, 2], [2, 3]])
eigenvals, eigenvecs = np.linalg.eigh(symmetric_matrix)

# 固有値で対角行列を作成
diagonal_eigenvals = np.diag(eigenvals)
print("固有値対角行列:")
print(diagonal_eigenvals)

# 対角化の確認
reconstructed = eigenvecs @ diagonal_eigenvals @ eigenvecs.T
print("元の行列との差:", np.allclose(symmetric_matrix, reconstructed))

まとめ

NumPyの対角操作関数を適切に使い分けることで、効率的な行列計算が可能になります。

主要なポイント:

  • np.diag(): 対角成分抽出と対角行列作成の万能関数
  • np.diagonal(): 多次元配列での柔軟な対角成分抽出
  • オフセット(k)パラメータで副対角線も操作可能
  • 非正方行列でも適切に動作

これらの関数を使いこなすことで、線形代数計算、機械学習、科学計算における様々な問題を効率的に解決できるでしょう。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座