簡體   English   中英

用Python中的等效類排序

[英]Sorting with equivalence classes in Python

假設我有一個自定義數據結構Data ,它揭示了兩個相關屬性: tag指示該項目屬於哪個等效類, rank指示該項目的質量。

我有一組無序的Data對象,並且想要檢索具有最高rankn對象,但是每個等效類中最多包含一個對象。

(同等類中的對象不一定比較相等,並且不一定具有相同的rank ,但是我不希望輸出中的任何兩個元素都來自同一個類。換句話說,產生這些等價類不是== 。)

我的第一種方法如下所示:

  • 按降序對列表進行rank
  • 創建一個空的一套s
  • 對於列表中的每個元素:
    • 檢查其tag是否在s 如果是這樣,繼續前進
    • 它添加tag ,以s
    • 屈服那個元素
    • 如果我們產生了n元素,則停止

但是,這感覺很尷尬,就像應該有一些更好的方法(可能使用itertools和高階函數)。 結果n元素的順序並不重要。

這個問題的Python解決方案是什么?

玩具示例:

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.groupbydoc )。 首先,我們根據您的條件對項目進行排序,然后按標簽將其分組(並且僅存儲每個組中的第一項):

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()來指示應針對每個對象進行排序的屬性。

注意:必須定義該屬性類型的比較-這是一個整數。 如果未定義屬性,則必須為該屬性實現gtlet等。 有關詳細信息,請參閱文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM