Python 压缩块矩阵

Python 压缩块矩阵,python,numpy,matrix,scipy,block,Python,Numpy,Matrix,Scipy,Block,考虑一个矩阵M1为所有组合x,y提供值。考虑一个分区 f(x)-> x 和一个分区 g(y)-> y>代码>。此外,考虑一个操作 p(a)< /代码>一个集合一个数字< /代码>,即 max(a)< /代码>或求和(a)< /代码> 映射f,g可用于从M1创建块矩阵M2,其中映射到相同x的所有x相邻,且所有y都相同 该矩阵M2对于“集合”的每个组合X,Y都有一个块 现在,我想通过分别在每个块上应用p将这个矩阵M2压缩成另一个矩阵M3M3对于X,Y的每个组合都有一个值 理想情况下,我希望跳过使用

考虑一个矩阵
M1
为所有组合
x,y
提供值。考虑一个分区<代码> f(x)-> x <代码>和一个分区<代码> g(y)-> y>代码>。此外,考虑一个操作<代码> p(a)< /代码>一个集合<代码>一个数字< /代码>,即<代码> max(a)< /代码>或<代码>求和(a)< /代码>

映射
f,g
可用于从
M1
创建块矩阵
M2
,其中映射到相同
x
的所有
x
相邻,且所有
y
都相同

该矩阵
M2
对于“集合”的每个组合
X,Y
都有一个块

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

理想情况下,我希望跳过使用
f
g
动态地将
M1
转换为
M2

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


特例:实际上,在我的例子中,
x
y
是相同的,只有一个函数
f
应用于这两个函数。我只关心对角线下的
M2
部分。

我能想到的最直接的方法是将矩阵转换为一维数组,尽管可能不是最有效的方法(特别是当矩阵很大时),然后为分区组索引
X
Y
创建相应的数组。然后,您可以按分区组索引进行分组,并最终将矩阵重新构造为其原始形式

例如,如果矩阵是

>>> 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]
从这一点来看,有几种方法可以实现重塑和后续分组。例如,您可以通过构造一个
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()
方法将数据帧转换回正确的2D“矩阵形式”,如果需要,可以使用
as_matrix()
方法将其转换回纯Numpy数组

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

如果你不想带熊猫进来,还有其他的图书馆也做同样的事情。例如,你可以看看。但是,我还没有找到任何真正的二维分组库,如果您使用的是非常大的矩阵,那么您可能需要这样的库,如果您使用的矩阵足够大,那么多2到3个副本将耗尽可用内存。

M1
成为numpy
n
x
m
数组。您可以先确定您有哪些分区。集合构造函数删除重复的条目,但对它们进行任意排序。我对它们进行排序只是为了有一个明确的顺序:

xs = sorted(set(f(i) for i in range(n)))
ys = sorted(set(g(i) for i in range(m)))
要为每个
X,Y
构建块矩阵,您可以使用numpy布尔索引和网格构造辅助工具
ix_u
分别选择属于
X
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
g
必须对numpy数组应用元素方式才能工作。正如在另一个答案中提到的,
numpy.vectorize
decorator可以用来实现这一点

举个例子:

from __future__ import division
n = m = 5
M1 = np.arange(25).reshape(5,5)
f = lambda x: x // 3      # f(ii) = [0, 0, 0, 1, 1]
g = lambda x: (x+2) // 3  # g(jj) = [0, 1, 1, 1, 2]
p = numpy.sum

M3 = [[  15.,   63.,   27.],
      [  35.,  117.,   43.]]

几年后我遇到了同样的问题,我认为最好的解决方法如下:

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

我将
矢量化
描述为一种简单的方法,而不是一种快速的方法(这意味着速度的提高)。您的
f(x)
可以写成
np.array([1,1,1,2,2])[x]
,它的运行速度将比矢量化版本快得多。这是我第一次看到
@vectorize
用作装饰器。它确实可以工作,但它不允许像
otypes
这样的参数。通常,当海报在
矢量化方面出现问题时(速度期望除外),这是因为海报需要一个额外的参数。@hpaulj从编写代码所需的时间来看,它很快。我很清楚矢量化的执行速度不快,但这里不关心速度。无论如何,谢谢你的建议。它应该运行得很快,而不是编码。我认为这里的主要问题是scipy不支持二维以上的稀疏矩阵。否则,您可以始终为每个组(4d)增加一个维度,并使用类似max()的函数来减少组成员的维度。是否
f
g
仅处理标量输入?理想情况下,要使用
numpy
,您需要以一种与val数组(可以是1d)配合使用的方式编写它们