Python:DIY将这个“all_subsets”函数概括为任何大小的子集

时间:2013-03-02 13:06:40

标签: python algorithm data-mining nested-loops apriori

- 数据关联规则实现玩具Apriori algorithm,我需要一个函数来返回所有子集。

subsets的长度由参数i给出。 我需要为任何i概括此函数。 i 1或2的情况很简单,可以看到一般模式:长度元组列表{ {1}}强制执行命令以防止重复。

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嵌套循环。任何提示或完整答案都会被视为很棒。

4 个答案:

答案 0 :(得分:4)

您可以使用itertools.combinations()

,而不是自己推出

答案 1 :(得分:1)

然后你没有做Apriori

在Apriori中,你从不枚举大小为k的所有子集,k = 1除外。

在任何较大的尺寸中,您根据Apriori-Gen 构建组合。

效率更高,实际上至少和手动构建所有组合一样简单。

这是一个例子。假设经常发现以下项目集:

 ABCD
 ABCF
 ABEF
 ABDF
 ACDF
 BCDF

然后apriori将只构造一个单个候选者(通过前缀规则!):

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

然后接下来将检查其他子集是否也经常被发现,即

 BCDF
 ACDF
 ABDF

由于所有这些候选人都在上一轮中,所以这个候选人幸存下来并将在下一个线性扫描中对数据集进行测试。

Apriori 所有关于而不是必须检查所有大小为k 的子集,但只有那些有机会频繁的,因为以前的知识< / em>的

答案 2 :(得分:1)

您在评论中提到代码here对您来说是不透明的。但它可能是实现你所瞄准的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构造;仅当else循环正常终止时才执行for块。)

    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)

这种方法有几种变化;在另一个方面,你向前迈进,增加第一个与它和后面的索引之间有“间隙”的索引,并重置较低的索引,而不是通过索引向后退。

最后,你可以也递归地定义它,虽然实际上,递归定义可能效率不高。

答案 3 :(得分:0)

好的,这是我自己推出的版本:

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']]