排列数字列表,允许联系

时间:2017-01-31 10:21:27

标签: python list

假设我有一个这样的列表:

newIndexVertList = [0, 1, 2, 2, 1, 20, 21, 21, 20, 3, 23, 22]

我想将其转换为:

newIndexVertList = [0, 1, 2, 2, 1, 4, 5, 5, 4, 3, 7, 6]

此处,转换基于原始列表中按升序排列的数字位置。因此,在新的列表中,数字将根据逻辑替换:

0   -->  0    0th position in sorted list
1   -->  1    1st position in sorted list
2   -->  2    2nd position in sorted list
3   -->  3    3rd position in sorted list
20  -->  4    4th position in sorted list
21  -->  5    5th position in sorted list
22  -->  6    6th position in sorted list
23  -->  7    7th position in sorted list

以下是我的代码:

c = 0
for i in xrange(len(newIndexVertList)):
    if c < newIndexVertList[i]:
        newIndexVertList[i] = c
        c += 1
        continue
    elif c == newIndexVertList[i]:
        c += 1
        continue
    else:
        continue

# actual output:   [0, 1, 2, 2, 1, 3, 4, 5, 6, 3, 7, 8]
# expected output: [0, 1, 2, 2, 1, 4, 5, 5, 4, 3, 7, 6]

我的代码有什么问题?实现这一目标的优雅方式是什么?

由于我的顶点列表将在100k范围内,我正在寻找最快的执行。

2 个答案:

答案 0 :(得分:7)

您可以通过创建中间dict对象来实现该目标,以使用sorted()set()使用enumerate()将数字与其在原始列表中的位置进行映射:

>>> my_list = [0, 1, 2, 2, 1, 20, 21, 21, 20, 3, 23, 22]
>>> num_map  = {j: i for i, j in enumerate(sorted(set(my_list)))}
#                                             ^    ^ to get unique elements
#                                             ^ sort numbers in ascending order

>>> [num_map[n] for n in my_list]
[0, 1, 2, 2, 1, 4, 5, 5, 4, 3, 7, 6]

Stefan评论,可以使用map()以单行格式实现:

list(map({j: i for i, j in enumerate(sorted(set(my_list)))}.get, my_list))
# ^ type-cast `map` object to `list` for Python 3.x compatibility

答案 1 :(得分:3)

您在评论中提到您的数据很大(100k)并且您正在寻找最快的执行。你应该考虑使用numpy:

>>> vertices = [0, 1, 2, 2, 1, 20, 21, 21, 20, 3, 23, 22]
>>> np.unique(vertices, return_inverse=True)[1]
array([0, 1, 2, 2, 1, 4, 5, 5, 4, 3, 7, 6])

对于随机分布在0到100之间的100k长整数数组,这比当前接受的答案快3倍以上。

Python聊天室中用户DSM建议的另一个高性能选项是使用scipy.stats对数据进行排名:

>>> import scipy.stats
>>> (scipy.stats.rankdata(vertices, 'dense') - 1).astype(int)
array([0, 1, 2, 2, 1, 4, 5, 5, 4, 3, 7, 6])