NumPyのnp.fill_diagonal徹底解説:使い方からエラー、代替手段まで

2025-05-16

numpy.fill_diagonal() とは

numpy.fill_diagonal(a, val, wrap=False)

この関数は、NumPy配列 a の主対角(および、a が2次元以上の場合には、オフセットされた対角)を指定された値 val で埋めます。

引数

  • wrap: オプション(デフォルトは False)。
    • False の場合: 配列が非正方行列(例: 2x3 や 3x2)の場合、対角は配列の境界内で終了します。つまり、最も短い辺の長さに合わせて対角が埋められます。
    • True の場合: 配列が非正方行列の場合でも、対角は「折り返され」て埋められます。これにより、配列全体にわたって対角パターンが繰り返されます。
  • val: 対角要素に設定する値。スカラー値または配列を指定できます。配列を指定した場合、その要素が対角に沿って繰り返されます。
  • a: 対角を埋めたい配列。NumPyのndarrayである必要があります。

具体的な動作

    • 主対角(インデックス (i, i) の要素)が val で埋められます。
    • wrap=False の場合、配列が正方行列でなければ、対角は配列の最も短い辺の長さに合わせて途中で終わります。
    • wrap=True の場合、非正方行列でも対角は配列の境界で折り返され、要素を繰り返し埋めます。
  1. 3次元以上の配列の場合

    • 最後の2つの軸に沿って対角が埋められます。例えば、3次元配列 (d1, d2, d3) の場合、各 (i, j) スライスに対して (i, i) の位置が埋められます。

基本的な2次元配列での使用 (正方行列)

import numpy as np

a = np.zeros((3, 3), dtype=int)
print("元の配列:\n", a)

# 対角を1で埋める
np.fill_diagonal(a, 1)
print("\nfill_diagonal(a, 1) 後:\n", a)

出力

元の配列:
 [[0 0 0]
 [0 0 0]
 [0 0 0]]

fill_diagonal(a, 1) 後:
 [[1 0 0]
 [0 1 0]
 [0 0 1]]

非正方行列での使用 (wrap=False の場合)

import numpy as np

b = np.zeros((2, 4), dtype=int)
print("元の配列:\n", b)

# 対角を5で埋める (wrap=False なので、短い方の辺の長さに合わせて2つの要素が埋まる)
np.fill_diagonal(b, 5)
print("\nfill_diagonal(b, 5) 後 (wrap=False):\n", b)

出力

元の配列:
 [[0 0 0 0]
 [0 0 0 0]]

fill_diagonal(b, 5) 後 (wrap=False):
 [[5 0 0 0]
 [0 5 0 0]]

非正方行列での使用 (wrap=True の場合)

import numpy as np

c = np.zeros((2, 4), dtype=int)
print("元の配列:\n", c)

# 対角を5で埋める (wrap=True なので、配列全体にわたって折り返される)
np.fill_diagonal(c, 5, wrap=True)
print("\nfill_diagonal(c, 5, wrap=True) 後:\n", c)

出力

元の配列:
 [[0 0 0 0]
 [0 0 0 0]]

fill_diagonal(c, 5, wrap=True) 後:
 [[5 0 0 0]
 [0 5 0 0]]

注意
wrap=True の挙動は、短い方の次元で対角が終了した後、残りの部分が最初の行・列から再開されるというもので、上記の例では (0,2)(1,3) などが埋まるわけではありません。実際には、fill_diagonal は配列の対角に沿って値を入れていきます。wrap=True の影響は、長い次元の終わりに到達したときに、インデックスが短い次元の始まりに戻って「折り返される」という点にあります。しかし、非正方行列でこの動作を直感的に理解するのは少し複雑です。多くの場合、wrap=False がより一般的な用途で使われます。

より正確な wrap=True の動作は、対角線が配列の境界を越えても、インデックスの「最小次元」に沿って継続されるというものです。例えば、a[i,j] の対角線は i=j ですが、wrap=True では j = (i + offset) % width のように計算され、より広い範囲で対角線が埋められます。

val に配列を指定する例

import numpy as np

d = np.zeros((4, 4), dtype=int)
print("元の配列:\n", d)

# 対角を [1, 2, 3] の繰り返しで埋める
np.fill_diagonal(d, [1, 2, 3])
print("\nfill_diagonal(d, [1, 2, 3]) 後:\n", d)

出力

元の配列:
 [[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]

fill_diagonal(d, [1, 2, 3]) 後:
 [[1 0 0 0]
 [0 2 0 0]
 [0 0 3 0]
 [0 0 0 1]]

このように、val に配列を指定すると、その要素が対角に沿って繰り返して使用されます。



配列の次元に関するエラー (ValueError: array must be at least 2-d)

原因
numpy.fill_diagonal() は、少なくとも2次元の配列(行列)に対して設計されています。1次元配列(ベクトル)を渡すと、このエラーが発生します。


import numpy as np

a = np.array([1, 2, 3])
# np.fill_diagonal(a, 0)  # ValueError: array must be at least 2-d

トラブルシューティング
fill_diagonal() は、配列の最後の2つの次元に対して対角線を埋めることを意図しています。1次元配列に対して対角という概念は適用されません。もし1次元配列の特定のインデックスを埋めたいのであれば、直接インデックス指定で値を代入するか、NumPyのより汎用的な関数(例: np.where など)を使用することを検討してください。

val (埋める値) の型に関する誤解

原因
val 引数には、スカラー値または配列ライクなオブジェクト(リストやNumPy配列など)を指定できます。しかし、val が配列ライクな場合に、その要素が対角要素の数に合わないと、期待しない挙動になることがあります。


import numpy as np

a = np.zeros((3, 3))
np.fill_diagonal(a, [1, 2, 3, 4]) # 期待通りに動作するが、最後の4は使われない
print(a)
# 出力:
# [[1. 0. 0.]
#  [0. 2. 0.]
#  [0. 0. 3.]]

b = np.zeros((3, 3))
np.fill_diagonal(b, [1]) # スカラー値のように扱われ、全対角要素が1になる
print(b)
# 出力:
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]]

トラブルシューティング

  • 意図しない繰り返しや無視を避けるために、val の長さが埋めたい対角要素の数に一致するように調整するか、スカラー値を使用してください。
  • val が配列ライクな場合、その要素が対角に沿って繰り返されます。
    • val の長さが対角要素の数より短い場合、val の要素が繰り返して使用されます。
    • val の長さが対角要素の数より長い場合、余分な要素は無視されます。
  • val がスカラー値の場合、すべての対角要素がその値で埋められます。

非正方行列における wrap 引数の誤解

原因
wrap=True は、非正方行列の対角線を「折り返して」埋めるためのものですが、その挙動が直感的でないと感じることがあります。多くの場合、ユーザーは配列全体にわたって対角パターンが繰り返されることを期待しますが、実際はそうではありません。


import numpy as np

a = np.zeros((2, 4), dtype=int)
np.fill_diagonal(a, 5, wrap=True)
print(a)
# 出力:
# [[5 0 0 0]
#  [0 5 0 0]]

この出力は wrap=False の場合と同じであり、wrap=True が期待通りの「折り返し」をしていないように見えるかもしれません。これは、fill_diagonal が「対角線」を概念的に捉えるためです。

トラブルシューティング

  • もし非正方行列の「すべての行/列」に対角のようなパターンを適用したい場合は、fill_diagonal ではなく、より汎用的なループ処理やNumPyのスライシング、または np.diag_indices() などと組み合わせてインデックスを生成する方法を検討してください。
  • wrap=True が真に効果を発揮するのは、対角線が配列の境界を越えても、最小次元に沿って継続される場合など、より複雑なシナリオです。一般的な非正方行列では、wrap=False と同じ結果になることが多く、混乱を招く可能性があります。
  • wrap=True は、対角線が配列の境界に達したときに、インデックスが「折り返され」て対角線の埋め込みを継続することを意味します。しかし、これは非正方行列の場合でも、主対角が配列の最も短い次元の長さに合わせて埋められるという基本的な挙動を変えるものではありません。

3次元以上の配列に対する挙動の誤解

原因
numpy.fill_diagonal() は、3次元以上の配列に対しても使用できますが、その場合、最後の2つの次元に沿って対角が埋められます。つまり、配列の各「スライス」に対して対角が埋められる形になります。ユーザーがすべての次元で共通の対角を期待していると、意図しない結果になることがあります。


import numpy as np

# 2x2x2 の3次元配列
a = np.zeros((2, 2, 2), dtype=int)
print("元の配列:\n", a)

np.fill_diagonal(a, 1)
print("\nfill_diagonal(a, 1) 後:\n", a)

出力

元の配列:
 [[[0 0]
  [0 0]]

 [[0 0]
  [0 0]]]

fill_diagonal(a, 1) 後:
 [[[1 0]
  [0 1]]

 [[0 0]
  [0 0]]]

この場合、a[0, :, :] の対角線が埋められ、a[1, :, :] の対角線は埋められません。

トラブルシューティング

  • もし3次元以上の配列の「すべてのスライス」で対角を埋めたい場合は、ループを使って各2次元スライスに対して fill_diagonal を適用するか、より高度なインデックス操作(例: np.einsumnp.indices など)を検討する必要があります。
  • fill_diagonal() は、N次元配列の a[..., i, i] のように、最後の2つの軸についてのみ対角を埋めます。

インプレース (in-place) 操作であることの理解不足

原因
numpy.fill_diagonal() は、元の配列を直接変更(in-place modification)し、新しい配列を返しません。関数は None を返します。これを理解していないと、結果を変数に代入しようとして「なぜ None なんだ?」と混乱することがあります。


import numpy as np

a = np.zeros((3, 3))
result = np.fill_diagonal(a, 1)
print(result) # None が出力される
print(a)      # a 自体は変更されている
  • 単に np.fill_diagonal(a, val) と呼び出すだけで、元の配列 a が変更されます。
  • fill_diagonal() は副作用を持つ関数であり、a = np.fill_diagonal(a, val) のように代入してはいけません。
  • 小さな配列で試す
    複雑な配列で問題が発生した場合、より小さな簡単な配列で同じ操作を試すことで、挙動を理解しやすくなります。
  • ドキュメントを確認する
    公式のNumPyドキュメントは、関数の詳細な動作、引数、例について最も信頼できる情報源です。
  • エラーメッセージを読む
    NumPyのエラーメッセージは、多くの場合、問題の原因を示唆しています。エラーメッセージを注意深く読み、理解しようとすることが重要です。


例1:基本的な2次元配列(正方行列)の対角を埋める

最も一般的な使用例です。正方行列の主対角を単一の値で埋めます。

import numpy as np

# 3x3 のゼロ行列を作成
matrix_a = np.zeros((3, 3), dtype=int)
print("元の行列 (matrix_a):\n", matrix_a)

# 主対角を 1 で埋める
np.fill_diagonal(matrix_a, 1)
print("\n対角を 1 で埋めた後:\n", matrix_a)

# 別の例:対角を 5 で埋める
matrix_b = np.full((4, 4), 7) # 全要素が7の4x4行列
print("\n元の行列 (matrix_b):\n", matrix_b)

np.fill_diagonal(matrix_b, 5)
print("\n対角を 5 で埋めた後:\n", matrix_b)

出力

元の行列 (matrix_a):
 [[0 0 0]
 [0 0 0]
 [0 0 0]]

対角を 1 で埋めた後:
 [[1 0 0]
 [0 1 0]
 [0 0 1]]

元の行列 (matrix_b):
 [[7 7 7 7]
 [7 7 7 7]
 [7 7 7 7]
 [7 7 7 7]]

対角を 5 で埋めた後:
 [[5 7 7 7]
 [7 5 7 7]
 [7 7 5 7]
 [7 7 7 5]]

例2:非正方行列の対角を埋める(wrap=False

非正方行列の場合、対角線は短い方の次元の長さで終了します。これがデフォルトの挙動です。

import numpy as np

# 2x4 のゼロ行列を作成
matrix_c = np.zeros((2, 4), dtype=int)
print("元の行列 (matrix_c):\n", matrix_c)

# 対角を 9 で埋める (2x4 なので、対角要素は2つ埋まる)
np.fill_diagonal(matrix_c, 9)
print("\n対角を 9 で埋めた後 (wrap=False):\n", matrix_c)

# 4x2 のゼロ行列を作成
matrix_d = np.zeros((4, 2), dtype=int)
print("\n元の行列 (matrix_d):\n", matrix_d)

# 対角を 8 で埋める (4x2 なので、対角要素は2つ埋まる)
np.fill_diagonal(matrix_d, 8)
print("\n対角を 8 で埋めた後 (wrap=False):\n", matrix_d)

出力

元の行列 (matrix_c):
 [[0 0 0 0]
 [0 0 0 0]]

対角を 9 で埋めた後 (wrap=False):
 [[9 0 0 0]
 [0 9 0 0]]

元の行列 (matrix_d):
 [[0 0]
 [0 0]
 [0 0]
 [0 0]]

対角を 8 で埋めた後 (wrap=False):
 [[8 0]
 [0 8]
 [0 0]
 [0 0]]

例3:val に配列を指定して対角を埋める

対角要素に適用する値をリストやNumPy配列で指定できます。その値は対角に沿って繰り返されます。

import numpy as np

# 5x5 のゼロ行列を作成
matrix_e = np.zeros((5, 5), dtype=int)
print("元の行列 (matrix_e):\n", matrix_e)

# 対角を [10, 20, 30] の繰り返しで埋める
np.fill_diagonal(matrix_e, [10, 20, 30])
print("\n対角を [10, 20, 30] で埋めた後:\n", matrix_e)

# 対角の長さ(この場合5)より長い配列を指定しても、余分な要素は使われない
matrix_f = np.zeros((3, 3), dtype=int)
print("\n元の行列 (matrix_f):\n", matrix_f)
np.fill_diagonal(matrix_f, [1, 2, 3, 4, 5])
print("\n対角を [1, 2, 3, 4, 5] で埋めた後:\n", matrix_f)

出力

元の行列 (matrix_e):
 [[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]

対角を [10, 20, 30] で埋めた後:
 [[10  0  0  0  0]
 [ 0 20  0  0  0]
 [ 0  0 30  0  0]
 [ 0  0  0 10  0]
 [ 0  0  0  0 20]]

元の行列 (matrix_f):
 [[0 0 0]
 [0 0 0]
 [0 0 0]]

対角を [1, 2, 3, 4, 5] で埋めた後:
 [[1 0 0]
 [0 2 0]
 [0 0 3]]

例4:3次元配列での使用

3次元以上の配列の場合、fill_diagonal()最後の2つの次元に沿って対角を埋めます。つまり、配列の各「スライス」の主対角が埋められます。

import numpy as np

# 2x3x3 の3次元配列を作成
array_g = np.zeros((2, 3, 3), dtype=int)
print("元の配列 (array_g):\n", array_g)

# 各スライス (array_g[0,:,:] と array_g[1,:,:]) の対角を 1 で埋める
# fill_diagonal は最後の2次元に対して適用されるため、
# array_g[0] の対角と array_g[1] の対角がそれぞれ埋められる
np.fill_diagonal(array_g, 1)
print("\n対角を 1 で埋めた後:\n", array_g)

出力

元の配列 (array_g):
 [[[0 0 0]
  [0 0 0]
  [0 0 0]]

 [[0 0 0]
  [0 0 0]
  [0 0 0]]]

対角を 1 で埋めた後:
 [[[1 0 0]
  [0 1 0]
  [0 0 1]]

 [[1 0 0]
  [0 1 0]
  [0 0 1]]]

解説
上記の3次元配列の例では、np.fill_diagonal(array_g, 1)array_g[0,:,:] の対角と array_g[1,:,:] の対角の両方を埋めています。これは、fill_diagonal が「先頭のインデックスはそのままにし、最後の2つのインデックスに対して対角を埋める」という動作をするためです。

例5:wrap=True の使用(非正方行列での特別な挙動)

wrap=True は、非正方行列の対角線が配列の境界を越えて「折り返す」ことを意味します。しかし、前述の通り、この挙動は直感的ではない場合があり、多くの単純なケースでは wrap=False と同じ結果になります。

import numpy as np

# 2x4 のゼロ行列を作成
matrix_h = np.zeros((2, 4), dtype=int)
print("元の行列 (matrix_h):\n", matrix_h)

# 対角を 5 で埋める (wrap=True)
np.fill_diagonal(matrix_h, 5, wrap=True)
print("\n対角を 5 で埋めた後 (wrap=True):\n", matrix_h)

# 4x2 のゼロ行列を作成
matrix_i = np.zeros((4, 2), dtype=int)
print("\n元の行列 (matrix_i):\n", matrix_i)

# 対角を 6 で埋める (wrap=True)
np.fill_diagonal(matrix_i, 6, wrap=True)
print("\n対角を 6 で埋めた後 (wrap=True):\n", matrix_i)

出力

元の行列 (matrix_h):
 [[0 0 0 0]
 [0 0 0 0]]

対角を 5 で埋めた後 (wrap=True):
 [[5 0 0 0]
 [0 5 0 0]]

元の行列 (matrix_i):
 [[0 0]
 [0 0]
 [0 0]
 [0 0]]

対角を 6 で埋めた後 (wrap=True):
 [[6 0]
 [0 6]
 [0 0]
 [0 0]]

ご覧の通り、これらの単純な非正方行列の例では wrap=Truewrap=False で結果は同じになります。wrap=True は、より複雑な、特に「対角線が物理的な配列の境界を越えても続く」という概念を表現したい場合に限定的に利用されます。一般的な用途では、wrap=False(デフォルト)の理解で十分です。



np.diag_indices() を使用したインデックス指定

np.diag_indices() は、配列の主対角(または特定のオフセットされた対角)のインデックスを生成する関数です。これを使って、直接インデックスを指定して値を代入することができます。

利点

  • より柔軟なインデックス操作ができる。
  • 対角以外の、オフセットされた対角(例えば、主対角の1つ上や1つ下の対角)を埋めることも可能。

欠点

  • 特に大きな配列の場合、インデックス配列を生成するため、fill_diagonal() よりもパフォーマンスが劣る可能性がある(ただし、ほとんどのケースでは大きな差は出ない)。
  • fill_diagonal() よりもコードが少し長くなる。

コード例

import numpy as np

# 2次元配列の場合
a = np.zeros((3, 3), dtype=int)
# 主対角のインデックスを取得
indices = np.diag_indices(a.shape[0])
print("主対角のインデックス:", indices)

# インデックスを使って値を代入
a[indices] = 10
print("\nnp.diag_indices() で対角を埋めた後:\n", a)

# オフセットされた対角 (例えば、主対角の1つ上) を埋める場合
b = np.zeros((4, 4), dtype=int)
# k=1 で主対角の1つ上の対角インデックスを取得
indices_offset = np.diag_indices(b.shape[0], k=1)
print("\nオフセット対角のインデックス (k=1):", indices_offset)
b[indices_offset] = 20
print("\nnp.diag_indices() でオフセット対角を埋めた後:\n", b)

出力

主対角のインデックス: (array([0, 1, 2]), array([0, 1, 2]))

np.diag_indices() で対角を埋めた後:
 [[10  0  0]
 [ 0 10  0]
 [ 0  0 10]]

オフセット対角のインデックス (k=1): (array([0, 1, 2]), array([1, 2, 3]))

np.diag_indices() でオフセット対角を埋めた後:
 [[ 0 20  0  0]
 [ 0  0 20  0]
 [ 0  0  0 20]
 [ 0  0  0  0]]

np.diag() または np.diagflat() と組み合わせる

np.diag() は配列の対角を抽出したり、1次元配列から対角行列を作成したりするために使います。np.diagflat() は、1次元配列から対角要素を持つ2次元配列を作成します。

利点

  • 既存の対角要素を抽出する際に便利。
  • 新しい対角行列を作成したい場合に簡潔。

欠点

  • 非正方行列の場合の挙動が fill_diagonal() と異なる場合がある。
  • 既存の配列の対角を「インプレースで変更する」という fill_diagonal() の直接的な代替にはならない。通常は新しい配列を作成したり、要素ごとの演算を行う必要がある。

コード例

import numpy as np

# 新しい対角行列を作成する例
# 1次元配列から対角行列を作成
diag_values = np.array([1, 2, 3])
new_matrix = np.diag(diag_values)
print("np.diag() で作成した対角行列:\n", new_matrix)

# 既存の配列の対角を置き換える(間接的な方法)
# まずは元の配列と、置き換えたい対角値を用意
original_matrix = np.full((3, 3), 7)
replace_values = np.array([10, 20, 30])

# 対角要素のみを新しい対角行列として作成し、元の行列に加算・減算などで結合
# この方法では、元の配列の非対角要素はそのまま残るが、対角要素は上書きされる
# np.identity を使って対角要素のみ1の行列を作成し、それにreplace_valuesを掛けて、
# その後元の行列から既存の対角要素を引いて結合するなどの複雑な処理が必要
# これは fill_diagonal と直接的な意味で同じではないため、あくまで「代替」として考える。

# 一般的な「置き換え」の方法として、以下のようにもできる
# まず、対角要素をゼロで埋める
original_matrix_copy = original_matrix.copy()
np.fill_diagonal(original_matrix_copy, 0) # fill_diagonal を使って既存の対角を0にする

# 新しい対角行列を作成し、ゼロにした配列に加算
diag_replacement = np.diag(replace_values)
result_matrix = original_matrix_copy + diag_replacement
print("\nnp.diag() と fill_diagonal() を組み合わせて対角を置き換えた後:\n", result_matrix)

# np.diagflat() の例 (1次元配列から2次元対角行列を作成)
flat_values = np.array([5, 6, 7, 8])
flat_matrix = np.diagflat(flat_values)
print("\nnp.diagflat() で作成した対角行列:\n", flat_matrix)

出力

np.diag() で作成した対角行列:
 [[1 0 0]
 [0 2 0]
 [0 0 3]]

np.diag() と fill_diagonal() を組み合わせて対角を置き換えた後:
 [[10  7  7]
 [ 7 20  7]
 [ 7  7 30]]

np.diagflat() で作成した対角行列:
 [[5 0 0 0]
 [0 6 0 0]
 [0 0 7 0]
 [0 0 0 8]]

手動でのインデックス操作 (ループまたはブロードキャスト)

より低レベルな制御が必要な場合、手動でループを回したり、ブロードキャストを利用したりする方法もあります。これは fill_diagonal() が内部的に行っていることの一部を再現するような形になります。

利点

  • fill_diagonal() が対応していないオフセットされた対角や、特定の条件を満たす対角要素のみを埋める場合などに有効。
  • 非常に柔軟で、任意の複雑なパターンを実装できる。

欠点

  • コードが長く、読みにくくなることがある。
  • PythonのループはNumPyのC言語実装に比べて遅くなる可能性がある(特に大規模な配列の場合)。

コード例 (手動ループ)

import numpy as np

matrix_j = np.zeros((3, 3), dtype=int)
for i in range(min(matrix_j.shape)):
    matrix_j[i, i] = 100
print("手動ループで対角を埋めた後:\n", matrix_j)

# 非正方行列の場合も同様に動作する
matrix_k = np.zeros((2, 4), dtype=int)
for i in range(min(matrix_k.shape)):
    matrix_k[i, i] = 200
print("\n手動ループで非正方行列の対角を埋めた後:\n", matrix_k)

出力

手動ループで対角を埋めた後:
 [[100   0   0]
 [  0 100   0]
 [  0   0 100]]

手動ループで非正方行列の対角を埋めた後:
 [[200   0   0   0]
 [  0 200   0   0]]

np.eye() または np.identity() を使用した初期化

もし、対角要素が1で、それ以外の要素が0の「単位行列」を作成したいだけであれば、np.eye() または np.identity() が最もシンプルで効率的な方法です。これらは fill_diagonal() を使うよりも簡潔です。

利点

  • 単位行列の作成に特化しており、非常に高速で簡潔。

欠点

  • 対角要素が1以外の場合や、既存の配列を変更したい場合には直接使えない。

コード例

import numpy as np

# 3x3 の単位行列を作成
identity_matrix = np.eye(3, dtype=int)
print("np.eye() で作成した単位行列:\n", identity_matrix)

# 4x4 の単位行列 (np.identity も同様)
identity_matrix_2 = np.identity(4, dtype=int)
print("\nnp.identity() で作成した単位行列:\n", identity_matrix_2)

# np.eye でオフセットされた対角を持つ行列も作成できる
offset_diag_matrix = np.eye(4, k=1, dtype=int)
print("\nnp.eye() でオフセット対角を持つ行列 (k=1):\n", offset_diag_matrix)

出力

np.eye() で作成した単位行列:
 [[1 0 0]
 [0 1 0]
 [0 0 1]]

np.identity() で作成した単位行列:
 [[1 0 0 0]
 [0 1 0 0]
 [0 0 1 0]
 [0 0 0 1]]

np.eye() でオフセット対角を持つ行列 (k=1):
 [[0 1 0 0]
 [0 0 1 0]
 [0 0 0 1]
 [0 0 0 0]]

numpy.fill_diagonal() は、既存のNumPy配列の主対角を効率的にインプレースで埋めるための最善の方法です。特に、単一の値または繰り返し可能な値のシーケンスで主対角を埋めたい場合に適しています。

しかし、以下のような場合には代替方法を検討すると良いでしょう。

  • 非常に特殊なインデックスパターンで値を埋めたい場合
    手動でのインデックス操作や、より高度なNumPyのブロードキャスト/マスク処理が必要になることがあります。
  • 対角行列を新たに作成したい場合
    np.diag()np.diagflat()、あるいは単位行列であれば np.eye()np.identity() が適しています。
  • オフセットされた対角を埋めたい場合
    np.diag_indices() を使うのが最も柔軟です。