簡體   English   中英

Python:DIY將此“ all_subsets”函數推廣為任何大小的子集

[英]Python : DIY generalize this “all_subsets” function to any size subsets

小型數據關聯規則礦實現玩具Apriori算法 ,我需要一個函數來返回所有子集。

子集的長度由參數i給出。 我需要對任何i推廣該功能。 i 1或2的情況是微不足道的,並且可以看到一般的模式:長度為i的元組的列表,為了防止重復,對它們進行了排序。

def all_subsets(di,i):
        if i == 1:
                return di
        elif i == 2:
                return [(d1,d2) for d1 in di for d2 in di if d1 < d2]
        else:
                return [ ... ]

我怎么能概括這個i嵌套循環模式以簡潔的方式,說使用列表解析,發電機或一些“函數式編程”的概念?

我在想某種功能列表,但我真的不知道我怎么能概括i嵌套的循環。 任何提示或完整答案將被視為真棒。

您可以使用itertools.combinations()代替自己開發。

那你就不做Apriori了

在Apriori中,除了k = 1外,您永遠不會枚舉大小為k的所有子集。

在任何較大的尺寸中,您都可以根據Apriori-Gen 構建組合。

這樣效率更高,實際上至少和手動構建所有組合一樣容易。

這是一個例子。 假設以下項目集很常見:

 ABCD
 ABCF
 ABEF
 ABDF
 ACDF
 BCDF

然后apriori將只構造一個候選(按前綴規則!):

 ABC + D   - ABC + D + F
 ABC + F   /

然后接下來將檢查是否也經常發現其他子集,即

 BCDF
 ACDF
 ABDF

由於所有這些對象都在上一輪中,因此該候選對象可以幸存,並將在下一次對數據集進行線性掃描時進行測試。

先驗是所有關於不必檢查大小為k的所有子集 ,但只有那些有機會頻繁,鑒於以前的知識

您在評論中提到這里的代碼對您來說是不透明的。 但這可能是實現您想要的那種combinations功能的最佳方法,並且值得理解,因此,我將嘗試對其進行詳細說明。

基本思想是,給定一個序列和許多可供選擇的項目,我們可以將每個組合表示為給定序列中的索引序列。 例如,假設我們有一個列表['a', 'b', 'c', 'd', 'e'] ,並且我們想從該列表中生成兩個值的所有組合。

我們的第一個組合看起來像這樣...

['a', 'b', 'c', 'd', 'e']
  ^    ^

...並由索引[0, 1]的列表表示。 我們的下一個組合如下所示:

['a', 'b', 'c', 'd', 'e']
  ^         ^

並且由索引[0, 2]的列表表示。

我們繼續將第二個插入符向前移動,將第一個插入符保持在適當的位置,直到第二個插入符到達末尾為止。 然后,我們將第一個插入號移動到索引1並通過將第二個插入號移動回索引2來“重置”該過程。

['a', 'b', 'c', 'd', 'e']
       ^    ^

然后,我們重復此過程,將第二個插入號向前移動直到到達末尾,然后將第一個插入號向前移動一個並重置第二個。

現在,我們必須弄清楚如何通過操作索引列表來做到這一點。 事實證明,這很簡單。 最終的組合如下所示:

['a', 'b', 'c', 'd', 'e']
                 ^    ^

並且其索引表示將是[3, 4] 這些是索引的最大可能值,並且等於i + n - r ,其中i是列表中的位置, n是值的數量(在這種情況下為5 ), r是選項的數量( 2在這種情況下)。 因此,一旦特定索引達到該值,它就不能再升高,並且需要“重置”。

考慮到這一點,下面是對代碼的分步分析:

def combinations(iterable, r):
    pool = tuple(iterable)
    n = len(pool)

首先,基於上述示例給出的輸入, pool將是我們上面轉換為元組的字符列表,而n只是池中的項目數。

if r > n:
    return

我們不能從n項目列表中選擇n個以上的項目而不進行替換,因此在這種情況下我們只返回即可。

indices = range(r)

現在我們有了索引,將其初始化為第一個組合( [0, 1] )。 所以我們產生它:

yield tuple(pool[i] for i in indices)

然后,我們使用無限循環生成其余組合。

while True:

在循環內部,我們首先向后瀏覽索引列表,以搜索尚未達到最大值的索引。 我們使用上述公式( i + n - r )確定給定索引的最大值。 如果我們發現一個尚未達到最大值的索引,那么我們就會跳出循環。

    for i in reversed(range(r)):
        if indices[i] != i + n - r:
            break

如果找不到一個索引,則意味着所有索引都處於最大值,因此我們已經完成了迭代。 (這使用鮮為人知的for-else構造;只有在for循環正常終止時才執行else塊。)

    else:
        return

所以現在我們知道索引i需要增加:

    indices[i] += 1

另外, i之后的索引全部為最大值,因此需要重置。

    for j in range(i+1, r):
        indices[j] = indices[j-1] + 1

現在我們有了下一組索引,因此我們得出了另一個組合。

    yield tuple(pool[i] for i in indices)

這種方法有多種變體。 在另一種方法中,您將向前移動,而不是向后瀏覽索引,而是遞增第一個索引與在其后一個索引之間具有“間隙”的第一個索引,並重置較低的索引。

最后,您還可以遞歸定義這個,雖然務實,遞歸定義可能不會被視為有效。

好的,這是我自己發布的版本:

def all_subsets(source,size):
        index = len(source)
        index_sets = [()]
        for sz in xrange(size):
                next_list = []
                for s in index_sets:
                        si = s[len(s)-1] if len(s) > 0 else -1
                        next_list += [s+(i,) for i in xrange(si+1,index)]
                index_sets = next_list
        subsets = []
        for index_set in index_sets:
                rev = [source[i] for i in index_set]
                subsets.append(rev)
        return subsets

產量:

>>> Apriori.all_subsets(['c','r','i','s'],2)
[['c', 'r'], ['c', 'i'], ['c', 's'], ['r', 'i'], ['r', 's'], ['i', 's']]

暫無
暫無

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

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