关于索引:如何在Python中获取排序数组的索引

How to get indices of a sorted array in Python

我有一个数字列表:

1
myList = [1, 2, 3, 100, 5]

现在,如果我对该列表进行排序以获得[1, 2, 3, 5, 100]
我想要的是来自
按排序顺序的原始列表,即[0, 1, 2, 4, 3]
--- ala同时返回两个
值和索引。


如果使用的是numpy,则可以使用argsort()函数:

1
2
3
>>> import numpy
>>> numpy.argsort(myList)
array([0, 1, 2, 4, 3])

http://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html

这将返回对数组或列表进行排序的参数。


如下所示:

1
2
3
>>> myList = [1, 2, 3, 100, 5]
>>> [i[0] for i in sorted(enumerate(myList), key=lambda x:x[1])]
[0, 1, 2, 4, 3]

enumerate(myList)为您提供一个包含(索引,值)元组的列表:

1
[(0, 1), (1, 2), (2, 3), (3, 100), (4, 5)]

您可以通过将列表传递给sorted并指定一个函数来提取排序键(每个元组的第二个元素;这就是lambda的目的)对列表进行排序。最后,使用[i[0] for i in ...]列表理解。


1
2
3
4
myList = [1, 2, 3, 100, 5]    
sorted(range(len(myList)),key=myList.__getitem__)

[0, 1, 2, 4, 3]


enumerate的答案很好,但我个人不喜欢用于按值排序的lambda。以下只是反转索引和值,并对它们进行排序。因此,它将首先按值排序,然后按索引排序。

1
sorted((e,i) for i,e in enumerate(myList))

enumerateitemgetter更新了答案:

1
2
sorted(enumerate(a), key=lambda x: x[1])
# [(0, 1), (1, 2), (2, 3), (4, 5), (3, 100)]

将列表压缩在一起:元组中的第一个元素将是索引,第二个是值(然后使用元组x[1]的第二个值对其进行排序,x是元组)

或使用operator模块中的itemgetter

1
2
from operator import itemgetter
sorted(enumerate(a), key=itemgetter(1))


如果您不想使用numpy,

1
sorted(range(len(seq)), key=seq.__getitem__)

如此处所示,最快。


我用perfplot(我的一个项目)对这些进行了快速性能检查,发现很难推荐除numpy以外的其他任何东西(请注意对数刻度):

enter image description here

复制剧情的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import perfplot
import numpy


def sorted_enumerate(seq):
    return [i for (v, i) in sorted((v, i) for (i, v) in enumerate(seq))]


def sorted_enumerate_key(seq):
    return [x for x, y in sorted(enumerate(seq), key=lambda x: x[1])]


def sorted_range(seq):
    return sorted(range(len(seq)), key=seq.__getitem__)


def numpy_argsort(x):
    return numpy.argsort(x)


perfplot.save(
   "argsort.png",
    setup=lambda n: numpy.random.rand(n),
    kernels=[sorted_enumerate, sorted_enumerate_key, sorted_range, numpy_argsort],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(x)",
    logx=True,
    logy=True,
)

其他答案是错误的。

一次运行argsort不是解决方案。
例如,以下代码:

1
2
3
import numpy as np
x = [3,1,2]
np.argsort(x)

产生array([1, 2, 0], dtype=int64),这不是我们想要的。

答案应该是两次运行argsort

1
2
3
import numpy as np
x = [3,1,2]
np.argsort(np.argsort(x))

按预期给出array([2, 0, 1], dtype=int64)


从本质上讲,您需要执行argsort,所需的实现取决于您要使用外部库(例如NumPy)还是要保持纯Python的依赖关系。

您需要问自己的问题是:您是否想要

  • 对数组/列表进行排序的索引
  • 元素在排序数组/列表中将具有的索引

不幸的是,问题中的示例并未明确说明所需的内容,因为两者都会给出相同的结果:

1
2
3
4
5
6
7
>>> arr = np.array([1, 2, 3, 100, 5])

>>> np.argsort(np.argsort(arr))
array([0, 1, 2, 4, 3], dtype=int64)

>>> np.argsort(arr)
array([0, 1, 2, 4, 3], dtype=int64)

选择argsort实现

如果可以使用NumPy,则可以简单地使用函数numpy.argsort或方法numpy.ndarray.argsort

已经在其他一些答案中提到了没有NumPy的实现,因此我将根据此处的基准答案来概述最快的解决方案

1
2
def argsort(l):
    return sorted(range(len(l)), key=l.__getitem__)

获取将对数组/列表进行排序的索引

要获取对数组/列表进行排序的索引,您只需在数组或列表上调用argsort。我在这里使用的是NumPy版本,但是Python实现应该给出相同的结果

1
2
3
>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(arr)
array([1, 2, 0, 3], dtype=int64)

结果包含获取排序数组所需的索引。

由于排序后的数组为[1, 2, 3, 4],因此argsorted数组包含原始元素中这些元素的索引。

  • 最小值为1,并且在原始索引中的索引为1,因此结果的第一个元素为1
  • 2在原始索引中位于索引2,因此结果的第二个元素是2
  • 3在原始索引中的索引为0,因此结果的第三个元素为0
  • 最大值4,它在原始索引中的索引为3,因此结果的最后一个元素为3

获取元素在已排序数组/列表中的索引

在这种情况下,您需要两次应用argsort

1
2
3
>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(np.argsort(arr))
array([2, 0, 1, 3], dtype=int64)

在这种情况下 :

  • 原始元素的第一个元素是3,这是第三大值,因此它在排序的数组/列表中将具有索引2,因此第一个元素是2
  • 原始元素的第二个元素是1,这是最小值,因此在已排序的数组/列表中将具有索引0,因此第二个元素是0
  • 原始元素的第三个元素是2,这是第二个最小的值,因此在排序后的数组/列表中将具有索引1,因此第三个元素是1
  • 原始元素的第四个元素是4,这是最大值,因此在已排序的数组/列表中将具有索引3,因此最后一个元素是3

我们将创建从0到n-1的另一个索引数组
然后将其压缩到原始数组,然后根据原始值对其进行排序

1
2
3
ar = [1,2,3,4,5]
new_ar = list(zip(ar,[i for i in range(len(ar))]))
new_ar.sort()

`


将numpy导入为np

索引

1
2
3
4
5
S=[11,2,44,55,66,0,10,3,33]

r=np.argsort(S)

[output]=array([5, 1, 7, 6, 0, 8, 2, 3, 4])

argsort按排序顺序返回S的索引

物有所值

1
2
3
np.sort(S)

[output]=array([ 0,  2,  3, 10, 11, 33, 44, 55, 66])