|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +""" |
| 3 | +Created on Sun Aug 5 17:29:40 2018 |
| 4 | +
|
| 5 | +@author: wzy |
| 6 | +""" |
| 7 | +# FP树的类定义 |
| 8 | +class treeNode: |
| 9 | + def __init__(self, nameValue, numOccur, parentNode): |
| 10 | + # 节点名称 |
| 11 | + self.name = nameValue |
| 12 | + # 节点出现次数 |
| 13 | + self.count = numOccur |
| 14 | + # 不同项集的相同项通过nodeLink连接在一起 |
| 15 | + self.nodeLink = None |
| 16 | + # 指向父节点 |
| 17 | + self.parent = parentNode |
| 18 | + # 存储叶子节点 |
| 19 | + self.children = {} |
| 20 | + # 节点出现次数累加 |
| 21 | + def inc(self, numOccur): |
| 22 | + self.count += numOccur |
| 23 | + # 将树以文本形式显示 |
| 24 | + def disp(self, ind=1): |
| 25 | + print(' '*ind, self.name, ' ', self.count) |
| 26 | + # 绘制子节点 |
| 27 | + for child in self.children.values(): |
| 28 | + # 缩进处理 |
| 29 | + child.disp(ind + 1) |
| 30 | + |
| 31 | + |
| 32 | +""" |
| 33 | +函数说明:构建FP-tree |
| 34 | +
|
| 35 | +Parameters: |
| 36 | + dataSet - 需要处理的数据集合 |
| 37 | + minSup - 最少出现的次数(支持度) |
| 38 | + |
| 39 | +Returns: |
| 40 | + retTree - 树 |
| 41 | + headerTable - 头指针表 |
| 42 | +
|
| 43 | +Modify: |
| 44 | + 2018-08-06 |
| 45 | +""" |
| 46 | +def createTree(dataSet, minSup=1): |
| 47 | + headerTable = {} |
| 48 | + # 遍历数据表中的每一行数据 |
| 49 | + for trans in dataSet: |
| 50 | + # 遍历每一行数据中的每一个数据元素 |
| 51 | + # 统计每一个字母出现的次数,将次数保存在headerTable中 |
| 52 | + for item in trans: |
| 53 | + # 字典get()函数返回指定键的值,如果值不在字典中返回0。 |
| 54 | + # 由于dataSet里的每个列表均为frozenset所以每一个列表的值均为1即dataSet[trans]=1 |
| 55 | + headerTable[item] = headerTable.get(item, 0) + dataSet[trans] |
| 56 | + # 遍历headerTable中的每一个字母 |
| 57 | + # 若headerTable中的字母出现的次数小于minSup则把这个字母删除处理 |
| 58 | + lessThanMinsup = list(filter(lambda k:headerTable[k] < minSup, headerTable.keys())) |
| 59 | + for k in lessThanMinsup: del(headerTable[k]) |
| 60 | + for k in list(headerTable): |
| 61 | + if headerTable[k] < minSup: |
| 62 | + del(headerTable[k]) |
| 63 | + # 将出现次数在minSup次以上的字母保存在freqItemSet中 |
| 64 | + freqItemSet = set(headerTable.keys()) |
| 65 | + # 如果没有达标的则返回None |
| 66 | + if len(freqItemSet) == 0: |
| 67 | + return None, None |
| 68 | + # 此时的headerTable中存放着出现次数在minSup以上的字母以及每个字母出现的次数 |
| 69 | + # headerTable这个字典被称为头指针表 |
| 70 | + for k in headerTable: |
| 71 | + # 保存计数值及指向每种类型第一个元素的指针 |
| 72 | + headerTable[k] = [headerTable[k], None] |
| 73 | + # 初始化tree |
| 74 | + retTree = treeNode('Null Set', 1, None) |
| 75 | + # 遍历dataSet的每一组数据以及这组数据出现的次数 |
| 76 | + for tranSet, count in dataSet.items(): |
| 77 | + localD = {} |
| 78 | + # 遍历一组数据中的每一个字母 |
| 79 | + for item in tranSet: |
| 80 | + # 如果这个字母出现在头指针表中 |
| 81 | + if item in freqItemSet: |
| 82 | + # 将这个字母以及它在头指针表中出现的次数存储在localD中 |
| 83 | + localD[item] = headerTable[item][0] |
| 84 | + # localD中存放的字母多于一个 |
| 85 | + if len(localD) > 0: |
| 86 | + # 将字母按照出现的次数按降序排列 |
| 87 | + ordereItems = [v[0] for v in sorted(localD.items(), key=lambda p:(p[1], p[0]), reverse=True)] |
| 88 | + # 对树进行更新 |
| 89 | + updateTree(ordereItems, retTree, headerTable, count) |
| 90 | + # 返回树和头指针表 |
| 91 | + return retTree, headerTable |
| 92 | + |
| 93 | + |
| 94 | +""" |
| 95 | +函数说明:更新树 |
| 96 | +
|
| 97 | +Parameters: |
| 98 | + items - 将字母按照出现的次数按降序排列 |
| 99 | + inTree - 树 |
| 100 | + headerTable - 头指针表 |
| 101 | + count - dataSet的每一组数据出现的次数,在本例中均为1 |
| 102 | + |
| 103 | +Returns: |
| 104 | + None |
| 105 | +
|
| 106 | +Modify: |
| 107 | + 2018-08-06 |
| 108 | +""" |
| 109 | +def updateTree(items, inTree, headerTable, count): |
| 110 | + # 首先查看是否存在该节点 |
| 111 | + if items[0] in inTree.children: |
| 112 | + # 存在则计数增加 |
| 113 | + inTree.children[items[0]].inc(count) |
| 114 | + # 不存在则新建该节点 |
| 115 | + else: |
| 116 | + # 创建一个新节点 |
| 117 | + inTree.children[items[0]] = treeNode(items[0], count, inTree) |
| 118 | + # 若原来不存在该类别,更新头指针列表 |
| 119 | + if headerTable[items[0]][1] == None: |
| 120 | + # 指向更新 |
| 121 | + headerTable[items[0]][1] = inTree.children[items[0]] |
| 122 | + # 更新指向 |
| 123 | + else: |
| 124 | + updateHeader(headerTable[items[0]][1], inTree.children[items[0]]) |
| 125 | + # 仍有未分配完的树,迭代 |
| 126 | + if len(items) > 1: |
| 127 | + updateTree(items[1:], inTree.children[items[0]], headerTable, count) |
| 128 | + |
| 129 | + |
| 130 | +""" |
| 131 | +函数说明:更新树 |
| 132 | +
|
| 133 | +Parameters: |
| 134 | + nodeToTest - 需要插入的节点 |
| 135 | + targetNode - 目标节点 |
| 136 | + |
| 137 | +Returns: |
| 138 | + None |
| 139 | +
|
| 140 | +Modify: |
| 141 | + 2018-08-06 |
| 142 | +""" |
| 143 | +def updateHeader(nodeToTest, targetNode): |
| 144 | + while(nodeToTest.nodeLink != None): |
| 145 | + nodeToTest = nodeToTest.nodeLink |
| 146 | + nodeToTest.nodeLink = targetNode |
| 147 | + |
| 148 | + |
| 149 | +""" |
| 150 | +函数说明:创建数据集 |
| 151 | +
|
| 152 | +Parameters: |
| 153 | + None |
| 154 | + |
| 155 | +Returns: |
| 156 | + simpDat - 返回生成的数据集 |
| 157 | +
|
| 158 | +Modify: |
| 159 | + 2018-08-06 |
| 160 | +""" |
| 161 | +def loadSimpDat(): |
| 162 | + simpDat = [['r', 'z', 'h', 'j', 'p'], |
| 163 | + ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'], |
| 164 | + ['z'], |
| 165 | + ['r', 'x', 'n', 'o', 's'], |
| 166 | + ['y', 'r', 'x', 'z', 'q', 't', 'p'], |
| 167 | + ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']] |
| 168 | + return simpDat |
| 169 | + |
| 170 | + |
| 171 | +""" |
| 172 | +函数说明:将数据集数据项转换为frozenset并保存在字典中,其值均为1 |
| 173 | +
|
| 174 | +Parameters: |
| 175 | + dataSet - 生成的数据集 |
| 176 | + |
| 177 | +Returns: |
| 178 | + retDict - 保存在字典中的数据集 |
| 179 | +
|
| 180 | +Modify: |
| 181 | + 2018-08-06 |
| 182 | +""" |
| 183 | +def createInitSet(dataSet): |
| 184 | + retDict = {} |
| 185 | + for trans in dataSet: |
| 186 | + fset = frozenset(trans) |
| 187 | + retDict.setdefault(fset, 0) |
| 188 | + retDict[fset] += 1 |
| 189 | + # retDict[frozenset(trans)] = 1 |
| 190 | + return retDict |
| 191 | + |
| 192 | + |
| 193 | +""" |
| 194 | +函数说明:寻找当前非空节点的前缀 |
| 195 | +
|
| 196 | +Parameters: |
| 197 | + leafNode - 当前选定的节点 |
| 198 | + prefixPath - 当前节点的前缀 |
| 199 | + |
| 200 | +Returns: |
| 201 | + None |
| 202 | +
|
| 203 | +Modify: |
| 204 | + 2018-08-06 |
| 205 | +""" |
| 206 | +def ascendTree(leafNode, prefixPath): |
| 207 | + # 当前节点的父节点不为空 |
| 208 | + if leafNode.parent != None: |
| 209 | + # 当前节点添加入前缀列表 |
| 210 | + prefixPath.append(leafNode.name) |
| 211 | + # 递归遍历所有前缀路线节点 |
| 212 | + ascendTree(leafNode.parent, prefixPath) |
| 213 | + |
| 214 | + |
| 215 | +""" |
| 216 | +函数说明:返回条件模式基 |
| 217 | +
|
| 218 | +Parameters: |
| 219 | + basePat - 头指针列表中的元素 |
| 220 | + treeNode - 树中的节点 |
| 221 | + |
| 222 | +Returns: |
| 223 | + condPats - 返回条件模式基 |
| 224 | +
|
| 225 | +Modify: |
| 226 | + 2018-08-06 |
| 227 | +""" |
| 228 | +def findPrefixPath(basePat, treeNode): |
| 229 | + condPats = {} |
| 230 | + while treeNode != None: |
| 231 | + prefixPath = [] |
| 232 | + # 寻找当前非空节点的前缀 |
| 233 | + ascendTree(treeNode, prefixPath) |
| 234 | + # 如果遍历到前缀路线 |
| 235 | + if len(prefixPath) > 1: |
| 236 | + # 将前缀路线保存入字典 |
| 237 | + condPats[frozenset(prefixPath[1:])] = treeNode.count |
| 238 | + # 到下一个频繁项集出现的位置 |
| 239 | + treeNode = treeNode.nodeLink |
| 240 | + # 返回条件模式基 |
| 241 | + return condPats |
| 242 | + |
| 243 | + |
| 244 | +""" |
| 245 | +函数说明:递归查找频繁项集 |
| 246 | +
|
| 247 | +Parameters: |
| 248 | + inTree - 初始创建的FP树 |
| 249 | + headerTable - 头指针表 |
| 250 | + minSup - 最小支持度 |
| 251 | + preFix - 前缀 |
| 252 | + freqItemList - 条件树 |
| 253 | + |
| 254 | +Returns: |
| 255 | + None |
| 256 | +
|
| 257 | +Modify: |
| 258 | + 2018-08-06 |
| 259 | +""" |
| 260 | +def mineTree(inTree, headerTable, minSup, preFix, freqItemList): |
| 261 | + # 从头指针表的底端开始 |
| 262 | + bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: str(p[1]))] |
| 263 | + for basePat in bigL: |
| 264 | + # 加入频繁项表 |
| 265 | + newFreqSet = preFix.copy() |
| 266 | + newFreqSet.add(basePat) |
| 267 | + # print('finalFrequent Item: ', newFreqSet) |
| 268 | + freqItemList.append(newFreqSet) |
| 269 | + # 创造条件基 |
| 270 | + condPattBases = findPrefixPath(basePat, headerTable[basePat][1]) |
| 271 | + # print('condPattBases: ', basePat, condPattBases) |
| 272 | + # 从条件模式来构建条件树 |
| 273 | + myContTree, myHead = createTree(condPattBases, minSup) |
| 274 | + # print('head from conditional tree: ', myHead) |
| 275 | + # 挖掘条件FP树,直到条件树中没有元素为止 |
| 276 | + if myHead != None: |
| 277 | + print('conditional tree for: ', newFreqSet) |
| 278 | + myContTree.disp(1) |
| 279 | + mineTree(myContTree, myHead, minSup, newFreqSet, freqItemList) |
| 280 | + |
| 281 | +if __name__ == '__main__': |
| 282 | + # 创建树中的一个单节点 |
| 283 | + # rootNode = treeNode('pyramid', 9, None) |
| 284 | + # 增加一个子节点 |
| 285 | + # rootNode.children['eye'] = treeNode('eye', 13, None) |
| 286 | + # 显示子节点 |
| 287 | + # rootNode.disp() |
| 288 | + # 两个子节点 |
| 289 | + # rootNode.children['phoenix'] = treeNode('phoenix', 3, None) |
| 290 | + # 显示 |
| 291 | + # rootNode.disp() |
| 292 | + simpDat = loadSimpDat() |
| 293 | + initSet = createInitSet(simpDat) |
| 294 | + myFPtree, myHeaderTab = createTree(initSet, 3) |
| 295 | + # print(findPrefixPath('x', myHeaderTab['s'][1])) |
| 296 | + # myFPtree.disp() |
| 297 | + freqItems = [] |
| 298 | + mineTree(myFPtree, myHeaderTab, 3, set([]), freqItems) |
| 299 | + print(freqItems) |
0 commit comments