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


199

我有一个数字列表:

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

现在,如果我对该列表进行排序以获得[1, 2, 3, 5, 100]。我想要的是按排序顺序排列的原始列表中元素的索引,即[0, 1, 2, 4, 3] --- ala MATLAB的sort函数,它既返回值又返回索引。



@unutbu这不是欺骗(IMO)。这个问题并不矛盾,用Numpy.argsort()
阿米特

@amit:“不矛盾”是什么意思?
unutbu 2015年

@unutbu Numpy.argsort()是此问题的一个很好的答案,它可能是对链接的另一个线程(您也关闭了该线程,但我想您不应该拥有的线程)的欺骗,而不是您提到的Numpy。 argsort()可以很好地解决这两个问题,但不适用于您所引用的一个问题。
阿米特(Amit)2015年

1
不幸的是,该问题在示例选择方面存在严重缺陷,因为当输入只是排序不正确的换位时,阅读问题的两种不同方式会给出相同的答案。

Answers:



147

如下所示:

>>> 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) 给您一个包含(索引,值)元组的列表:

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

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


7
您可以使用itemgetter(1),而不是lambda函数
约翰·拉ROOY

4
@gnibbler引用模块FYI中的itemgetter功能operator。因此,请from operator import itemgetter使用它。
Lauritz V. Thaulow

1
您可以使用zip取得排序后的列表和索引:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.

@RomanBodnarchuk这不起作用,x = [3,1,2]; numpy.argsort(x)产生[1,2,0]。
shahar_m


24

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

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

11

使用enumerate和更新了答案itemgetter

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

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

或者用itemgetteroperatormodule`:

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

1
在这种情况下,枚举似乎比zip更合适
njzk2

10

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

在此处输入图片说明


复制剧情的代码:

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)",
)

6

如果您不想使用numpy,

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

是最快的,这表现在这里


5

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

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

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

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

>>> 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的实现,因此我将根据此处的基准答案来概述最快的解决方案

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

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

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

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

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

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

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

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

在这种情况下,您需要申请argsort 两次

>>> 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

4

其他答案是错误的。

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

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

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

答案应该是运行argsort两次:

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

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


您的主张使x[2](3)成为最小元素,而x[1](1)成为最大元素(因为对整数进行排序将它们从最小值到最大值排序)。同样,在OP的示例中,单个np.argsort([1, 2, 3, 100, 5])yields array([0, 1, 2, 4, 3])似乎是OP想要的索引。
0:0

1
@ 0 0您的示例是一个特定的情况。如果我们运行,arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)那么我们会得出[0 1 2 4 5 3]错误的信息。
shahar_m

我不清楚发生了什么问题:arr[res]yields array([ 1, 2, 3, 5, 9, 100]),这似乎很好,因为结果数组的顺序是(递增)。
0:0

@ 0 0 for arr=[1,2,3,100, 5, 9],我希望输出是inds=[0,1,2,5,3,4],因为这是您将元素排序的顺序(越来越多)-1位于0位置,2位于第一位置,....,5位于第三名,第四名9。为了获得该输出(inds),我需要运行argsort两次,如前所述。
shahar_m

因此,这些索引是对数组元素的排名(第0位,第1位等)。给定OP对MATLABsort的提及,我认为OP希望使用其他功能,就像np.argsort通常使用的那样(可以使用该功能arr[np.argsort[arr]]来获得排序的数组,如上一个MATLAB示例所示)。您的答案改为适用于这种情况/问题
0

0

将numpy导入为np

索引

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的索引

物有所值

np.sort(S)

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

0

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

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

`

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.