从numpy数组中提取对角块

时间:2012-05-31 10:03:18

标签: python numpy scipy

我正在寻找一种简洁的方法来提取大小为2x2的对角块,它位于(2N)x(2N)numpy数组的主对角线上(也就是说,会有N个这样的块)。这概括了numpy.diag,它沿着主对角线返回元素,人们可能会认为它是1x1块(当然numpy并不代表它们)。

为了更广泛地说明这一点,人们可能希望从(MN)x(MN)阵列中提取N个MxM块。这似乎是scipy.linalg.block_diag的补充,在How can I transform blocks into a blockdiagonal matrix (NumPy)中巧妙地讨论过,从block_diag将它们放置的地方拉出块。

借用the solution to that question中的代码,以下是如何设置代码:

>>> 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进行类比,人们可能还希望提取非对角线块:它们在第k个块对角线上的N - 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]]])

我看到使用this question中涵盖的stride_tricks旨在产生类似于此功能的东西,但我知道跨步操作在字节级别,这听起来不太合适。

通过动机,这源于我希望提取协方差矩阵(即方差)的对角元素的情况,其中元素本身不是标量而是2x2矩阵。

修改:根据the suggestion from Chris,我做了以下尝试:

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

5 个答案:

答案 0 :(得分:3)

您也可以使用视图进行操作。这可能比索引方法更快。

import numpy as np
import scipy.linalg

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

b = scipy.linalg.block_diag(a1, a2, a3)
b[1,4] = 4
b[1,7] = 5
b[4,1] = 6
b[4,7] = 7
b[7,1] = 8
b[7,4] = 9
print b

def extract_block_diag(a, n, k=0):
    a = np.asarray(a)
    if a.ndim != 2:
        raise ValueError("Only 2-D arrays handled")
    if not (n > 0):
        raise ValueError("Must have n >= 0")

    if k > 0:
        a = a[:,n*k:] 
    else:
        a = a[-n*k:]

    n_blocks = min(a.shape[0]//n, a.shape[1]//n)

    new_shape = (n_blocks, n, n)
    new_strides = (n*a.strides[0] + n*a.strides[1],
                   a.strides[0], a.strides[1])

    return np.lib.stride_tricks.as_strided(a, new_shape, new_strides)

print "-- Diagonal blocks:"
c = extract_block_diag(b, 3)
print c

print "-- They're views!"
c[0,1,2] = 9
print b[1,2]

print "-- 1st superdiagonal"
c = extract_block_diag(b, 3, 1)
print c

print "-- 2nd superdiagonal"
c = extract_block_diag(b, 3, 2)
print c

print "-- 3rd superdiagonal (empty)"
c = extract_block_diag(b, 3, 3)
print c

print "-- 1st subdiagonal"
c = extract_block_diag(b, 3, -1)
print c

print "-- 2nd subdiagonal"
c = extract_block_diag(b, 3, -2)
print c

答案 1 :(得分:2)

作为起点,您可以使用类似

的内容
>>> 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]])]

答案 2 :(得分:0)

您不想使用简单方法的任何特殊原因?

>>> A=np.array([[1,1,1,1],[2,2,2,2],[3,3,3,3],[4,4,4,4]])
>>> M1s,M2s=0,2 # start from A[M1s,M2s]
>>> M=2  # extract an MxM block
>>> for a in A[M1s:M1s+M]:
...   print a[M2s:M2s+M]
... 
[1 1]
[2 2]
>>>

答案 3 :(得分:0)

根据unutbu对https://stackoverflow.com/a/8070716/219229的回答,我得到以下结论:( BTW在字节级操作有什么问题?)

from numpy.lib.stride_tricks import as_strided

...

def extract_block_diag(A, M=3, k=0):
   ny, nx = A.shape
   ndiags = min(map(lambda x: x//M, A.shape))
   offsets = (nx*M+M, nx, 1)
   strides = map(lambda x:x*A.itemsize, offsets)
   if k > 0:
       B = A[:,k*M]
       ndiags = min(nx//M-k, ny//M)
   else:
       k = -k
       B = A[k*M]
       ndiags = min(nx//M, ny//M-k)
   return as_strided(B, shape=(ndiags,M,M),
                     strides=((nx*M+M)*A.itemsize, nx*A.itemsize, A.itemsize))

使用示例:

# a1, a2, a3 from your example
>>> bigA = scipy.linalg.block_diag(a1, a2, a3)
>>> extract_block_diag ( bigA, 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]]])
>>> extract_block_diag ( bigA, 3 )[2]
array([[3, 3, 3],
       [3, 3, 3],
       [3, 3, 3]])
>>> extract_block_diag(np.arange(1,9*9+1).reshape(9,9),3,1)
[[[ 4  5  6]
 [13 14 15]
 [22 23 24]]

[[34 35 36]
 [43 44 45]
 [52 53 54]]]

请注意,上面的函数返回一个视图,如果更改返回的数组数组中的任何内容,原始视图也会受到影响。如有必要,请复印。

答案 4 :(得分:0)

将numpy导入为np

def extract_blocks(array):

prev = -1
for i in xrange(len(array)-1):
    if array[i+1][i] == 0 and array[i][i+1] == 0:
        yield array[prev + 1: i + 1, prev + 1: i + 1]
        print prev + 1, i
        prev = i
yield array[prev + 1: len(array), prev + 1: len(array)]

d = np.array([[4,4,0,0,0,0,0,0,0],[4,4,0,0,0,0,0,0], [0,0,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,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]])

for extract_blocks(d)中的h:

print h
  
    
      

[[4 4] [4 4]],[[1]],[[2 2 2] [2 2 2] [2 2 2]],[[3 3 3] [3 3 3] [ 3 3 3]]