我想要一个函数返回一个列表,以便给定一个“混乱”列表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)
那么,我怎样才能更简洁地写出来呢?是否有清晰易读的清单解决方案?
答案 0 :(得分:5)
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
答案 1 :(得分:4)
这是我提出的最好的:
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
它比你的解决方案更快,但就是这样。
答案 2 :(得分:2)
有一个单行理解,但它真的很难看:
>>> 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]
Unutbu's answer更容易阅读并减少垃圾。
答案 3 :(得分:2)
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]
答案 4 :(得分:2)
如果你正在进行统计计算,你可能会在某个时候开始使用numpy。使用numpy,您可以使用现有的argsort实现:
>>> from numpy import array
>>> x = array([3, 1, 2, 2, 3])
>>> x.argsort().argsort()
array([3, 0, 1, 2, 4])
这是unutbu的答案的numpy版本。 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])