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