Apriori算法的python实现:通过限制候选产生发现频繁项集

Apriori算法是Agrawl和R.Srikant于1994年提出的,为布尔关联规则挖掘频繁项集的原创性算法[AS94b]。该算法使用了频繁项集性质的先验知识,使用了一种称为逐层迭代方法。为了提高频繁项集逐层产生的效率,该算法使用了先验性质 用于压缩搜索空间。

先验性质 :频繁项集的所有非空子集也一定是频繁的。

Apriori算法挖掘频繁项集主要由两步组成——连接步 剪枝步
参考链接:关联规则,Apriori算法及python实现
原文中没有进行剪枝的操作,本文主要添加了剪枝部分的代码,通过遍历候选项集的子集,加入该项的子集不在频繁项集中,则该项不是频繁项,将其删去。

# -*- coding: utf-8 -*-
import copy

def PowerSetsBinary(items):
    """ 找出集合的所有子集 """
    #generate all combination of N items
    N = len(items)
    #enumerate the 2**N possible combinations
    for i in range(2**N):
        combo = []
        for j in range(N):
            #test jth bit of integer i
            if(i >> j ) % 2 == 1:
                combo.append(items[j])
        yield combo


def loadDataSet():
    """ 创建一个用于测试的简单的数据集 """
    D = [[1, 2, 5], [2, 4], [2, 3], [1, 2, 4], [1, 3], [2, 3], [1, 3], [1, 2, 3, 5], [1, 2, 3]]
    return D


def createC1(dataSet):
    """ 构建初始候选项集的列表,即所有候选项集只包含一个元素, C1是大小为1的所有候选项集的集合 """
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if [item] not in C1:
                C1.append([item])
    C1.sort()
    # return map( frozenset, C1 )
    # return [var for var in map(frozenset,C1)]
    return [frozenset(var) for var in C1]


def scanDataSet(D, Ck, minSupport):
    """ 计算Ck中的项集在数据集合D(记录或者transactions)中的支持度, 返回满足最小支持度的项集的集合,和所有项集支持度信息的字典。 """
    subSetCount = {}
    # D=[{},{},{}] tid.type==set
    for tid in D:
        # Ck = [{},{},{}],can.type==frozenset
        for can in Ck:
            # 检查候选k项集中的每一项的所有元素是否都出现在每一个事务中,若true,则加1
            if can.issubset(tid):
                # subSetCount为候选支持度计数,get()返回值,如果值不在字典中则返回默认值0。
                subSetCount[can] = subSetCount.get(can, 0) + 1
    numItems = float(len(D))
    returnList = []
    # 选择出来的频繁项集,未使用先验性质
    supportData = {}
    for key in subSetCount:
        # 计算绝对支持度。
        support = subSetCount[key] / numItems  # 每个项集的支持度
        if support >= minSupport:  # 将满足最小支持度的项集,加入returnList
            returnList.insert(0, key)
        supportData[key] = support  # 汇总支持度数据
    return returnList, supportData


def aprioriGen(Lk, k):  # Aprior算法
    """ 由初始候选项集的集合Lk生成新的生成候选项集, k表示生成的新项集中所含有的元素个数 """
    returnList = []
    for i in range(len(Lk)):
        L1 = list(Lk[i])[: k - 2]
        for j in range(i + 1, len(Lk)):
            # Lk[i].type == frozenset
            # 只需取前k-2个元素相等的候选频繁项集即可组成元素个数为k+1的候选频繁项集
            L2 = list(Lk[j])[: k - 2]
            L1.sort()
            L2.sort()
            if L1 == L2:
                # print("k:{}---L1:{}---L2:{}".format(k, Lk[i], Lk[j]))
                # 返回一个包含Lk[i]和Lk[j]中每一个元素的集合set,相当于集合的union方法
                returnList.append(Lk[i] | Lk[j])

    # print("returnList:{}".format(returnList))
    return returnList

def has_infrequent_subset(L, Ck, k):
    # 这里涉及到深拷贝、浅拷贝的知识
    Ckc = copy.deepcopy(Ck)
    for i in Ck:
        p = [t for t in i]
        i_subset = PowerSetsBinary(p)
        subsets = [i for i in i_subset]
        # print(subsets)
        for each in subsets:
            # print(each)
            if each!=[] and each!=p and len(each)<k:
                # [t for z in L for t in z]将列表中的frozenset全部移到一层中
                if frozenset(each) not in [t for z in L for t in z]:
                    Ckc.remove(i)
                    break
    return Ckc


def apriori(dataSet, minSupport):
    # 构建初始候选项集C1
    C1 = createC1(dataSet)
    # 将dataSet集合化,以满足scanDataSet的格式要求
    D = [set(var) for var in dataSet]
    # 构建初始的频繁项集,即所有项集只有一个元素
    L1, suppData = scanDataSet(D, C1, minSupport)
    # 最初的L1中的每个项集含有一个元素,新生成的
    L = [L1]
    # 项集应该含有2个元素,所以 k=2
    k = 2

    while (len(L[k - 2]) > 0):
        Ck = aprioriGen(L[k - 2], k)
        # 剪枝
        Ck2 = has_infrequent_subset(L, Ck, k)
        # 候选支持度计数和min_sup进行比较
        Lk, supK = scanDataSet(D, Ck2, minSupport)
        # 将新的项集的支持度数据加入原来的总支持度字典中
        suppData.update(supK)
        # 将符合最小支持度要求的项集加入L
        L.append(Lk)
        # 新生成的项集中的元素个数应不断增加
        k += 1
    # 返回所有满足条件的频繁项集的列表,和所有候选项集的支持度信息
    return L[:-1], suppData


if __name__ == '__main__':
    myDat = loadDataSet()
    L, suppData = apriori(myDat, 0.22)
    print("频繁项集L:", L)

挖掘出频繁项集之后,接下来进行第二步关联规则的挖掘。请参看第二篇文章由频繁项集产生关联规则

点赞