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
成为numpyn
xm
数组。您可以先确定您有哪些分区。集合构造函数删除重复的条目,但对它们进行任意排序。我对它们进行排序只是为了有一个明确的顺序:
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)配合使用的方式编写它们