基于pyhton3.6-机器学习实战-fpGrowth代码解释

本人是一名数学系研究生,于2017年底第一次接触python和机器学习,作为一名新手,欢迎与大家交流。

我主要给大家讲解代码,理论部分给大家推荐3本书:

《机器学习实战中文版》

《机器学习》周志华

《统计学习方法》李航

以上3本书,第一本是基于python2的代码实现;剩余两本主要作为第一本书理论省略部分的补充,理论大部分都讲得很细。

博客上关于机器学习实战理论解释都很多,参差不齐,好作品也大都借鉴了以上3本书,网上有很多电子版的书。

与其看看一些没用的博客,真心不如以上3本书有收获。

说实话,学习一定要静下心来,切忌浮躁。不懂可以每天看一点,每天你懂一点,天天积累就多了。

操作系统:windows8.1

python版本:python3.6

运行环境:spyder(anaconda)

# -*- coding: utf-8 -*-
"""
Created on Tue Mar 27 21:39:50 2018

@author: Lelouch_C.C
"""

#FP树中节点的类定义
class treeNode:
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue
        self.count = numOccur
        self.nodeLink = None                 #nodeLink 变量用于链接相似的元素项
        self.parent = parentNode             #指向当前节点的父节点
        self.children = {}                   #空字典,存放节点的子节点

    def inc(self, numOccur):
        self.count += numOccur

    def disp(self, ind=1):
        """
        函数说明:将树以文本形式显示
        """
        print ('  ' * ind, self.name, ' ', self.count)
        for child in self.children.values():
            child.disp(ind + 1)    #self.children字典的值child是类似于treeNode,所以要用递归

"""
if __name__=='__main__':
    rootNode=treeNode('pyramid',9,None)
    rootNode.children['eye']=treeNode('eye',13,None)
    print('rootNode.disp()=',rootNode.disp())
    rootNode.children['phoenix']=treeNode('phoenix',3,None) 
    print('rootNode.disp()=',rootNode.disp())
#"""

#
def createTree(dataSet, minSup=1):
    """
    函数说明:FP-tree构建函数
    输入参数:数据集 dataSet
             (此处的数据集dataSet经过处理的,它是一个字典,键是每个样本,值是这个样本出现的频数)
             最小支持度 minSup
    返回值:构建好的树retTree
            头指针表headerTable
    """
    headerTable = {}                           #初始化空字典作为一个头指针表
    ##############第一次遍历数据集:统计每个元素项出现的频度################
    for trans in dataSet:              
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
    #####################################################################
    #######删除未达到最小频度的元素########
    for k in list(headerTable):        
        #此处headerTable要取list,因为字典要进行删除del操作,字典在迭代过程中长度发生变化是会报错的
        if headerTable[k] < minSup:
            del (headerTable[k])
    ######################################
    freqItemSet = set(headerTable.keys())       #对达到最小频度的元素建立一个集合
    #print ('freqItemSet= ',freqItemSet)        
    if len(freqItemSet) == 0:
        return None, None                       #若达到要求的数目为0
    for k in headerTable:                       #遍历头指针表
        headerTable[k] = [headerTable[k], None] #保存计数值及指向每种类型第一个元素项的指针
    #print ('headerTable= ',headerTable)
    retTree = treeNode('Null Set', 1, None)     #创建只包含空集的根节点
    ######################第二次遍历###########################
    for tranSet, count in dataSet.items():      #此处count为每个样本的频数
        localD = {}
        #############################################
        for item in tranSet:          #通过for循环,把每个样本中频繁项集及其频数储存在localD字典中             
            if item in freqItemSet:             
                localD[item] = headerTable[item][0]  
        #############################################
        if len(localD) > 0:
            orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]
            #items()方法返回一个可迭代的dict_items类型,其元素是键值对组成的2-元组
            #sorted(排序对象,key,reverse),当待排序列表的元素由多字段构成时,
            #我们可以通过sorted(iterable,key,reverse)的参数key来制定我们根据哪个字段对列表元素进行排序 
            #这里key=lambda p: p[1]指明要根据键对应的值,即根据频繁项的频数进行从大到小排序 
            updateTree(orderedItems, retTree, headerTable, count)  
            #使用排序后的频繁项集对树进行填充
    ##########################################################
    return retTree, headerTable                  #返回树和头指针表

def updateTree(items, inTree, headerTable, count):
    """
    函数说明:让FP树生长
    """
    if items[0] in inTree.children:           
        #首先检查事物项items中第一个元素是否作为子节点存在
        #如果树中存在现有元素
        inTree.children[items[0]].inc(count)      #则更新增加该元素项的计数,
    else:                                         #否则向树增加一个分支
        inTree.children[items[0]] = treeNode(items[0], count, inTree)  
        #创建一个新的树节点,并更新了父节点inTree,父节点是一个类对象,包含很多特性
        ############################不参与树的生长#############################
        if headerTable[items[0]][1] == None:      
            #若原来指向每种类型第一个元素项的指针为 None,则需要更新头指针列表
            headerTable[items[0]][1] = inTree.children[items[0]]       
            #更新头指针表,把指向每种类型第一个元素项放在头指针表里
        else:                                     
            updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
            #更新生成链表,注意,链表也是每过一个样本,更一次链表,且链表更新都是从头指针表开始的
        ######################################################################
    if len(items) > 1:                            #仍有未分配完的树,迭代
        updateTree(items[1::], inTree.children[items[0]], headerTable, count)
        #由items[1::]可知,每次调用updateTree时都会去掉列表中第一个元素,递归

def updateHeader(nodeToTest, targetNode):
    """
    函数说明:它确保节点链接指向树中该元素项的每一个实例。
    """
    while (nodeToTest.nodeLink != None):
        nodeToTest = nodeToTest.nodeLink
        #从头指针表的 nodeLink 开始,一直沿着nodeLink直到到达链表末尾,这就是一个链表
    nodeToTest.nodeLink = targetNode
    #链表链接的都是相似元素项,通过ondeLink 变量用来链接相似的元素项。

def loadSimpDat():
    simpDat = [['r', 'z', 'h', 'j', 'p'],
               ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
               ['z'],
               ['r', 'x', 'n', 'o', 's'],
               ['y', 'r', 'x', 'z', 'q', 't', 'p'],
               ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
    return simpDat
#createInitSet() 
def createInitSet(dataSet):
    """
    函数说明:用于将列表形式的数据集dataSet格式化为一个‘键为一个样本,值为对应频数’的字典
    """
    retDict = {}
    for trans in dataSet:
        retDict[frozenset(trans)] = 1
    return retDict
"""
if __name__=='__main__':
    simpDat=loadSimpDat()
    print('simpDat=',simpDat)
    initSet=createInitSet(simpDat)
    print('initSet=',initSet)
    myFPtree,myHeaderTab=createTree(initSet,3)
    myFPtree.disp()
    #说明:构建的fp-树可能不同但等价,这是由于我们是每遍历一个样本画一次图。
    #元素项出现的频数(z5,r3,x4,y3,s3,t3)
    #第一个样本{z,r},排序是根据他们出现的频数,所以z1->r1
    #第二个样本{z,x,y,s,t},排序是根据他们出现的频数,
    #               z2->r1
    #                 ->x1->...
    #(注意:x以后的元素y,s,t的频数都是相同的,排序随机,所以画出的图可能不一样)
#"""

"""
从FP树中抽取频繁项集的三个基本步骤:
(1) 从FP树中获得条件模式基;
(2) 利用条件模式基,构建一个条件FP树;
(3) 迭代重复步骤(1)步骤(2),直到树包含一个元素项为止。
"""

#从FP树中发现频繁项集
def ascendTree(leafNode, prefixPath): 
    """
    函数说明:递归上溯整棵树,并把当前树节点的所有父节点处存在prefixPath列表中
    """
    if leafNode.parent != None:                   
        #判断当前叶节点是否有父节点,
        prefixPath.append(leafNode.name)    
        #如果有的话,向 prefixPath列表添加当前树节点,看清不是添加父节点
        ascendTree(leafNode.parent, prefixPath)   
        #通过递归不断向 prefixPath列表添加当前树节点,直到没有父节点


def findPrefixPath(basePat, treeNode):  
    """
    函数说明:发现已给定元素项结尾的所有前缀路径
    参数:给定元素项:basePat
         在头指针表headerTable中储存着指向每种类型第一个元素项的指针:treeNode
    返回值:储存条件模式基的一个字典
    """
    condPats = {}
    while treeNode != None:
        prefixPath = []
        ascendTree(treeNode, prefixPath)      #寻找当前非空节点的前缀
        if len(prefixPath) > 1:
            condPats[frozenset(prefixPath[1:])] = treeNode.count 
            #将条件模式基的个数赋给当前前缀路径,添加到字典中
            #prefixPath[1:],去掉第一项是因为第一项是给定元素项,不属于前缀路径
        treeNode = treeNode.nodeLink         #更换链表的下一个节点,继续寻找前缀路径
    return condPats
"""
if __name__=='__main__':
    simpDat=loadSimpDat()
    initSet=createInitSet(simpDat)
    myFPtree,myHeaderTab=createTree(initSet,3)
    #print('myFPtree=',myFPtree)                #myFPtree是一个类对象
    #print('myHeaderTab=',myHeaderTab)
    print('x的条件模式基:',findPrefixPath('x', myHeaderTab['x'][1]))
    print('z的条件模式基:',findPrefixPath('z', myHeaderTab['z'][1]))
    print('r的条件模式基:',findPrefixPath('r', myHeaderTab['r'][1]))
    print('t的条件模式基:',findPrefixPath('t', myHeaderTab['t'][1]))
#"""

#创建条件FP树
def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
    """
    函数说明:递归查找频繁项集
    参数: FP树 inTree
          头指针表 headerTable
          最小支持度 minSup     
          前缀路径 preFix(初始时一般为空,用来储存前缀路径)
          频繁项列表 freqItemList(初始时一般为空,用来储存频繁项集)
    """
    bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: str(p[1]))]
    #python3修改
    # 头指针表中的元素项按照其出现频率排序,从小到大(默认),从头指针表的底层开始
    for basePat in bigL:  
        #频繁项可能是一个,也可能是多个,经过上面的步骤把单个的频繁项储存bigL
        #寻找多个频繁项是从单个的频繁项开始的,因为一个项集是非频繁集,那么它的超级也是非频繁项集
        #所以下面就从头指针表的最底层的单个频繁项开始,一直向上寻找。
        newFreqSet = preFix.copy()
        newFreqSet.add(basePat)
        print ('finalFrequent Item: ',newFreqSet)
        freqItemList.append(newFreqSet)
        condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
        #找见当前节点的前缀路径
        print ('condPattBases :',basePat, condPattBases)  #输出条件模式基
        myCondTree, myHead = createTree(condPattBases, minSup)
        #将创建的条件基作为新的数据集输入到 FP树构建函数,来构建条件FP树
        #将前缀路径频繁项提取出来
        print ('head from conditional tree: ', myHead)
        if myHead != None:          
            #若myHead为非空,说明条件 FP树还可以向上缩减。
            #说的详细一点,一开始我们通过当前节点basePat,找到条件模式基,进而创建条件FP树
            #这棵条件FP树就是通过当前节点basePat找的频繁项(当前节点basePat本身也是单个的频繁项)
            #接下来,我们要看若myHead为非空,找的频繁项的子集还是不是频繁项
            print ('conditional tree for: ',newFreqSet)
            myCondTree.disp(1) 
            mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)
            #若myHead为非空,就把找的的频繁项条件FP树从下往上依次去一个点,看它还是不是频繁项,递归。
            #把当前节点basePat的所有频繁项找到后,进行下一个节点的频繁项寻找
"""
if __name__=='__main__':
    simpDat=loadSimpDat()
    initSet=createInitSet(simpDat)
    myFPtree,myHeaderTab=createTree(initSet,3)
    myFreqItems=[]
    mineTree(myFPtree,myHeaderTab,3,set([]),myFreqItems)
    print('myFreqItems=',myFreqItems)

#"""
"""
#示例:从新闻网站点击流中挖掘 
if __name__=='__main__':
    parsedDat=[line.split() for line in open('kosarak.dat').readlines()] #将数据集导入
    initSet=createInitSet(parsedDat)                            #对初始集合格式化
    myFPtree,myHeaderTab=createTree(initSet,100000)
    #构建FP树,并从中寻找那些至少被10万人浏览过的新闻报道
    myFreqList=[]                                  #需要创建一个空列表来保存这些频繁项集
    mineTree(myFPtree,myHeaderTab,100000,set([]),myFreqList)
    print('len(myFreqList)=',len(myFreqList))
    #看下有多少新闻报道或报道集合曾经被10万或者更多的人浏览过:
    print('myFreqList=',myFreqList)

#"""



你可能感兴趣的:(机器学习)