繁体   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