关联分析-fpGrowth的python代码分析

'''
fpGrowth 算法寻找频繁项集
'''

'''
1.构造fp树节点的结构体:
/*@name 节点代表的物品名称
 *@count 该节点被重复使用的次数
 *@nodeLink 用来横向连接各个节点的指针
 *@parent 父亲节点的指针
 *@children 存放孩子节点的字典
 *@inc 增加节点计数
 *@disp 打印以某节点为根节点的fp树,用空格表示树里面的父子节点关系
 */
'''
class treeNode:
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue
        self.count = numOccur
        self.nodeLink = None
        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)



'''
2.建树
/*@dataSet 用来建树的数据,数据是一个字典类型,字典的键是一类清单集合,值是它出现的次数
 *@minSup 最少出现次数,用来确定一个物品是否是频繁出现的
 *@retTree 返回参数1,表示构造的fp树的树根
 *@headerTable 返回参数2,表示fp数里面每个节点构成横向链表头结点数组
 */
'''
def createTree(dataSet,minSup=1):
    # 统计每个项目出现的次数
    headerTable = {}
    for trans in dataSet:
        for item in trans:
            headerTable[item] = headerTable.get(item,0)+dataSet[trans]

    #支持度小于要求的删去。
    for k in list(headerTable.keys()):
        if headerTable[k] < minSup:
            del(headerTable[k])

    #构造频繁1项集,若集合为空,则返回空树
    freqItemSet = set(headerTable.keys())
    if len(freqItemSet)==0:
        return None,None

    #将headerTable扩展一维,一维存放各个事件频数的同时,另一维存放该事件第一个节点的指针
    for k in headerTable:
        headerTable[k] = [headerTable[k],None]

    #创建树根
    retTree = treeNode('Null Set',1,None)
 
    #枚举数据中每次购买清单tranSet 和它出现的次数 count
    #对于每个清单,从中选择频繁的事物放入字典localID
    #对localID字典中键值按照频繁程度由大到小排序后放入列表orderedItem
    #通过updateTree将orderedItem中事件更新到树上
    
    for tranSet,count in dataSet.items():
        localID = {}
        for item in tranSet:
            if item in freqItemSet:
                localID[item] = headerTable[item][0]
        if len(localID) > 0:
            orderedItem = [v[0] for v in sorted(localID.items(),key=lambda p:p[1],reverse=True)]
            updateTree(orderedItem,retTree,headerTable,count)

    #返回结果
    return retTree,headerTable


'''
3.更新fp树
/*@item 已排序待加入fp树的事物清单
 *@inTree fp树的根节点
 *@headerTable 横向链表的头结点数组
 *@count 这种购物清单有几份
 */
'''
def updateTree(items,inTree,headerTable,count):

    #采用递归的方式,对清单内逐个元素进行处理
    #如果列表内当前节点在fp树当前位置的孩子列表里,只需让对应孩子节点计数加count
    #否则需要新增一个孩子节点,并且需要修改链表
    #递归处理下一个元素
    if items[0] in inTree.children:
        inTree.children[items[0]].inc(count)
    else:
        inTree.children[items[0]] = treeNode(items[0],count,inTree)
        if headerTable[items[0]][1] == 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)

'''
3.更新链表
  这里某个事物多开辟一个节点时,要加入相应的链表,这里的代码采用的尾插的方法
  当然也可以改成头插来提高效率
  
/*@nodeToTest 相应链表的头结点
 *@targetNode 待插入的新节点
 */
'''
def updateHeader(nodeToTest,targetNode):
    while (nodeToTest.nodeLink != None):
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode

'''
4.从树上某节点开始,往上回溯,将路径上节点存在列表prefixPath中
/*@leafNode fp树上当前位置节点
 *@prefixPath 存放前缀路径的列表 
 */
'''
def ascendTree(leafNode,prefixPath):
    if leafNode.parent != None:
        prefixPath.append(leafNode.name)
        ascendTree(leafNode.parent,prefixPath)

'''
5.对于某个事物,生成包含该事物的条件模式基,并且格式与建树的数据集格式一致,便于后面的递归。
/*@basePat 基础事物
 *@treeNode 用来导引链表的节点
 */
'''

def findPrefixPath(basePat,treeNode):
    condPats = {}
    while treeNode != None:
        prefixPath = []
        ascendTree(treeNode,prefixPath)
        if len(prefixPath) > 1:
             condPats[frozenset(prefixPath[1:])] = treeNode.count
        treeNode = treeNode.nodeLink
    return condPats


'''
6.从已建好的树上挖掘频繁项集
/*@inTree树根
 *@headerTable横向链表
 *@minSup 最小支持度
 *@prefix 记录前缀
 *freqItemList 最后的结果
 */
'''
def mineTree(inTree,headerTable,minSup,preFix,freqItemList):

    #将当前事物按频繁程度排序后放入列表bigL
    bigL = [v[0] for v in sorted(headerTable.items(),key=lambda p: str(p[1]))]

    #枚举每个事物,将其固定到前缀,然后执行fp递归算法
    for basePat in bigL:
        newFreSet = preFix.copy()
        newFreSet.add(basePat)
        freqItemList.append(newFreSet)
        condpattBases = findPrefixPath(basePat,headerTable[basePat][1])
        myCondTree,myHead = createTree(condpattBases,minSup)
        if myHead != None:
            '''打印各模式基的fp树
            print('condition tree for: ',newFreSet)
            myCondTree.disp(1)
            '''
            mineTree(myCondTree,myHead,minSup,newFreSet,freqItemList)


'''
7.简单的数据集合,用来测试程序
'''
def loadSimpData():
    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


'''
8.将简单数据集转变成建树所需的字典类型
'''
def createInitSet(dataSet):
    retDict = {}
    for trans in dataSet:
        retDict[frozenset(trans)] = 1  #retDict.get(frozenset(trans),0)
    return retDict


'''
9.将01矩阵类型转变成建树时所需的字典类型
'''
def transData(dataSet):
    retDict = {}
    (m,n) = dataSet.shape
    print(m,n)
    for i in range(m):
        st=set([])
        for j in range(n):
            if dataSet[i][j] == True:
                st.add(j)
        retDict[frozenset(st)] = retDict.get(frozenset(st),0)+1
    return retDict


'''
10.进行关联分析,传入建树所需的字典类型数据以及阈值T,返回多元频繁项集
'''
def associationAnalysis(dataSet,T):
#    print(dataSet)
    myFPtree, myHeaderTab = createTree(dataSet, T)
    if(type(myFPtree) == None):
        return []
    freqItems = []
    mineTree(myFPtree, myHeaderTab,T, set([]), freqItems)

    ans = []
    for i in freqItems:
        if len(i) > 1:
            ans.append(i)
    return ans

'''
simpDat = loadSimpData()
initSet = createInitSet(simpDat)
myFPtree,myHeaderTab = createTree(initSet,4)
print(type(myHeaderTab))
#myFPtree.disp()
#print(findPrefixPath('r',myHeaderTab['r'][1]))
freqItems = []
mineTree(myFPtree,myHeaderTab,4,set([]),freqItems)
print(freqItems)
'''

 

你可能感兴趣的:(数据分)