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

本文详细介绍FP树算法原理及实现过程,包括FP树构建、频繁项集挖掘等关键步骤,并提供Python代码示例。

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

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

《机器学习实战中文版》

《机器学习》周志华

《统计学习方法》李航

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

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

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

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

操作系统:windows8.1

python版本:python3.6

运行环境:spyder(anaconda)

 

 


 
  1. # -*- coding: utf-8 -*-

  2. """

  3. Created on Tue Mar 27 21:39:50 2018

  4.  
  5. @author: Lelouch_C.C

  6. """

  7.  
  8. #FP树中节点的类定义

  9. class treeNode:

  10. def __init__(self, nameValue, numOccur, parentNode):

  11. self.name = nameValue

  12. self.count = numOccur

  13. self.nodeLink = None #nodeLink 变量用于链接相似的元素项

  14. self.parent = parentNode #指向当前节点的父节点

  15. self.children = {} #空字典,存放节点的子节点

  16.  
  17. def inc(self, numOccur):

  18. self.count += numOccur

  19.  
  20. def disp(self, ind=1):

  21. """

  22. 函数说明:将树以文本形式显示

  23. """

  24. print (' ' * ind, self.name, ' ', self.count)

  25. for child in self.children.values():

  26. child.disp(ind + 1) #self.children字典的值child是类似于treeNode,所以要用递归

  27.  
  28. """

  29. if __name__=='__main__':

  30. rootNode=treeNode('pyramid',9,None)

  31. rootNode.children['eye']=treeNode('eye',13,None)

  32. print('rootNode.disp()=',rootNode.disp())

  33. rootNode.children['phoenix']=treeNode('phoenix',3,None)

  34. print('rootNode.disp()=',rootNode.disp())

  35. #"""

  36.  
  37. #

  38. def createTree(dataSet, minSup=1):

  39. """

  40. 函数说明:FP-tree构建函数

  41. 输入参数:数据集 dataSet

  42. (此处的数据集dataSet经过处理的,它是一个字典,键是每个样本,值是这个样本出现的频数)

  43. 最小支持度 minSup

  44. 返回值:构建好的树retTree

  45. 头指针表headerTable

  46. """

  47. headerTable = {} #初始化空字典作为一个头指针表

  48. ##############第一次遍历数据集:统计每个元素项出现的频度################

  49. for trans in dataSet:

  50. for item in trans:

  51. headerTable[item] = headerTable.get(item, 0) + dataSet[trans]

  52. #####################################################################

  53. #######删除未达到最小频度的元素########

  54. for k in list(headerTable):

  55. #此处headerTable要取list,因为字典要进行删除del操作,字典在迭代过程中长度发生变化是会报错的

  56. if headerTable[k] < minSup:

  57. del (headerTable[k])

  58. ######################################

  59. freqItemSet = set(headerTable.keys()) #对达到最小频度的元素建立一个集合

  60. #print ('freqItemSet= ',freqItemSet)

  61. if len(freqItemSet) == 0:

  62. return None, None #若达到要求的数目为0

  63. for k in headerTable: #遍历头指针表

  64. headerTable[k] = [headerTable[k], None] #保存计数值及指向每种类型第一个元素项的指针

  65. #print ('headerTable= ',headerTable)

  66. retTree = treeNode('Null Set', 1, None) #创建只包含空集的根节点

  67. ######################第二次遍历###########################

  68. for tranSet, count in dataSet.items(): #此处count为每个样本的频数

  69. localD = {}

  70. #############################################

  71. for item in tranSet: #通过for循环,把每个样本中频繁项集及其频数储存在localD字典中

  72. if item in freqItemSet:

  73. localD[item] = headerTable[item][0]

  74. #############################################

  75. if len(localD) > 0:

  76. orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]

  77. #items()方法返回一个可迭代的dict_items类型,其元素是键值对组成的2-元组

  78. #sorted(排序对象,key,reverse),当待排序列表的元素由多字段构成时,

  79. #我们可以通过sorted(iterable,key,reverse)的参数key来制定我们根据哪个字段对列表元素进行排序

  80. #这里key=lambda p: p[1]指明要根据键对应的值,即根据频繁项的频数进行从大到小排序

  81. updateTree(orderedItems, retTree, headerTable, count)

  82. #使用排序后的频繁项集对树进行填充

  83. ##########################################################

  84. return retTree, headerTable #返回树和头指针表

  85.  
  86. def updateTree(items, inTree, headerTable, count):

  87. """

  88. 函数说明:让FP树生长

  89. """

  90. if items[0] in inTree.children:

  91. #首先检查事物项items中第一个元素是否作为子节点存在

  92. #如果树中存在现有元素

  93. inTree.children[items[0]].inc(count) #则更新增加该元素项的计数,

  94. else: #否则向树增加一个分支

  95. inTree.children[items[0]] = treeNode(items[0], count, inTree)

  96. #创建一个新的树节点,并更新了父节点inTree,父节点是一个类对象,包含很多特性

  97. ############################不参与树的生长#############################

  98. if headerTable[items[0]][1] == None:

  99. #若原来指向每种类型第一个元素项的指针为 None,则需要更新头指针列表

  100. headerTable[items[0]][1] = inTree.children[items[0]]

  101. #更新头指针表,把指向每种类型第一个元素项放在头指针表里

  102. else:

  103. updateHeader(headerTable[items[0]][1], inTree.children[items[0]])

  104. #更新生成链表,注意,链表也是每过一个样本,更一次链表,且链表更新都是从头指针表开始的

  105. ######################################################################

  106. if len(items) > 1: #仍有未分配完的树,迭代

  107. updateTree(items[1::], inTree.children[items[0]], headerTable, count)

  108. #由items[1::]可知,每次调用updateTree时都会去掉列表中第一个元素,递归

  109.  
  110. def updateHeader(nodeToTest, targetNode):

  111. """

  112. 函数说明:它确保节点链接指向树中该元素项的每一个实例。

  113. """

  114. while (nodeToTest.nodeLink != None):

  115. nodeToTest = nodeToTest.nodeLink

  116. #从头指针表的 nodeLink 开始,一直沿着nodeLink直到到达链表末尾,这就是一个链表

  117. nodeToTest.nodeLink = targetNode

  118. #链表链接的都是相似元素项,通过ondeLink 变量用来链接相似的元素项。

  119.  
  120. def loadSimpDat():

  121. simpDat = [['r', 'z', 'h', 'j', 'p'],

  122. ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],

  123. ['z'],

  124. ['r', 'x', 'n', 'o', 's'],

  125. ['y', 'r', 'x', 'z', 'q', 't', 'p'],

  126. ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]

  127. return simpDat

  128. #createInitSet()

  129. def createInitSet(dataSet):

  130. """

  131. 函数说明:用于将列表形式的数据集dataSet格式化为一个‘键为一个样本,值为对应频数’的字典

  132. """

  133. retDict = {}

  134. for trans in dataSet:

  135. retDict[frozenset(trans)] = 1

  136. return retDict

  137. """

  138. if __name__=='__main__':

  139. simpDat=loadSimpDat()

  140. print('simpDat=',simpDat)

  141. initSet=createInitSet(simpDat)

  142. print('initSet=',initSet)

  143. myFPtree,myHeaderTab=createTree(initSet,3)

  144. myFPtree.disp()

  145. #说明:构建的fp-树可能不同但等价,这是由于我们是每遍历一个样本画一次图。

  146. #元素项出现的频数(z5,r3,x4,y3,s3,t3)

  147. #第一个样本{z,r},排序是根据他们出现的频数,所以z1->r1

  148. #第二个样本{z,x,y,s,t},排序是根据他们出现的频数,

  149. # z2->r1

  150. # ->x1->...

  151. #(注意:x以后的元素y,s,t的频数都是相同的,排序随机,所以画出的图可能不一样)

  152. #"""

  153.  
  154. """

  155. 从FP树中抽取频繁项集的三个基本步骤:

  156. (1) 从FP树中获得条件模式基;

  157. (2) 利用条件模式基,构建一个条件FP树;

  158. (3) 迭代重复步骤(1)步骤(2),直到树包含一个元素项为止。

  159. """

  160.  
  161. #从FP树中发现频繁项集

  162. def ascendTree(leafNode, prefixPath):

  163. """

  164. 函数说明:递归上溯整棵树,并把当前树节点的所有父节点处存在prefixPath列表中

  165. """

  166. if leafNode.parent != None:

  167. #判断当前叶节点是否有父节点,

  168. prefixPath.append(leafNode.name)

  169. #如果有的话,向 prefixPath列表添加当前树节点,看清不是添加父节点

  170. ascendTree(leafNode.parent, prefixPath)

  171. #通过递归不断向 prefixPath列表添加当前树节点,直到没有父节点

  172.  
  173.  
  174. def findPrefixPath(basePat, treeNode):

  175. """

  176. 函数说明:发现已给定元素项结尾的所有前缀路径

  177. 参数:给定元素项:basePat

  178. 在头指针表headerTable中储存着指向每种类型第一个元素项的指针:treeNode

  179. 返回值:储存条件模式基的一个字典

  180. """

  181. condPats = {}

  182. while treeNode != None:

  183. prefixPath = []

  184. ascendTree(treeNode, prefixPath) #寻找当前非空节点的前缀

  185. if len(prefixPath) > 1:

  186. condPats[frozenset(prefixPath[1:])] = treeNode.count

  187. #将条件模式基的个数赋给当前前缀路径,添加到字典中

  188. #prefixPath[1:],去掉第一项是因为第一项是给定元素项,不属于前缀路径

  189. treeNode = treeNode.nodeLink #更换链表的下一个节点,继续寻找前缀路径

  190. return condPats

  191. """

  192. if __name__=='__main__':

  193. simpDat=loadSimpDat()

  194. initSet=createInitSet(simpDat)

  195. myFPtree,myHeaderTab=createTree(initSet,3)

  196. #print('myFPtree=',myFPtree) #myFPtree是一个类对象

  197. #print('myHeaderTab=',myHeaderTab)

  198. print('x的条件模式基:',findPrefixPath('x', myHeaderTab['x'][1]))

  199. print('z的条件模式基:',findPrefixPath('z', myHeaderTab['z'][1]))

  200. print('r的条件模式基:',findPrefixPath('r', myHeaderTab['r'][1]))

  201. print('t的条件模式基:',findPrefixPath('t', myHeaderTab['t'][1]))

  202. #"""

  203.  
  204. #创建条件FP树

  205. def mineTree(inTree, headerTable, minSup, preFix, freqItemList):

  206. """

  207. 函数说明:递归查找频繁项集

  208. 参数: FP树 inTree

  209. 头指针表 headerTable

  210. 最小支持度 minSup

  211. 前缀路径 preFix(初始时一般为空,用来储存前缀路径)

  212. 频繁项列表 freqItemList(初始时一般为空,用来储存频繁项集)

  213. """

  214. bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: str(p[1]))]

  215. #python3修改

  216. # 头指针表中的元素项按照其出现频率排序,从小到大(默认),从头指针表的底层开始

  217. for basePat in bigL:

  218. #频繁项可能是一个,也可能是多个,经过上面的步骤把单个的频繁项储存bigL

  219. #寻找多个频繁项是从单个的频繁项开始的,因为一个项集是非频繁集,那么它的超级也是非频繁项集

  220. #所以下面就从头指针表的最底层的单个频繁项开始,一直向上寻找。

  221. newFreqSet = preFix.copy()

  222. newFreqSet.add(basePat)

  223. print ('finalFrequent Item: ',newFreqSet)

  224. freqItemList.append(newFreqSet)

  225. condPattBases = findPrefixPath(basePat, headerTable[basePat][1])

  226. #找见当前节点的前缀路径

  227. print ('condPattBases :',basePat, condPattBases) #输出条件模式基

  228. myCondTree, myHead = createTree(condPattBases, minSup)

  229. #将创建的条件基作为新的数据集输入到 FP树构建函数,来构建条件FP树

  230. #将前缀路径频繁项提取出来

  231. print ('head from conditional tree: ', myHead)

  232. if myHead != None:

  233. #若myHead为非空,说明条件 FP树还可以向上缩减。

  234. #说的详细一点,一开始我们通过当前节点basePat,找到条件模式基,进而创建条件FP树

  235. #这棵条件FP树就是通过当前节点basePat找的频繁项(当前节点basePat本身也是单个的频繁项)

  236. #接下来,我们要看若myHead为非空,找的频繁项的子集还是不是频繁项

  237. print ('conditional tree for: ',newFreqSet)

  238. myCondTree.disp(1)

  239. mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

  240. #若myHead为非空,就把找的的频繁项条件FP树从下往上依次去一个点,看它还是不是频繁项,递归。

  241. #把当前节点basePat的所有频繁项找到后,进行下一个节点的频繁项寻找

  242. """

  243. if __name__=='__main__':

  244. simpDat=loadSimpDat()

  245. initSet=createInitSet(simpDat)

  246. myFPtree,myHeaderTab=createTree(initSet,3)

  247. myFreqItems=[]

  248. mineTree(myFPtree,myHeaderTab,3,set([]),myFreqItems)

  249. print('myFreqItems=',myFreqItems)

  250.  
  251. #"""

  252. """

  253. #示例:从新闻网站点击流中挖掘

  254. if __name__=='__main__':

  255. parsedDat=[line.split() for line in open('kosarak.dat').readlines()] #将数据集导入

  256. initSet=createInitSet(parsedDat) #对初始集合格式化

  257. myFPtree,myHeaderTab=createTree(initSet,100000)

  258. #构建FP树,并从中寻找那些至少被10万人浏览过的新闻报道

  259. myFreqList=[] #需要创建一个空列表来保存这些频繁项集

  260. mineTree(myFPtree,myHeaderTab,100000,set([]),myFreqList)

  261. print('len(myFreqList)=',len(myFreqList))

  262. #看下有多少新闻报道或报道集合曾经被10万或者更多的人浏览过:

  263. print('myFreqList=',myFreqList)

  264.  
  265. #"""

 



版权声明:本文为博主辛苦整理的文章,如有参考或转载,请注明出处。尊重别人劳动成果,尊重自己。 https://blog.youkuaiyun.com/Lelouc_CC/article/details/80805296

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值