有没有一种高效的方法以对角顺序展开一个numpy数组?

3
我正在寻找一种高效的方法(最好是矢量化的快速内置函数)以对numpy数组进行对角线顺序展平。例如:
A=np.array([[1,2,3],
            [4,5,6],
            [7,8,9]])
b=flatten_diagonally(A)

b 应该是 [7,4,8,1,5,9,2,6,3]

A 将会是一个非常大的矩阵,因此我不想逐个迭代元素。出于同样的原因,我也不想提前准备所有正确顺序的索引列表。由于 A 很大且结果将同样很大,我希望避免使用过多的内存的解决方案。

如果我能够指定我想要展平哪些对角线子集,那就更好了,例如只展平第一和第二条对角线将会得到 [1,5,9,2,6]


2
你必须迭代或使用高级索引。这是一个非连续的切片,因此无法使用“正常”的切片方法来完成。使用高级索引,你需要提前准备好一个索引数组。因此,最好通过迭代来解决这个问题。如果你想避免在内存中复制数据,通常可以使用 numpy.fromiter - Joe Kington
1
话虽如此,看看 scipy.sparse.dia_matrixscipy.sparse.spdiags。实现你想要的一种方法是将事物暂时转换为 scipy.sparse.dia_matrix,但对于密集矩阵来说,这不会是内存高效的。 - Joe Kington
@JoeKington 谢谢,不幸的是我也这么认为。我知道这不是连续的,但我希望可能有一个numpy函数是在C中硬编码的,所以它可以比通过Python进行单独访问更快地访问这些元素 - 但我想这样的东西不存在。 - Bitwise
2
@JoeKington 是的,我也是这么想的,已经在研究了。再次感谢。也许我会尝试用Cython编写一个快速对角线展平迭代算法。 - Bitwise
1
如果你选择这条路,那么看看Cython解决方案在速度上与Python生成器(带有简单嵌套的for循环)和numpy.fromiter相比会很有趣。不过,Cython应该是完美的选择! - Joe Kington
2个回答

0
以下函数基于indices比较,基于每个对角线具有索引关系的事实,例如在主对角线上i==j等等...
即使对于非正方形的二维数组也是有效的。
def flatten_diagonally(x, diags=None):
    diags = np.array(diags)
    if x.shape[1] > x.shape[0]:
        diags += x.shape[1]-x.shape[0]
    n = max(x.shape)
    ndiags = 2*n-1
    i,j = np.indices(x.shape)
    d = np.array([])
    for ndi in range(ndiags):
        if diags != None:
            if not ndi in diags:
                continue
        d = np.concatenate((d,x[i==j+(n-1)-ndi]))
    return d

示例:

print flatten_diagonally(A)
#[ 7.  4.  8.  1.  5.  9.  2.  6.  3.]

print flatten_diagonally(A, diags=(1,2))
#[ 4.  8.  1.  5.  9.]

对于非方形数组:

A=np.array([[1,2,3],
            [7,8,9]])
print flatten_diagonally(A, diags=(1,2))
#[ 1.  8.  2.  9.]

0

numpy.diag返回沿着特定索引的对角线。文档

因此,这应该为您提供所需的输出:(请注意,第0个对角线是正常对角线,因此如果您想要子对角线,则可能需要使用负值来表示对角线。)

import numpy as np

def flatten_diagonally(npA, diagonals = None):
    diagonals = diagonals or xrange(-npA.shape[0] + 1, npA.shape[1])
    return np.concatenate(map(lambda x: np.diag(npA, k = x), diagonals))

请注意,您可以使用np.diagonal而不是np.diag,我不太确定哪个更好用。文档

谢谢,但是我提到了数组会非常大(例如10000x10000),所以这个解决方案将是低效的,因为既有串联操作,又有循环。 - Bitwise
我认为你不可能在没有循环的情况下找到解决方案 - 至少这里的循环是由numpy库完成的,该库经过优化,甚至可以被推入c代码中(对此我不确定)。 - James
实际上它并不会真正地进行C优化,但我可能会尝试通过使用Cython来进行优化。 - Bitwise
循环遍历19999个对角线将被优化,因为map是用C编写的。但它确实返回一个列表,所以如果你想的话,你可以使用生成器推导式代替。不幸的是,我找不到numpy的源代码在线上,所以我不能多说什么,但除非你用另一种语言编写实际代码,否则你将难以优化比numpy更快的速度。 - James

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接