[英]Sorting with equivalence classes in Python
假設我有一個自定義數據結構Data
,它揭示了兩個相關屬性: tag
指示該項目屬於哪個等效類, rank
指示該項目的質量。
我有一組無序的Data
對象,並且想要檢索具有最高rank
的n
對象,但是每個等效類中最多包含一個對象。
(同等類中的對象不一定比較相等,並且不一定具有相同的rank
,但是我不希望輸出中的任何兩個元素都來自同一個類。換句話說,產生這些等價類不是==
。)
我的第一種方法如下所示:
rank
s
tag
是否在s
; 如果是這樣,繼續前進 tag
,以s
n
元素,則停止 但是,這感覺很尷尬,就像應該有一些更好的方法(可能使用itertools
和高階函數)。 結果n
元素的順序並不重要。
玩具示例:
Data = namedtuple('Data', ('tag', 'rank'))
n = 3
algorithm_input = { Data('a', 200), Data('a', 100), Data('b', 50), Data('c', 10), Data('d', 5) }
expected_output = { Data('a', 200), Data('b', 50), Data('c', 10) }
您可以使用itertools.groupby
( doc )。 首先,我們根據您的條件對項目進行排序,然后按標簽將其分組(並且僅存儲每個組中的第一項):
from itertools import groupby
from collections import namedtuple
Data = namedtuple('Data', ('tag', 'rank'))
n = 3
algorithm_input = { Data('a', 200), Data('a', 100), Data('b', 50), Data('c', 10), Data('d', 5) }
# 1. sort the data by rank (descending) and tag (ascending)
s = sorted(algorithm_input, key=lambda k: (-k.rank, k.tag))
# 2. group the data by tag and store first item from each group to 'out', limit the number of groups to 'n'
out = []
for (_, g), _ in zip(groupby(s, lambda k: k.tag), range(n)):
out.append(next(g))
print(out)
打印:
[Data(tag='a', rank=200), Data(tag='b', rank=50), Data(tag='c', rank=10)]
編輯:更改了排序鍵。
我認為采用每個組的最大元素( O(|elements|)
)然后獲得n個最大的秩( O(|groups|.lg n)
的堆大小為n
)會O(|groups|.lg n)
,而不是首先排序( O(|elements|.lg |elements|)
),然后取n
元素( O(|elements|)
):
創建一個dict max_by_tag
,以按標簽存儲最高排名的商品:
>>> from collections import namedtuple
>>> Data = namedtuple('Data', ('tag', 'rank'))
>>> n = 3
>>> algorithm_input = { Data('a', 200), Data('a', 100), Data('b', 50), Data('c', 10), Data('d', 5) }
>>> max_by_tag = {}
>>> for item in algorithm_input:
... if item.tag not in max_by_tag or item.rank > max_by_tag[item.tag].rank:
... max_by_tag[item.tag] = item
>>> max_by_tag
{'a': Data(tag='a', rank=200), 'b': Data(tag='b', rank=50), 'c': Data(tag='c', rank=10), 'd': Data(tag='d', rank=5)}
然后使用heapq
模塊:
>>> import heapq
>>> heapq.nlargest(n, max_by_tag.values(), key=lambda data: data.rank)
[Data(tag='a', rank=200), Data(tag='b', rank=50), Data(tag='c', rank=10)]
將排序后的輸入存儲在OrderedDict
(以tag
作為鍵,並將Data
作為值)。 這將導致每個等效類中只有一個Data
存儲在OrderedDict
>>> from collections import namedtuple, OrderedDict
>>> Data = namedtuple('Data', ('tag', 'rank'))
>>> n = 3
>>> algorithm_input = { Data('a', 200), Data('a', 100), Data('b', 50), Data('c', 10), Data('d', 5) }
>>>
>>> set(list(OrderedDict((d.tag, d) for d in sorted(algorithm_input)).values())[:n])
{Data(tag='b', rank=50), Data(tag='a', rank=200), Data(tag='c', rank=10)}
如果這是您控制的類定義,那么我相信最Python化的方式是這樣的:
from random import shuffle
class Data:
def __init__(self, order=1):
self.order = order
def __repr__(self):
return "Order: " + str(self.order)
if __name__ == '__main__':
import sys
d = []
for i in range(0,10):
d.append(Data(order=i))
shuffle(d)
print(d)
print(sorted(d, key=lambda data: data.order))
輸出:
[Order: 5, Order: 2, Order: 6, Order: 0, Order: 4, Order: 7, Order: 3, Order: 9, Order: 1, Order: 8]
[Order: 0, Order: 1, Order: 2, Order: 3, Order: 4, Order: 5, Order: 6, Order: 7, Order: 8, Order: 9]
因此,從本質上講,添加一個屬性以對類進行排序。 定義字符串rep(只是為了更輕松地了解正在發生的事情)。 然后在帶有lambda函數的對象列表上使用python的sorted()來指示應針對每個對象進行排序的屬性。
注意:必須定義該屬性類型的比較-這是一個整數。 如果未定義屬性,則必須為該屬性實現gt , let等。 有關詳細信息,請參閱文檔 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.