我有一个数字列表:

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

现在如果我对这个列表进行排序得到[1,2,3,5,100]。 我想要的是元素的下标 原始列表的排序顺序,即[0,1,2,4,3] ——ala MATLAB的排序函数,返回两者 值和索引。


当前回答

我用perfplot(我的一个项目)快速检查了这些功能的性能,发现很难推荐其他功能

np.argsort(x)

(注意对数刻度):


代码重现情节:

import perfplot
import numpy as np


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


b = perfplot.bench(
    setup=np.random.rand,
    kernels=[sorted_enumerate, sorted_enumerate_key, sorted_range, np.argsort],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(x)",
)
b.save("out.png")

其他回答

我用perfplot(我的一个项目)快速检查了这些功能的性能,发现很难推荐其他功能

np.argsort(x)

(注意对数刻度):


代码重现情节:

import perfplot
import numpy as np


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


b = perfplot.bench(
    setup=np.random.rand,
    kernels=[sorted_enumerate, sorted_enumerate_key, sorted_range, np.argsort],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(x)",
)
b.save("out.png")

使用Numpy包最简单的方法:

import numpy
s = numpy.array([2, 3, 1, 4, 5])
sort_index = numpy.argsort(s)
print(sort_index)

但是如果你想要你的代码应该使用baisc python代码:

s = [2, 3, 1, 4, 5]
li=[]
  
for i in range(len(s)):
      li.append([s[i],i])
li.sort()
sort_index = []
  
for x in li:
      sort_index.append(x[1])
  
print(sort_index)

如果您不想使用numpy,

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

是最快的,如这里所示。

使用enumerate的答案很好,但我个人不喜欢用lambda来按值排序。下面的操作只是倒转索引和值,并对其排序。首先是按值排序,然后是按下标排序。

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

本质上你需要做一个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处,所以结果的第一个元素是1。 2在原式的下标2处所以结果的第二个元素是2。 3在原矩阵的下标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)

在这种情况下:

the first element of the original is 3, which is the third largest value so it would have index 2 in the sorted array/list so the first element is 2. the second element of the original is 1, which is the smallest value so it would have index 0 in the sorted array/list so the second element is 0. the third element of the original is 2, which is the second-smallest value so it would have index 1 in the sorted array/list so the third element is 1. the fourth element of the original is 4 which is the largest value so it would have index 3 in the sorted array/list so the last element is 3.