Extracting diagonal blocks from a numpy array

2024/10/7 8:22:47

I am searching for a neat way to extract the diagonal blocks of size 2x2 that lie along the main diagonal of a (2N)x(2N) numpy array (that is, there will be N such blocks). This generalises numpy.diag, which returns elements along the main diagonal, that one might think of as 1x1 blocks (though of course numpy doesn't represent them this way).

To phrase this a little more broadly, one might wish to extract N MxM blocks from a (MN)x(MN) array. This seems the complement of scipy.linalg.block_diag, ably discussed in How can I transform blocks into a blockdiagonal matrix (NumPy), pulling blocks out of the places that block_diag will have put them.

Borrowing code from the solution to that question, here's how this could be set up:

>>> 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]]) 

Then, we would wish to have a function like

>>> 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]]]) 

To continue the analogy with numpy.diag, one might also wish to extract the off-diagonal blocks: N - k of them on the kth block diagonal. (And in passing, an extension of block_diag allowing block to be placed off the principal diagonal would certainly be useful, but that is not the scope of this question.) In the case of the array above, this could yield:

>>> 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]]])

I see that the use of stride_tricks covered in this question aims to produce something similar to this functionality, but I understand that striding operates at the byte level, which doesn't sound quite appropriate.

By way of motivation, this arises from the situation where I wish to extract the diagonal elements of a covariance matrix (that is, the variances), where the elements themselves are not scalar but 2x2 matrices.

Edit: Based on the suggestion from Chris, I have made the following attempt:

def extract_block_diag(A,M,k=0):"""Extracts blocks of size M from the kth diagonalof square matrix A, whose size must be a multiple of M."""# Check that the matrix can be block dividedif 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 diagonalif abs(k) > M - 1:raise StandardError('kth diagonal does not exist in matrix')elif k > 0:ro = 0co = abs(k)*M elif k < 0:ro = abs(k)*Mco = 0else:ro = 0co = 0blocks = 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

where the the following results will be returned for the data above:

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]]]
Answer

As a starting point you could just use something like

>>> a
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]]) >>> M = 3
>>> [a[i*M:(i+1)*M,i*M:(i+1)*M] for i in range(a.shape[0]/M)]
[array([[1, 1, 1],[1, 1, 1],[1, 1, 1]]), array([[2, 2, 2],[2, 2, 2],[2, 2, 2]]), array([[3, 3, 3],[3, 3, 3],[3, 3, 3]])]
https://en.xdnf.cn/q/70263.html

Related Q&A

AttributeError: Unknown property density [duplicate]

This question already has an answer here:matplotlib histogram plot density argument not working(1 answer)Closed 3 years ago.I am trying to get a hold of SciPy, but I am stuck with Unknown property dens…

Find diagonals sums in numpy (faster)

I have some board numpy arrays like that:array([[0, 0, 0, 1, 0, 0, 0, 0],[1, 0, 0, 0, 0, 1, 0, 1],[0, 0, 0, 0, 0, 0, 0, 1],[0, 1, 0, 1, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0, 1],[0, 0, 0, 0, 1, 0, 0, 0],[0,…

Create dictionary from list python

I have many lists in this format:[1, O1, , , , 0.0000, 0.0000, , ] [2, AP, , , , 35.0000, 105.0000, , ] [3, EU, , , , 47.0000, 8.0000, , ]I need to create a dictionary with key as the first element in …

Outputting height of a pyramid

So for this coding exercise I have to input a number of imaginary blocks and it will tell me how many complete rows high the pyramid is. So for example if I input 6 blocks...I want it to tell me that t…

PySide SVG image formats not found?

I am using PyDev plugin for Eclipse with Qt integration. I have PySide installed and I am having trouble with SVG image formats. I know when I run my application the formats located in C:\Python27\Lib\…

convert ascii character to signed 8-bit integer python

This feels like it should be very simple, but I havent been able to find an answer..In a python script I am reading in data from a USB device (x and y movements of a USB mouse). it arrives in single AS…

What is the equivalent way of doing this type of pythonic vectorized assignment in MATLAB?

Im trying to translate this line of code from Python to MATLAB:new_img[M[0, :] - corners[0][0], M[1, :] - corners[1][0], :] = img[T[0, :], T[1, :], :]So, naturally, I wrote something like this:new_img(…

How do I connect mitmproxy to another proxy outside of my control?

The process would be that the browser send a request to MITMproxy and then generate a request that gets sent to target proxy server which isnt controlled by us. The proxy server would send a response t…

How does conda-env list / conda info --envs find environments?

Ive been experimenting with anaconda/miniconda because my users use structural biology programs installed with miniconda and none of the authors A) take into account that there might be other miniconda…

Updating a large number of entities in a datastore on Google App Engine

I would like to perform a small operation on all entities of a specific kind and rewrite them to the datastore. I currently have 20,000 entities of this kind but would like a solution that would scale …