python - 如何在numpy中反转排列数组

标签 python algorithm numpy vectorization

给定一个自索引(不确定这是否是正确的术语)numpy 数组,例如:

a = np.array([3, 2, 0, 1])

这表示 permutation (=> 是一个箭头):

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

看起来很简单,但我就是想不出怎么做。我试过谷歌搜索,但没有找到任何相关信息。

最佳答案

简答

def invert_permutation(p):
    """Return an array s with which np.array_equal(arr[p][s], arr) is True.
    The array_like argument p must be some permutation of 0, 1, ..., len(p)-1.
    """
    p = np.asanyarray(p) # in case p is a tuple, etc.
    s = np.empty_like(p)
    s[p] = np.arange(p.size)
    return s


在这里排序是多余的。这只是一个单 channel 线性时间算法,具有恒定的内存需求:

from __future__ import print_function
import numpy as np

p = np.array([3, 2, 0, 1])
s = np.empty(p.size, dtype=np.int32)
for i in np.arange(p.size):
    s[p[i]] = i

print('s =', s)

上面的代码打印出来

 s = [2 3 1 0]

根据需要。

答案的其余部分与上述 for 的有效矢量化有关。环形。如果你只是想知道结论,请跳到这个答案的末尾。


(2014 年 8 月 27 日的原始答案;时间对 NumPy 1.8 有效。稍后会更新 NumPy 1.11。)

单次线性时间算法预计比 np.argsort 更快;有趣的是,上述 s[p] = xrange(p.size) 的微不足道的矢量化( forsee index arrays )循环实际上比 np.argsort 稍慢只要p.size < 700 000 (嗯,在我的机器上,您的里程有所不同):

import numpy as np

def np_argsort(p):
    return np.argsort(p)
    
def np_fancy(p):
    s = np.zeros(p.size, p.dtype) # np.zeros is better than np.empty here, at least on Linux
    s[p] = xrange(p.size) 
    return s

def create_input(n):
    np.random.seed(31)
    indices = np.arange(n, dtype = np.int32)
    return np.random.permutation(indices)

来 self 的 IPython 笔记本:

p = create_input(700000)
%timeit np_argsort(p)
10 loops, best of 3: 72.7 ms per loop
%timeit np_fancy(p)
10 loops, best of 3: 70.2 ms per loop

最终,渐近复杂度开始出现(O(n log n) 用于 argsortO(n) 用于单 channel 算法),并且在足够大的 n = p.size 之后单 channel 算法将始终更快(我的机器上的阈值约为 700k)。

但是,有一种不太直接的方法可以对上述 for 进行矢量化。循环 np.put :

def np_put(p):
    n = p.size
    s = np.zeros(n, dtype = np.int32)
    i = np.arange(n, dtype = np.int32)
    np.put(s, p, i) # s[p[i]] = i 
    return s

这给出了 n = 700 000 (和上面一样大小):

p = create_input(700000)
%timeit np_put(p)
100 loops, best of 3: 12.8 ms per loop

这是一个不错的 5.6 倍加速,几乎没有!

公平地说,np.argsort仍然胜过np.put较小的方法n (在我的机器上,临界点在 n = 1210 左右):

p = create_input(1210)
%timeit np_argsort(p)
10000 loops, best of 3: 25.1 µs per loop
%timeit np_fancy(p)
10000 loops, best of 3: 118 µs per loop
%timeit np_put(p)
10000 loops, best of 3: 25 µs per loop

这很可能是因为我们使用 np.arange() 分配并填充了一个额外的数组(在 np_put 调用中)接近。


虽然您没有要求 Cython 解决方案,但出于好奇,我还将以下 Cython 解决方案计时为 typed memoryviews :

import numpy as np
cimport numpy as np

def in_cython(np.ndarray[np.int32_t] p):    
    cdef int i
    cdef int[:] pmv
    cdef int[:] smv 
    pmv = p
    s = np.empty(p.size, dtype=np.int32)
    smv = s
    for i in xrange(p.size):
        smv[pmv[i]] = i
    return s

时间安排:

p = create_input(700000)
%timeit in_cython(p)
100 loops, best of 3: 2.59 ms per loop

所以,np.put解决方案仍然没有尽可能快(这个输入大小运行了 12.8 毫秒;argsort 花了 72.7 毫秒)。


2017 年 2 月 3 日更新 NumPy 1.11

Jamie、Andris 和 Paul 在下面的评论中指出,花式索引的性能问题已得到解决。 Jamie 说它已经在 NumPy 1.9 中解决了。我在 2014 年使用的机器上使用 Python 3.5 和 NumPy 1.11 对其进行了测试。

def invert_permutation(p):
    s = np.empty(p.size, p.dtype)
    s[p] = np.arange(p.size)
    return s

时间安排:

p = create_input(880)
%timeit np_argsort(p)
100000 loops, best of 3: 11.6 µs per loop
%timeit invert_permutation(p)
100000 loops, best of 3: 11.5 µs per loop

确实是一个显着的改进!


结论

总而言之,为了代码清晰,我会采用顶部提到的简答方法。在我看来,它没有 argsort 那样晦涩难懂。 ,并且对于大输入大小也更快。如果速度成为问题,我会选择 Cython 解决方案。

关于python - 如何在numpy中反转排列数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11649577/

相关文章:

python - Scipy值错误: Total size of new array must be unchanged

python - CSV 行附加 : DictWriter always writes from first column

python - 如果某行包含 CSV 文件中的字符串,则删除该行

python - 什么时候在 pytest 中使用 fixture 而不是仅仅声明一个变量

algorithm - 如何轻松比较由空间中的点组成的两条线?

python - 2D NumPy 数组的蛇形遍历

python - 安装 pygtk 不工作

algorithm - 当语言没有这样做的功能时,按字母顺序对项目列表进行排序?

c# - 在 byte[] (C#) 中搜索 Int32 的最快算法是什么?

python - 一个 "ij"meshgrid和一个长meshgrid的对应关系