在numpy中就地置换矩阵


27

我想通过使用python的numpy库更改其若干行和列的顺序来就地修改密集的正方形转换矩阵。从数学上讲,这对应于将矩阵乘以置换矩阵P并将其乘以P ^ -1 = P ^ T,但这并不是计算合理的解决方案。

现在,我正在手动交换行和列,但是我希望numpy有一个不错的函数f(M,v),其中M具有n行和列,而v具有n项,以便f(M,v)更新M根据索引排列v。也许我只是在搜索互联网上失败了。

numpy的“高级索引”可能会发生这种情况,但是我的理解是这样的解决方案不会就位。同样,对于某些简单情况,仅跟踪索引排列可能就足够了,但是在我的情况下这并不方便。

补充:
有时当人们谈论排列时,它们仅表示对随机排列进行采样,例如,这是在统计中获取p值的过程的一部分。或者,它们的意思是计数或枚举所有可能的排列。我不是在谈论这些事情。

补充:
矩阵足够小以适合桌面RAM,但又足够大,我不想深思熟虑地复制它。实际上,我想使用尽可能大的矩阵,但我不想处理无法将其保存在RAM中的不便,并且我会对矩阵执行O(N ^ 3)LAPACK运算,这也会限制实际矩阵的大小。我目前不必要地复制这么大的矩阵,但我希望可以很容易地避免这种排列的情况。


3
如果可以更新问题以给出矩阵的大小,那将是很好的。对所有人来说,“巨大”并不意味着同一件事。
Bill Barth 2012年

2
正确的是,高级(或称为花式)索引创建了一个副本。但是,如果您接受这个事实,那么您的代码就是M[v]置换行。
Daniel Velkov 2012年

@daniel:整个排列是M [v,:] [:, v]吗?这是使用花式索引获取排列的最佳方法吗?是否会使用3倍的矩阵内存,包括原始矩阵的大小,行+列排列的矩阵以及临时行排列的矩阵?

没错,您将拥有原始矩阵和2个副本。顺便说一句,为什么您需要同时排列行和列?
Daniel Velkov 2012年

4
您将如何处理置换矩阵?应用运算符时,最好只对向量进行置换。
杰德·布朗

Answers:


9

根据文档,在numpy中没有就地置换方法,类似于ndarray.sort

MN×Np

  1. 在C中实现自己的算法作为扩展模块(但是就地算法很难,至少对我而言!)
  2. N

    for i in range(N):
        M[:,i] = M[p,i]
    for i in range(N):
        M[i,:] = M[i,p]
  3. N2

    M[:,:] = M[p,:]
    M[:,:] = M[:,p]

希望这些次优的hacks有用。


@ hack没什么2.你称之为“手动交换行和列”吗?
Stefano M

1
O(N)p

O(N)O(N)

2
这对于cython功能确实是一个很好的候选人。不应超过10行。。。要我给它一个裂缝吗?
meawoppl 2012年

大声笑。我开始使用Cython,然后在我一直使用的函数中找到正确的答案。h 查看我发布的答案。
meawoppl 2014年

6

警告:以下示例正常运行,但是使用在后期建议的完整参数集会暴露numpy.take()函数中的bug或至少一个“未记录的功能”。有关详细信息,请参见下面的评论。 错误报告已提交

您可以使用numpy的take()函数就地执行此操作,但是它需要一些箍跳。

这是对一个单位矩阵的行进行随机排列的示例:

import numpy as np
i = np.identity(10)
rr = range(10)
np.random.shuffle(rr)
np.take(i, rr, axis=0)
array([[ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
       [ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.]])

要做到这一点原地的,所有你需要做的是指定的“out”参数是一样的输入数组您必须设置模式=“夹”或模式=“包装”。如果不设置模式,它将在Python异常下进行复制以恢复阵列状态(请参见此处)

最后一点,take似乎是一个数组方法,所以代替

np.take(i, rr, axis=0)

你可以打电话

i.take(rr, axis=0)

如果这更符合您的口味。因此,总的来说,您的呼叫应类似于以下内容:

#Inplace Rearrange
arr = makeMyBixMatrix()
pVec0, pVec1 = calcMyPermutationVectors()
arr.take(pVec0, axis=0, out=arr, mode="clip")
arr.take(pVec1, axis=1, out=arr, mode="clip")

要置换行和列,我认为您要么必须运行两次,要么使用numpy.unravel_index拉一些丑陋的恶作剧,这让我难以思考。


如前所述,就地算法很难。您的解决方案不适用于numpy 1.6.2。和1.7.1(重复的行/列)。没有时间检查1.8.x是否可以解决此问题
Stefano M

嗯 您可以在某个地方发布测试代码吗?在我的脑海中,我觉得需要对索引进行排序操作,该操作首先要在摘录之前发生。我将对此PM进行更多调查。
meawoppl 2014年

1
当我运行这段代码,我得到1.6.2test take, not overwriting: Truetest not-in-place take: Truetest in-place take: Falserr [3, 7, 8, 1, 4, 5, 9, 0, 2, 6]arr [30 70 80 70 40 50 90 30 80 90]ref [30 70 80 10 40 50 90 0 20 60]。因此np.take,至少对于numpy 1.6.2,它不知道执行就地排列并将事情搞砸了。
Stefano M

ouch子 很好的证明。这可能被视为恕我直言的错误。至少文档应该说输入和输出不能是同一数组,可能要检查一下,除非是。
meawoppl 2014年

同意该错误:也许您应该在帖子中添加注释,以警告读者您的解决方案可能会产生错误的结果。
Stefano M

2

如果您以COO格式存储稀疏矩阵,则以下内容可能会有所帮助

    A.row = perm[A.row];
    A.col = perm[A.col];

ACOOpermnumpy.arraymm


但是首先存储一个全密矩阵作为稀疏C00矩阵的内存开销是多少?
Federico Poloni 2014年

intfloatfloatn2numpy.ndarray

1

我没有足够的声誉来发表评论,但我认为以下SO问题可能会有所帮助:https : //stackoverflow.com/questions/4370745/view-onto-a-numpy-array

其基本点是,你可以使用基本的切片,这将创建到阵列的视图,而无需拷贝,但如果这样做先进的切片/索引那么它创建一个副本。


OP正在请求排列,而使用基本切片是不可能的。
Stefano M

你当然是对的。我认为OP理解切片会发生什么(如果他们不知道的话)会很有用,因为他们担心何时会发生复制。如果他在您的答案中使用了某些内容,那么我想知道这一点,因为您可以在循环中使用它们。
2013年

-1

关于什么

my_array [:,[0,1]] = my_array [:,[1,0]]


1
这构造了一个临时对象,这正是他想要避免的。
Michael Grant
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.