[英]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.