给定一个自索引(不确定这是否是正确的术语)numpy数组,例如:
a = np.array([3, 2, 0, 1])
这表示该排列(=>
是箭头):
0 => 3
1 => 2
2 => 0
3 => 1
我尝试用一个数组来表示逆变换,而不是在python中"手动"完成,也就是说,我想要一个 * pure * numpy的解,在上面的例子中我想要的结果是:
array([2, 3, 1, 0])
这相当于
0 <= 3 0 => 2
1 <= 2 or 1 => 3
2 <= 0 2 => 1
3 <= 1 3 => 0
这看起来很简单,但我就是想不出该怎么做。我试着用谷歌搜索,但没有找到任何相关的东西。
3条答案
按热度按时间deyfvvtc1#
简短回答
上面的代码将打印
根据需要。
答案的其余部分与上面
for
循环的高效矢量化有关,如果您只想知道结论,请跳到答案的末尾。预期单遍线性时间算法比
np.argsort
快;有趣的是,上述for
循环的普通矢量化(s[p] = xrange(p.size)
,参见index arrays)实际上比np.argsort
慢了p.size < 700 000
那么长(当然,在我的机器上,您的速度会有所不同):从我的IPython笔记本中:
最终,渐近复杂性开始显现(
argsort
的O(n log n)
与单遍算法的O(n)
),并且在n = p.size
足够大(在我的机器上阈值大约为700k)之后,单遍算法将始终更快。但是,使用
np.put
对上述for
循环进行矢量化还有一种不那么直接的方法:对于
n = 700 000
(与上面的大小相同):公平地说,对于较小的
n
,np.argsort
仍然优于np.put
方法(在我的机器上,临界点大约是n = 1210
):这很可能是因为我们使用
np_put
方法分配并填充了一个额外的数组(在np.arange()
调用处)。虽然您没有要求使用Cython解决方案,但出于好奇,我还使用typed memoryviews对以下Cython解决方案进行了计时:
时间:
因此,
np.put
解决方案仍然没有尽可能快(对于该输入大小运行了12.8ms;argsort花费72.7毫秒)。于2017年2月3日更新为NumPy 1.11
Jamie、Andris和Paul在下面的评论中指出,花式索引的性能问题已经解决,Jamie说NumPy 1.9已经解决了这个问题,我在2014年使用的机器上用Python 3.5和NumPy 1.11测试了它。
时间:
确实是一个重大的进步!
结论
总而言之,为了代码的清晰性,我会采用上面提到的Short answer方法。在我看来,它比
argsort
更容易理解,而且对于大输入大小也更快。如果速度成为一个问题,我会采用Cython解决方案。ct2axkht2#
np.arange(n)
的置换p
的逆是对p
排序的索引s
的阵列,即必须全为true。
np.argsort
返回的就是这样一个s
:jhiyze9q3#
我想为larsmans的正确答案提供一点背景知识。当你使用permutation by a matrix的表示时,可以找到
argsort
正确的 * 原因 *。置换 * 矩阵 *P
的数学优势在于矩阵“对向量进行操作”,即置换矩阵乘以向量置换向量。排列如下所示:
给定一个置换矩阵,我们可以通过乘以它的逆矩阵
P^-1
来“撤销”乘法。置换矩阵的美妙之处在于它们是正交的,因此P*P^(-1)=I
,或者换句话说P(-1)=P^T
,逆矩阵是转置矩阵。这意味着我们可以用转置矩阵的索引来找到你的逆置换向量:仔细想想,这与查找对
P
的列进行排序的索引完全相同!