Python 从numpy数组中提取对角块
我正在寻找一种简洁的方法来提取沿(2N)x(2N)numpy数组主对角线的大小为2x2的对角线块(即,将有N个这样的块)。这概括了numpy.diag,它沿主对角线返回元素,人们可能会将其视为1x1块(当然numpy不会以这种方式表示它们) 为了更广泛地表述这一点,您可能希望从(MN)x(MN)数组中提取N个MxM块。这似乎是scipy.linalg.block_diag的补充,在中进行了巧妙的讨论,将块从block_diag将放置它们的位置拉出 借用的代码,下面是如何设置的:Python 从numpy数组中提取对角块,python,numpy,scipy,Python,Numpy,Scipy,我正在寻找一种简洁的方法来提取沿(2N)x(2N)numpy数组主对角线的大小为2x2的对角线块(即,将有N个这样的块)。这概括了numpy.diag,它沿主对角线返回元素,人们可能会将其视为1x1块(当然numpy不会以这种方式表示它们) 为了更广泛地表述这一点,您可能希望从(MN)x(MN)数组中提取N个MxM块。这似乎是scipy.linalg.block_diag的补充,在中进行了巧妙的讨论,将块从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进行类比,可能还希望提取非对角块:第k个块对角线上的N-k个块。(顺便说一句,block_diag的扩展允许block被放置在主对角线之外当然是有用的,但这不是这个问题的范围。)对于上面的数组,这可能会产生:
>>> 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]]])
我看到,中介绍的跨步技巧的使用旨在产生类似于此功能的东西,但我理解跨步是在字节级别操作的,这听起来不太合适
出于动机,我希望提取协方差矩阵的对角元素(即方差),其中元素本身不是标量,而是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
您不想使用直截了当的方法有什么特别的原因吗
>>> 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]
>>>
作为一个起点,你可以使用
>>> 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]])]
您还可以使用视图来执行此操作。这可能比索引方法更快
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
根据unutbu在上的回答,我得到以下结果:(顺便问一下,在字节级别操作有什么问题?)
用法示例:
# 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]]]
请注意,上面的函数返回一个视图,如果在返回的数组数组中更改任何内容,则原始数组也会受到影响。如有必要,复制一份。将numpy作为np导入 def提取_块(阵列): d=np.数组([[4,4,0,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,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,3,3]) 对于提取块(d)中的h: [4 4][4]]、[1]]、[[2 2 2][2 2]]、[[3 3][3 3][3]
我觉得这个很好。我认为,如果在列表中使用
numpy.array()
调用,那么输出将是一个(a.shape[0]/M,M,M)数组。索引算法可以推广到非对角块。伟大的很高兴我能帮忙。但是请注意,我没有真正考虑当M
没有整齐地划分为矩阵形状时会发生什么。如果将其封装在extract\u block\u diag
函数中,则很可能需要进行一些输入检查。使用extract\u block(数组)可以吐出任何大小的所有对角线块。
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]]]
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)]
print h