Python 给一个;混杂的;列表L,如果L已排序,则获取一个列表,其中每个元素都是L的对应元素的索引

Python 给一个;混杂的;列表L,如果L已排序,则获取一个列表,其中每个元素都是L的对应元素的索引,python,Python,期望输出 我想要一个函数返回一个列表,这样,给定一个“混乱”列表l,如果l已排序,则每个元素都是l对应元素的索引。(很抱歉,我想不出一种不那么复杂的说法。) 示例 f([3,1,2])=[2,0,1] f([3,1,2,2,3])=[3,0,1,2,4],因为排序的输入是[1,2,2,3,3] (这对于某些统计数据计算非常有用。) 我的尝试 我想出了一个实现这个函数的方法,但这是python——似乎应该有一个单行程序来实现这个功能,或者至少有一个更干净、更清晰的方法 def getIndicie

期望输出 我想要一个函数返回一个列表,这样,给定一个“混乱”列表
l
,如果
l
已排序,则每个元素都是
l
对应元素的索引。(很抱歉,我想不出一种不那么复杂的说法。)

示例

f([3,1,2])
=
[2,0,1]

f([3,1,2,2,3])
=
[3,0,1,2,4]
,因为排序的输入是
[1,2,2,3,3]

(这对于某些统计数据计算非常有用。)

我的尝试 我想出了一个实现这个函数的方法,但这是python——似乎应该有一个单行程序来实现这个功能,或者至少有一个更干净、更清晰的方法

def getIndiciesInSorted(l):
    sortedL = sorted(l)
    outputList = []
    for num in l:
        sortedIndex = sortedL.index(num)
        outputList.append(sortedIndex)
        sortedL[sortedIndex] = None
    return outputList

l=[3,1,2,2,3] 
print getIndiciesInSorted(l)

那么,我怎样才能写得更简洁呢?有一个易读的列表理解解决方案吗?

这是我想到的最好的解决方案:

def get_sorted_indices(l):
    sorted_positions = sorted(range(len(l)), key=l.__getitem__)
    result = [None for _ in range(len(l))]

    for new_index, old_index in enumerate(sorted_positions):
        result[old_index] = new_index

    return result
它比您的解决方案快,但仅此而已

def argsort(seq):
    # http://stackoverflow.com/questions/3382352/3382369#3382369
    # http://stackoverflow.com/questions/3071415/3071441#3071441
    '''
    >>> seq=[1,3,0,4,2]
    >>> index=argsort(seq)
    [2, 0, 4, 1, 3]

    Given seq and the index, you can construct the sorted seq:
    >>> sorted_seq=[seq[x] for x in index]
    >>> assert sorted_seq == sorted(seq)

    Given the sorted seq and the index, you can reconstruct seq:
    >>> assert [sorted_seq[x] for x in argsort(index)] == seq
    '''
    return sorted(range(len(seq)), key=seq.__getitem__)

def f(seq):
    idx = argsort(seq)
    return argsort(idx)

print(f([3,1,2]))
# [2, 0, 1]

print(f([3,1,2,2,3]))
# [3, 0, 1, 2, 4]

请注意,nightcracker的功能更快:

def get_sorted_indices(l):
    sorted_positions = sorted(range(len(l)), key=l.__getitem__)
    result = [None for _ in range(len(l))]
    for new_index, old_index in enumerate(sorted_positions):
        result[old_index] = new_index
    return result
对于长列表而言,差异可能很大:

In [83]: import random
In [98]: l = [random.randrange(100) for _ in range(10000)]
In [104]: timeit get_sorted_indices(l)
100 loops, best of 3: 4.73 ms per loop

In [105]: timeit f(l)
100 loops, best of 3: 6.64 ms per loop

有一行理解,但它真的很难看:

>>> E, S = enumerate, sorted
>>> l = [3,1,2,2,3]
>>> [a for _,a in S((a,b) for b,(_,a) in E(S((a,b) for b,a in E(l))))]
[3, 0, 1, 2, 4]

更易于阅读,产生的垃圾更少。

如果您正在进行统计计算,您可能会在某个时候开始使用numpy。使用numpy,您可以使用argsort的现有实现:

k = [3, 0, 1, 2, 4]
initial = dict(zip(k, range(len(k)))) #{0: 1, 1: 2, 2: 3, 3: 0, 4: 4}
sorted_initial = dict(zip(sorted(k), range(len(k)))) #{0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
initial.update(sorted_initial) #{0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
result = [initial[i] for i in k] #[3, 0, 1, 2, 4]
>>> from numpy import array
>>> x = array([3, 1, 2, 2, 3])
>>> x.argsort().argsort()
array([3, 0, 1, 2, 4])
这是联合国大学答案的一个简单版本。nightcracker的答案可以实现为

>>> from numpy import array, empty_like, arange
>>> x = array([3, 1, 2, 2, 3])
>>> s = x.argsort()
>>> r = empty_like(s)
>>> r[s] = arange(x.size)
>>> r
array([3, 0, 1, 2, 4])

请注意,这与排序(范围(len(l)),key=lambda i:l[i])不同。。。。(虽然这是我的第一个想法。)@nightcracker我知道,你的答案也是我第一次尝试,所以我想我应该留下一条评论,让其他人知道这种方法是不可行的。如果速度是一个因素,那么这会排序两次,我的版本可能会更快(另一方面,排序代码是用C编写的,这样可能会让你的速度更快)。这种方法唯一的困难是序列中的项必须是可散列的。+1:的确,你的方法更快,尤其是对于较长的列表。(注:我认为
[None for in len(l)]
需要
[None for in range(len(l)]
。此外,如果将
key=lambda…
更改为
key=l,代码会更快一些。@unutbu:合并了您建议的更改。或者只是
result=[None]*len(l)
@Warren这更简洁,但列表乘法通常是一个坏主意。尽管在这种情况下它会很好,但在一个问答网站上,有很多新的Pythonistas,我倾向于认为最好不要演示列表乘法,以免有人试图推广列表乘法,最终导致巨大的Python山因此,在Python中与共享引用有关的问题。