(2N)x(2N) numpy 配列の主対角線に沿ってあるサイズ 2x2 の対角ブロックを抽出するきちんとした方法を探しています (つまり、そのようなブロックが N 個存在します)。これは numpy.diag を一般化し、主対角線に沿って要素を返します。これは 1x1 ブロックと見なすことができます (もちろん、numpy はこのようには表現しません)。
これをもう少し広く表現すると、(MN)x(MN) 配列から N MxM ブロックを抽出したい場合があります。これは scipy.linalg.block_diag の補数のようです。ブロックをブロックをブロック対角行列に変換する方法 (NumPy)で適切に説明されており、block_diag が配置した場所からブロックを引き出します。
その質問の解決策からコードを借りて、これを設定する方法は次のとおりです。
>>> a1 = np.array([[1,1,1],[1,1,1],[1,1,1]])
>>> a2 = np.array([[2,2,2],[2,2,2],[2,2,2]])
>>> a3 = np.array([[3,3,3],[3,3,3],[3,3,3]])
>>> import scipy.linalg
>>> scipy.linalg.block_diag(a1, a2, a3)
array([[1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 2, 2, 0, 0, 0],
[0, 0, 0, 2, 2, 2, 0, 0, 0],
[0, 0, 0, 2, 2, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 3, 3, 3],
[0, 0, 0, 0, 0, 0, 3, 3, 3],
[0, 0, 0, 0, 0, 0, 3, 3, 3]])
次に、次のような関数が必要です
>>> A = scipy.linalg.block_diag(a1, a2, a3)
>>> extract_block_diag(A, M=3)
array([[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]],
[[2, 2, 2],
[2, 2, 2],
[2, 2, 2]],
[[3, 3, 3],
[3, 3, 3],
[3, 3, 3]]])
numpy.diag との類推を続けるために、非対角ブロックを抽出することもできます: N - k 番目のブロック対角線上のそれらの k。(そして、ブロックを主対角線から離して配置できるようにする block_diag の拡張は確かに便利ですが、それはこの質問の範囲ではありません。) 上記の配列の場合、次の結果が得られます。
>>> extract_block_diag(A, M=3, k=1)
array([[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]])
この質問でカバーされている stride_tricks の使用は、この機能に似たものを生成することを目的としていますが、ストライドがバイトレベルで動作することは理解していますが、これはあまり適切ではないようです。
動機として、これは、要素自体がスカラーではなく 2x2 行列である共分散行列 (つまり、分散) の対角要素を抽出したい状況から生じます。
編集:クリスからの提案に基づいて、次の試みを行いました:
def extract_block_diag(A,M,k=0):
"""Extracts blocks of size M from the kth diagonal
of square matrix A, whose size must be a multiple of M."""
# Check that the matrix can be block divided
if A.shape[0] != A.shape[1] or A.shape[0] % M != 0:
raise StandardError('Matrix must be square and a multiple of block size')
# Assign indices for offset from main diagonal
if abs(k) > M - 1:
raise StandardError('kth diagonal does not exist in matrix')
elif k > 0:
ro = 0
co = abs(k)*M
elif k < 0:
ro = abs(k)*M
co = 0
else:
ro = 0
co = 0
blocks = np.array([A[i+ro:i+ro+M,i+co:i+co+M]
for i in range(0,len(A)-abs(k)*M,M)])
return blocks
上記のデータに対して次の結果が返されます。
D = extract_block_diag(A,3)
[[[1 1 1]
[1 1 1]
[1 1 1]]
[[2 2 2]
[2 2 2]
[2 2 2]]
[[3 3 3]
[3 3 3]
[3 3 3]]]
D = extract_block_diag(A,3,-1)
[[[0 0 0]
[0 0 0]
[0 0 0]]
[[0 0 0]
[0 0 0]
[0 0 0]]]