numpy:压缩块矩阵

时间:2017-01-05 15:24:28

标签: python numpy matrix scipy block

考虑矩阵M1,为所有组合x,y提供值。考虑分区f(x)->X和分区g(y)->Y。此外,请考虑对p(A)个数字设置A的操作max(A)

映射sum(A)可用于从f,g创建一个块矩阵M1,其中映射到同一M2的所有x都相邻,所有X都是一样的。

此矩阵y为“集合”M2的每个组合都有一个块。

现在,我想通过在每个块上分别应用X,Y,将此矩阵M2压缩到另一个矩阵M3中。 p的每个组合M3都有一个值。

理想情况下,我想暂时使用X,YM1跳过M2f的转换。

执行此类操作的最有效方法是什么?是否可以为其部署gnumpy

特殊情况:实际上,在我的情况下,scipyx是相同的,并且只有一个函数y适用于它们。我只关心对角线下的f部分。

3 个答案:

答案 0 :(得分:3)

我能想到的最简单的方法,尽管可能不是最有效的(特别是如果你的矩阵很大),是将矩阵转换为一维数组,然后为分区提供相应的数组小组索引XY。然后,您可以按分区组索引进行分组,最后将矩阵重新构建回原始形式。

例如,如果你的矩阵是

>>> M1 = np.arange(25).reshape((5,5))
>>> M1
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])

你的分区是

>>> def f(x):
...     return np.array([1,1,1,2,2])[x]
>>> def g(y):
...     return np.array([3,4,4,4,5])[y]

从那时起,有几种方法可以实现重塑和后续分组。您可以使用Pandas来执行此操作,例如,通过构造DataFrame并使用其stack()方法将所有行“堆叠”在一个列中的彼此之上,索引为他们原来的行和列索引。

>>> st = pd.DataFrame(M1).stack().to_frame('M1')
>>> st
     M1
0 0   0
  1   1
  2   2
  3   3
  4   4
1 0   5
...
4 3  23
  4  24

(为了便于阅读,我已截断输出,我相信如果你想查看它们的输出,你可以自己评估其余的这些例子。)然后你可以添加代表分区组索引的列:

>>> st['X'] = f(st.index.get_level_values(0))
>>> st['Y'] = g(st.index.get_level_values(1))

然后您可以按这些索引进行分组并应用您选择的聚合函数。

>>> stp = st.groupby(['X', 'Y']).agg(p)

您必须定义p(或查找现有定义),以便它采用一维Numpy数组并返回单个数字。如果你想使用像sum()这样的东西,你可以使用st.groupby(...).sum(),因为Pandas内置了对它和其他一些标准函数的支持,但agg是通用的,适用于任何您可以提供的缩减功能p

最后,unstack()方法会将DataFrame转换回正确的2D“矩阵形式”,然后如果需要,可以使用as_matrix()方法将其转换回纯Numpy数组

>>> M3 = stp.unstack().as_matrix()
>>> M3
array([[ 15,  63,  27],
       [ 35, 117,  43]])

如果您不想引入Pandas,还有其他库可以执行相同的操作。例如,您可能会看numpy-groupies,但是当我写这篇文章时,它似乎只与Python 2兼容。但是我没有找到任何真正的二维分组库,如果你可能需要它正在使用非常大的矩阵,足够大,额外的2或3个副本会耗尽可用的内存。

答案 1 :(得分:1)

M1成为一个numpy n x m数组。您可以从确定您拥有的分区开始。 set构造函数删除重复的条目,但是任意命令它们。我对它们进行排序只是为了有一个明确定义的顺序:

xs = sorted(set(f(i) for i in range(n)))
ys = sorted(set(g(i) for i in range(m)))

要为每个X,Y构建块矩阵,您可以使用numpy布尔索引以及网格构造帮助器ix_来仅选择属于X和{{1的行和列}}, 分别。最后,将Y应用于选定的子矩阵:

p

分区from numpy import zeros, arange, ix_ ii, jj = arange(n), arange(m) M3 = zeros((len(xs), len(ys))) for k, X in enumerate(xs): for l, Y in enumerate(ys): M3[k,l] = p(M1[ix_(f(ii) == X, g(jj) == Y)]) f必须以元素方式应用于numpy数组才能使其正常工作。正如在另一个答案中提到的,可以使用g装饰器来实现这一点。

举个例子:

numpy.vectorize

答案 2 :(得分:0)

几年后,我遇到了同样的问题,我认为,实现此目的的最佳解决方案如下:

M2 = np.zeros((n,m))
for i in range(n):
    for j in range(m):
        M2[i,j] = p(M1[f(x) == i, :][: , g(y) == j])

这假设f取[0,1,.. n-1]上的值,而g取[0,1 ... m-1]上的值

一个例子是

import numpy as np

M1 = np.random.random((4,6))

print(M1)

x = range(4)
y = range(6)
p = np.sum


def f(x):
    return np.array([0,0,1,2])[x]

def g(y):
    return np.array([0,1,1,0,1,0])[y]

n = 3 # number of elements in partition f
m = 2 # number of elements in partition g


M2 = np.zeros((n,m))
for i in range(n):
    for j in range(m):
        M2[i,j] = p(M1[f(x) == i, :][: , g(y) == j])


print(M2)

要使n和m自动化,可以使用len(set(f(x())))和len(set(g(y)))