Python - 使用另一个列表对元组列表进行排序

时间:2017-11-30 08:40:58

标签: python list sorting tuples

我有一个元组列表to_order,例如:

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

一个列表,它给出了应用于to_order每个元组的第二个元素的顺序:

order = [2, 1, 3]

所以我正在寻找获得此输出的方法:

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

有什么想法吗?

4 个答案:

答案 0 :(得分:19)

您可以提供key来检查order中的索引(第二个元素)并根据它进行排序:

to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
order = [2, 1, 3]
print(sorted(to_order, key=lambda item: order.index(item[1]))) # [(2, 2), (3, 2), (0, 1), (1, 3)]

修改

因为,关于时间复杂性的讨论开始了......在这里,以下算法在O(n+m)中运行,使用Eric的输入示例:

N = 5
to_order = [(randrange(N), randrange(N)) for _ in range(10*N)]
order = list(set(pair[1] for pair in to_order))
shuffle(order)


def eric_sort(to_order, order):
    bins = {}

    for pair in to_order:
        bins.setdefault(pair[1], []).append(pair)

    return [pair for i in order for pair in bins[i]]


def alfasin_new_sort(to_order, order):
    arr = [[] for i in range(len(order))]
    d = {k:v for v, k in enumerate(order)}
    for item in to_order:
        arr[d[item[1]]].append(item) 
    return [item for sublist in arr for item in sublist]


from timeit import timeit
print("eric_sort", timeit("eric_sort(to_order, order)", setup=setup, number=1000))
print("alfasin_new_sort", timeit("alfasin_new_sort(to_order, order)", setup=setup, number=1000))

输出:

eric_sort 59.282021682999584
alfasin_new_sort 44.28244407700004

答案 1 :(得分:18)

算法

您可以根据第二个元素在列表的dict中分发元组,并迭代order索引以获取排序列表:

from collections import defaultdict
to_order = [(0, 1), (1, 3), (2, 2), (3, 2)]
order = [2, 1, 3]

bins = defaultdict(list)

for pair in to_order:
    bins[pair[1]].append(pair)

print(bins)
# defaultdict(<class 'list'>, {1: [(0, 1)], 3: [(1, 3)], 2: [(2, 2), (3, 2)]})

print([pair for i in order for pair in bins[i]])
# [(2, 2), (3, 2), (0, 1), (1, 3)]

sortindex不需要,输出稳定。

此算法类似于假定的duplicate中提到的mapping。只有当to_orderorder具有相同的长度时,此相关联的答案才有效,而OP的问题并非如此。

性能

此算法在to_order的每个元素上迭代两次。复杂性为O(n)。 @ alfasin的第一个算法慢得多(O(n * m * log n)),但他的第二个算法也是O(n)

这是01000之间10000个随机对的列表。我们提取唯一的第二个元素并对其进行随机播放以定义order

from random import randrange, shuffle
from collections import defaultdict
from timeit import timeit
from itertools import chain

N = 1000
to_order = [(randrange(N), randrange(N)) for _ in range(10*N)]
order = list(set(pair[1] for pair in to_order))
shuffle(order)


def eric(to_order, order):
    bins = defaultdict(list)
    for pair in to_order:
        bins[pair[1]].append(pair)
    return list(chain.from_iterable(bins[i] for i in order))


def alfasin1(to_order, order):
    arr = [[] for i in range(len(order))]
    d = {k:v for v, k in enumerate(order)}
    for item in to_order:
        arr[d[item[1]]].append(item) 
    return [item for sublist in arr for item in sublist]

def alfasin2(to_order, order):
    return sorted(to_order, key=lambda item: order.index(item[1]))

print(eric(to_order, order) == alfasin1(to_order, order))
# True
print(eric(to_order, order) == alfasin2(to_order, order))
# True

print("eric", timeit("eric(to_order, order)", globals=globals(), number=100))
# eric 0.3117517130003762
print("alfasin1", timeit("alfasin1(to_order, order)", globals=globals(), number=100))
# alfasin1 0.36100843100030033
print("alfasin2", timeit("alfasin2(to_order, order)", globals=globals(), number=100))
# alfasin2 15.031453827000405

答案 2 :(得分:3)

另一个解决方案: [item for key in order for item in filter(lambda x: x[1] == key, to_order)]

此解决方案首先使用order,对to_order中的每个key进行过滤order

等同于:

ordered = []
for key in order:
    for item in filter(lambda x: x[1] == key, to_order):
        ordered.append(item)

更短,但我还没有意识到使用列表理解的方法:

ordered = []
for key in order:
    ordered.extend(filter(lambda x: x[1] == key, to_order))

注意:如果ValueError包含to_order x不在x[1]的元组order,则不会抛出{{1}}。

答案 3 :(得分:2)

我个人更喜欢list对象sort函数,而不是内置sort,它会生成新列表而不是更改列表。

to_order = [(0, 1), (1, 3), (2, 2), (3,2)]
order = [2, 1, 3]
to_order.sort(key=lambda x: order.index(x[1]))
print(to_order)
>[(2, 2), (3, 2), (0, 1), (1, 3)]
  

关于方式的一点解释:排序方法的key参数基本上是preprocesses列表,ranks基于度量的所有值。在我们的例子中,order.index()查看当前处理的项目的第一次出现并返回其位置。

x = [1,2,3,4,5,3,3,5]
print x.index(5)
>4