MLiA笔记_apriori算法

# -*-coding:utf-8-*-

# 11.1 辅助函数
# loadDataSet()函数创建了一个用于测试的简单数据集
def loadDstaSet():
    return [[2,3,4],[2,3,5],[1,2,3,5],[2,5]]

# createC1()函数构建集合C1,C1是大小为1的所有候选项集的集合
def createC1(dataSet):
    # 首先构建C1,存储所有不重复的项值
    C1 = []
    # 遍历所有交易记录
    for transaction in dataSet:
        # 对每一条记录,遍历记录中方的每一个项。
        for item in transaction:
            if not [item] in C1:
                # 这里不是简单地添加每个物品项,而是添加只包含该物品项的一个列表。
                # 目的是为每个物品构建一个集合,在apriori算法的后续处理中,需要做集合操作
                # python不能创建只有一个整数的集合,因此在这里必须使用列表
                C1.append([item])
    # 最后对大列表进行排序并将其中的每个单元索引列表映射到frozenset()
    C1.sort()
    # 而后续的项集列表则是按一定的格式frozenset存放的,就是说它们是不能改变的,即用户不能修改它们
    # 最后返回frozenset的列表
    return map(frozenset, C1)


#  然后扫描数据集来判断这些只有一个元素的项集是否满足最小支持度的要求
# 那些满足最低要求的项集构成集合L1,而L1中的元素相互组合构成C2,C2再进一步过滤变为L2
# 由于算法一开始是从输入数据中提取候选项集列表,所以这里需要一个特殊的函数来处理,
# scanD(包含候选集合的列表,数据集Ck,感兴趣项集的最小支持度minSupport(用于C1生成L1))
def scanD(D,Ck, minSupport):
    # 首先创建空字典ssCnt
    ssCnt = {}
    # 遍历数据集中的所有交易记录,
    for tid in D:
        # 以及C1中的所有候选集
        for can in Ck:
            # 如果C1中的集合时记录的一部分,那么增加字典中对应的计数值,这里字典的键就是集合
            if can.issubset(tid):
                if not ssCnt.has_key(can):ssCnt[can]=1
                else:ssCnt[can] += 1
    numItems = float(len(D))
    # 构建空列表,包含满足最小支持度要求的集合
    retList = []
    suppportData = {}
    # 循环遍历字典中的每个元素并且计算支持度
    for key in ssCnt:
        suppport = ssCnt[key]/numItems
        # 如果支持度满足最小支持度要求,则将字典元素添加到retList中
        if suppport >= minSupport:
            # 使用retList.insert(0,key)在列表的首部插入任意新的集合
            retList.insert(0,key)
        suppportData[key] = suppport
    return retList, suppportData

# 11.2 apriori算法
# apriori(频繁项集列表Lk,项集元素个数k),输出为Ck
def aprioriGen(Lk,k):
    # 首先创建空列表
    retList = []
    # 计算频繁项集Lk中的元素数目
    lenLk = len(Lk)
    # 通过两个for循环,比较Lk中的每一个元素与其他元素
    for i in range(lenLk):
        for j in range(i+1, lenLk):
            # 取列表的两个集合比较,如果这两集合的前面k-2个元素都相等,
            L1 = list(Lk[i])[:k-2]
            L2 = list([Lk[j]])[:k-2]
            L1.sort()
            L2.sort()
            if L1 == L2:
                # 那么就将这两个集合合成一个大小为k的集合
                # 这里使用集合的并操作来完成,|
                retList.append(Lk[i]|Lk[j])
    return retList

# 上述所有操作都被封装在apriori()函数中。该函数传递一个数据集以及一个支持度,函数会生成候选项集的列表,
def apriori(dataSet, minSupport=0.5):
    # 首先创建C1然后读入数据集将其转化为D
    C1 = createC1(dataSet)
    D = map(set, dataSet)
    # 使用scanD()函数来创建L1,并将L1放入列表中,后将陆续放入L2、L3...
    L1, supportData = scanD(D, C1, minSupport)
    L = [L1]
    k = 2
    # while循环创建包含更大项集的更大列表,知道下一个大的项集为空
    while(len(L[k-2])>0):
        # 首先使用aprioriGen()来创建Ck
        Ck = aprioriGen(L[k-2],k)
        # 然后用scanD()基于Ck来创建Lk,Ck是一个候选项集列表,然后scanD()会遍历Ck,丢掉不满足最小支持度要求的项集
        Lk, supK = scanD(D, Ck, minSupport)
        supportData.update(supK)
        # Lk列表被添加到L,同时增加k的值
        L.append(Lk)
        k += 1
    # 最后当Lk为空时,程序返回L并推出
    return L, supportData

# 113.关联规则生成函数
# generateRules()是主函数,它调用其它两个函数
# generateRules(频繁项集列表, 包含那么频繁项集支持数据的字典,最小可信度阈值),
# 其中两个输入参数正好是apriori()函数的输出结果
def generateRules(L,supportData, minConf=0.7):
    # 函数最后要生成一个包含可信度的规则列表,这些规则存放在bigRuleList中
    bigRuleList = []
    # 遍历L中的每一个频繁项集并对每个频繁项集创建只包含单个元素集合的列表H1
    for i in range(1, len(L)):
        for freqSet in L[i]:
            H1 = [frozenset([item]) for item in freqSet]
            # 因为无法从单元素项集中构建关联规则,所以要从包含两个或更多元素的项集开始规则构建过程
            if (i>1):
                rulesFromConseq(freqSet, H1, supportData, bigRuleList,minConf)
            else:
                calcConf(freqSet,H1,supportData,bigRuleList,minConf)
    return bigRuleList
# 如果频繁项集的元素数目超过2,那么会考虑对它做进一步的合并,具体合并可通过rulesFronConseq()来完成
# 如果项集中只有两个元素,那么使用calcConf()函数来计算可信度值

# 对规则进行评估,计算规则的可信度以及找到满足最小可信度要求的规则
def calcConf(freqSet, H, supportData, br1, minConf = 0.7):
    # 创建空列表,保存函数将会返回的一个满足最小可信度要求的规则列表
    prunedH = []
    # 遍历H中的搜游项集并计算它们的可信度值
    for conseq in H:
        # 计算时使用supportData中的支持度数据
        conf = supportData[freqSet]/supportData[freqSet-conseq]
        if conf >= minConf:
            print freqSet-conseq,'-->',conseq,'conf:',conf
            # 同时也需要对列表br1进行填充, br1是前面通过检查的bigRuleLis
            br1.append(conseq)
    return prunedH

# 生成候选规则集合,从最初的项集中生成更多的关联规则
# 两个参数:频繁项集,和可以出现在规则右部的元素列表H
def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7):
    # 先计算H中的频繁集大小m
    m = len(H[0])
    # 接下来查看该频繁项集是否大到可以移除大小为m的子集,如果可以则移除
    if (len(freqSet)>(m+1)):
        # 可以使用aprioriGen()来生成H中元素的无重复组合,结果存储在Hmp1中,是下一次迭代的H列表
        # Hmp1包含所有可能的规则
        Hmp1 = aprioriGen(H, m+1)
        # 可以利用calcConf()来测试它们的可信度以确定规则事都满足要求
        Hmp1 = calcConf(freqSet, Hmp1, supportData, br1, minConf)
        # 如果不止一条规则满足要求,那么使用Hmp1迭代调用函数rulesFromConseq()来判断是否可以进一步组合这些规则
        if (len(Hmp1) > 1):
            rulesFromConseq()(freqSet, Hmp1, supportData, br1, minConf)

# 11.4 收集美国国会议案中actionID的函数
# 导入votesmart模块并通过sleep函数来延迟API调用
from time import sleep
from votesmart import votesmart
# 首先导入APIkey
votesmart.apikey = '49024thereoncewasamanfromnantucket94040'
# getActionIds()函数会返回存储在recent20bills.txt文件中议案的actionId
def getActionIds():
    # 然后创建两个空列表,分别用来返回actionsId 和标题
    actionIdList = []
    billTitleList = []
    # 打开文件,对每一行内不同元素使用tab进行分隔,
    fr = open('recent20bills.txt')
    for line in fr.readlines():
        billNum = int(line.split('\t')[0])
        # 之后进入try-except模块,由于在使用外部API时可能会遇到错误,并且不想让错误占用数据获取的时间
        try:
            # 首先尝试使用getBill()方法来获得一个billDetail对象
            billDetail = votesmart.votes.getBill(billNum)
            # 接下来遍历议案中的所有行为,来寻找有投票数据的行为i
            for action in billDetail.actions:
                # 在Passage阶段和AmendmentVote阶段都会有投票数据,要找到它们。
                # 现在在行政级别上也有一个Passage阶段,但并不包含任何投票数据,要确保这个阶段是发生在众议院
                if action.level == 'House' and (action.stage == 'Amendment Vote' or action.stage == 'Pasage'):
                    actionId = int(action.actionId)
                    # 如果确实如此,将actionId打印出来并将它添加到actionIdList中
                    print 'bill: %d has actionId : %d' % (billNum,actionId)
                    actionIdList.append(actionId)
                    # 同时也将会议的标题添加到billTitleList中
                    billTitleList.append(line.strip().split('\t')[1])
        # 如果在API调用时发生错误,就不会执行actionIdList的添加操作,一旦有错误就会执行except操作
        except:
            print "problem getting bill %d" % billNum
        sleep(1)
    return actionIdList, billTitleList

# 11.5 基于投票数据的事务列表填充函数
# getTransList()函数会创建一个事物数据库, 在此基础上可以使用前面的Apriori代码来生成频繁项集与关联guize
def getTrainslist(actionIdList, billTileList):
    # 该函数也会创建一个标题列表
    # 一开始使用前两元素Republican和Deocratic创建一个含义列表itemMeaning
    # 当想知道某些元素的具体含义时, 需要做的是以元素项的编号作为索引访问itemMeaning即可
    itemMeaning = ['Republican','Democratic']
    # 接下来遍历所有议案,然后在议案标题后添加nay或yea并将它们放入itemMeaning列表中
    for billTile in billTileList:
        itemMeaning.append('%s --Nay' % billTile)
        itemMeaning.append('%s -- Yea' % billTile)
    # 创建一个空字典用于加入元素项
    transDict = {}
    voteCount = 2
    # 遍历函数actionIds()返回的每一个actionId
    for actionId in actionIdList:
        # 遍历时要做的第一件事是休眠
        sleep(3)
        # 接着将运行结果打印出来,以便知道程序是否在正常工作
        print 'getting votes for actionId: %d' % actionId
        # 再接着通过try、except块来使用votesmartAPI来获取某个特定actionId相关的所有投票信息
        try:
            voteList = votesmart.votes.getBillActionVotes(actionId)
            # 然后遍历所有投票信息
            for vote in voteList:
                # 在遍历时,使用政客的名字作为字典的键值来填充tansDict
                # 如果之前没有遇到该政客,那么就要获取它的政党信息
                if not transDict.has_key(vote.candidateName):
                    # 字典中的每个政客都有一个列表来存储他投票的元素项或者他的政党信息
                    transDict[vote.candidateName] = []
                    if vote.officeParties == 'Democratic':
                        transDict[vote.candidateName].append(1)
                    elif vote.officeParties == 'Republican':
                        transDict[vote.candidateName].append(0)
                # 接下来会看到政客是否对当前议案投了赞成机票反对票
                # 如果他们之前有投票,那么不管是赞成还是反对,信息都将添加到列表中
                if vote.action == 'Nay':
                    transDict[vote.candidateName].append(voteCount)
                elif vote.action == 'Yea':
                    transDict[vote.candidateName].append(voteCount+1)
        except:
            print "the problem getting actionId: %d" % actionId
        voteCount += 2
    return transDict, itemMeaning

猜你喜欢

转载自blog.csdn.net/weixin_42836351/article/details/81393093