当前位置:   article > 正文

头歌实践教学平台-FPgrowth算法_graphaprior算法 第1关:候选生成一顶点增长 头歌

graphaprior算法 第1关:候选生成一顶点增长 头歌

目录

#第一关:头表建设

任务描述

本关任务:认识 FPgrowth 算法及FP树的头表构建。

相关知识

为了完成本关任务,你需要掌握:FP 树表示法及头表构建。

本节我们开始介绍一种称作 FP增长的算法。该算法采用完全不同的方法来发现频繁项集。该算法不同于 Apriori 算法的“产生测试”范型,而是使用一种称作 FP 树的紧凑数据结构组织数据,并直接从该结构中提取频繁项集。下面详细说明该方法。

FP树表示法及头表构建

FP 树是一种输入数据的压缩表示,它通过逐个读入事务,并把每个事务映射到 FP 树中的一条路径来构造。由于不同的事务可能会有若千个相同的项,因此它们的路径可能部分重叠。路径相互重叠越多,使用 FP 树结构获得的压缩的效果越好。如果 FP 树足够小,能够存放在内存中,就可以直接从这个内存中的结构提取频繁项集,而不必重复地扫描存放在硬盘上的数据。

,


图一 构建的FP树

上图显示了一个数据集,它包含 10 个事务和 5 个项。图中还绘制了读入前 3 个事务之后FP树的结构。树中每一个结点都包括一个项的标记和一个计数,计数显示映射到给定路径的事务个数。初始,FP 树仅包含一个根结点,用符号null标记。随后,用如下方法扩充 FP 树: (1) 扫描一次数据集,确定每个项的支持度计数。丢弃非频繁项,而将频繁项按照支持度的递减排序。对于上图中的数据集,a是最频繁的项,接下来依次是b,c, d,e
该过程是头表的构建过程,主要的代码实现如下:

 
  1. # 统计各项出现次数
  2. # 第一次遍历,得到频繁一项集
  3. headerTable = {} #头表构建
  4. for k in item_count: # 剔除不满足最小支持度的项
  5. if item_count[k] >= min_support:
  6. headerTable[k] = item_count[k]
  7. freqItemSet = set(headerTable.keys()) # 满足最小支持度的频繁项集
编程要求

根据提示,在右侧编辑器BeginEnd之间补充代码,完成 头表构建的过程。

测试说明

点击评测,平台会对你编写的代码进行测试。 如程序无误,运行程序后提示程序执行成功,结果见文档输出。,输出会展示在文档中; 如程序有误,无法通过评测,且提示程序执行出错

  1. import os
  2. import time
  3. from tqdm import tqdm
  4. class Node():
  5. def __init__(self, node_name, count, parentNode):
  6. self.name = node_name
  7. self.count = count
  8. self.nodeLink = None # 根据nideLink可以找到整棵树中所有nodename一样的节点
  9. self.parent = parentNode # 父亲节点
  10. self.children = {} # 子节点{节点名字:节点地址}
  11. class Fp_tree():
  12. def update_header(self, node, targetNode): # 更新headertable中的node节点形成的链表
  13. while node.nodeLink != None:
  14. node = node.nodeLink
  15. node.nodeLink = targetNode
  16. def update_fptree(self, items, node, headerTable): # 用于更新fptree
  17. if items[0] in node.children:
  18. # 判断items的第一个结点是否已作为子结点
  19. node.children[items[0]].count += 1
  20. else:
  21. # 创建新的分支
  22. node.children[items[0]] = Node(items[0], 1, node)
  23. # 更新相应频繁项集的链表,往后添加
  24. if headerTable[items[0]][1] == None:
  25. headerTable[items[0]][1] = node.children[items[0]]
  26. else:
  27. self.update_header(headerTable[items[0]][1], node.children[items[0]])
  28. # 递归
  29. if len(items) > 1:
  30. self.update_fptree(items[1:], node.children[items[0]], headerTable)
  31. def create_fptree(self, data_set, min_support, flag=False): # FPTree构建, 建树主函数
  32. '''
  33. 根据data_set创建fp树
  34. header_table结构为
  35. {"nodename":[num,node],..} 根据node.nodelink可以找到整个树中的所有nodename
  36. '''
  37. ###########Begin##########
  38. ###########End###########
  39. if flag:
  40. ite = tqdm(data_set)
  41. else:
  42. ite = data_set
  43. for t in ite: # 第二次遍历,建树
  44. localD = {}
  45. for item in t:
  46. if item in freqItemSet: # 过滤,只取该样本中满足最小支持度的频繁项
  47. localD[item] = headerTable[item][0] # element : count
  48. if len(localD) > 0:
  49. # 根据全局频数从大到小对单样本排序
  50. order_item = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)]
  51. # 用过滤且排序后的样本更新树
  52. self.update_fptree(order_item, tree_header, headerTable)
  53. return tree_header, headerTable

第一关通关代码

  1. ###########Begin##########
  2. # 头表构建
  3. item_count = {} # 统计各项出现次数
  4. for t in data_set: # 第一次遍历,得到频繁一项集
  5. for item in t:
  6. if item not in item_count:
  7. item_count[item] = 1
  8. else:
  9. item_count[item] += 1
  10. headerTable = {} #头表构建
  11. for k in item_count: # 剔除不满足最小支持度的项
  12. if item_count[k] >= min_support:
  13. headerTable[k] = [item_count[k], None] # element: [count, node]
  14. freqItemSet = set(headerTable.keys()) # 满足最小支持度的频繁项集
  15. tree_header = Node('head node', 1, None)
  16. ###########End###########

第二关:FPtree构建

任务描述

本关任务:完成FPtree的构建。

相关知识

为了完成本关任务,你需要掌握:1. FP 树表示法,2.FPtree的构建。

图一 构建的FP树 上节我们对FPtree做了基本认识并构建的头表,下面我们来看它整体的构造过程。初始,FP 树仅包含一个根结点,用符号null标记。随后,用如下方法扩充 FP 树: (1) 扫描一次数据集,确定每个项的支持度计数。丢弃非频繁项,而将频繁项按照支持度的递减排序。对于上图中的数据集,a是最频繁的项,接下来依次是b,c, d,e

(2) 算法第二次扫描数据集,构建 FP 树。读入第一个事务{a, b}之后,创建标记为 a 和 b 的结点。然后形成null→a→b路径,对该事务编码。该路径上的所有结点的频度计数为 1 。

(3) 读入第二个事务{b, c, d}之后,为项 b, c 和 d 创建新的结点集。然后,连接结点null→b→c→d,形成一条代表该事务的路径。该路径上的每个结点的频度计数也等于1。尽管前两个事务具有一个共同项 b ,但是它们的路径不相交,因为这两个事务没有共同的前缀。

(4) 第三个事务{a, c, d, e}与第一个事务共享一个共同前缀项 a ,所以第三个事务的路径null→a→c→d→e与第-个事务的路径null→a→b部分重叠。因为它们的路径重叠,所以结点 a 的频度计数增加为 2 ,而新创建的结点 c , d 和 e 的频度计数等于 1 。

(5) 继续该过程,直到每个事务都映射到 FP 树的一条路径。读入所有的事务后即可形成的 FP 树。

整个FP树构建的代码实现如下:

  1. for t in ite: # 第二次遍历,建树
  2. localD = {}
  3. for item in t:
  4. if item in freqItemSet: # 过滤,只取该样本中满足最小支持度的频繁项
  5. localD[item] = headerTable[item][0] # element : count
  6. if len(localD) > 0:
  7. # 根据全局频数从大到小对单样本排序
  8. order_item = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)]
  9. # 用过滤且排序后的样本更新树
  10. self.update_fptree(order_item, tree_header, headerTable)

上图构建的 FP 树还包含一个连接具有相同项的结点的指针列表。这些指针用虚线表示,有助于方便快速地访问树中的项。之后将使用FP树和它的相应指针产生频繁项集。

编程要求

根据提示,在右侧编辑器BeginEnd之间补充代码,完成 FPtree的构建过程。

测试说明

点击评测,平台会对你编写的代码进行测试。 如程序无误,运行程序后提示程序执行成功,结果见文档输出。,输出会展示在文档中; 如程序有误,无法通过评测,且提示程序执行出错

源代码

  1. import os
  2. import time
  3. from tqdm import tqdm
  4. class Node():
  5. def __init__(self, node_name, count, parentNode):
  6. self.name = node_name
  7. self.count = count
  8. self.nodeLink = None # 根据nideLink可以找到整棵树中所有nodename一样的节点
  9. self.parent = parentNode # 父亲节点
  10. self.children = {} # 子节点{节点名字:节点地址}
  11. class Fp_tree():
  12. def update_header(self, node, targetNode): # 更新headertable中的node节点形成的链表
  13. while node.nodeLink != None:
  14. node = node.nodeLink
  15. node.nodeLink = targetNode
  16. def update_fptree(self, items, node, headerTable): # 用于更新fptree
  17. if items[0] in node.children:
  18. # 判断items的第一个结点是否已作为子结点
  19. node.children[items[0]].count += 1
  20. else:
  21. # 创建新的分支
  22. node.children[items[0]] = Node(items[0], 1, node)
  23. # 更新相应频繁项集的链表,往后添加
  24. if headerTable[items[0]][1] == None:
  25. headerTable[items[0]][1] = node.children[items[0]]
  26. else:
  27. self.update_header(headerTable[items[0]][1], node.children[items[0]])
  28. # 递归
  29. if len(items) > 1:
  30. self.update_fptree(items[1:], node.children[items[0]], headerTable)
  31. def create_fptree(self, data_set, min_support, flag=False): # FPTree构建, 建树主函数
  32. '''
  33. 根据data_set创建fp树
  34. header_table结构为
  35. {"nodename":[num,node],..} 根据node.nodelink可以找到整个树中的所有nodename
  36. '''
  37. ###########Begin##########
  38. ###########End##########
  39. return tree_header, headerTable

通关代码

  1. ###########Begin##########
  2. # 头表构建
  3. item_count = {} # 统计各项出现次数
  4. for t in data_set: # 第一次遍历,得到频繁一项集
  5. for item in t:
  6. if item not in item_count:
  7. item_count[item] = 1
  8. else:
  9. item_count[item] += 1
  10. headerTable = {} #头表构建
  11. for k in item_count: # 剔除不满足最小支持度的项
  12. if item_count[k] >= min_support:
  13. headerTable[k] = [item_count[k], None] # element: [count, node]
  14. freqItemSet = set(headerTable.keys()) # 满足最小支持度的频繁项集
  15. tree_header = Node('head node', 1, None)
  16. # 建树
  17. if flag:
  18. ite = tqdm(data_set)
  19. else:
  20. ite = data_set
  21. for t in ite: # 第二次遍历,建树
  22. localD = {}
  23. for item in t:
  24. if item in freqItemSet: # 过滤,只取该样本中满足最小支持度的频繁项
  25. localD[item] = headerTable[item][0] # element : count
  26. if len(localD) > 0:
  27. # 根据全局频数从大到小对单样本排序
  28. order_item = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)]
  29. # 用过滤且排序后的样本更新树
  30. self.update_fptree(order_item, tree_header, headerTable)
  31. ###########End##########

第三关:{e)的条件FPtree构建

任务描述

本关任务:认识 FP 增长算法的频繁项集产生。

相关知识

为了完成本关任务,你需要掌握:FP 增长算法的频繁项集产生及条件FPtree构建。

FP 增长算法的频繁项集产生

FP 增长(FP-growth)是一种以自底向上方式探索树,由FP 树产生频繁项集的算法。给定图一产生的树,算法首先查找以 e 结尾的频繁项集,接下来依次是d,c,b,最后是a。由于每一个事务都映射到FP树中的一条路径,因而通过仅考察包含特定结点(例如e)的路径,就可以发现以e结尾的频繁项集.使用与结点e相关联的指针,可以快速访问这些路径。下图a显示了所提取的路径。之后我们便可以处理这些路径,以得到频繁项集。

,


图一 包含各结点的路径

发现以 e 结尾的频繁项集之后,算法通过处理与结点d相关联的路径,进一步寻找以 d 结尾的频繁项集。上图 b 显示了对应的路径。继续该过程,直到处理了所有与结点 c, b 和 a 相关联的路径为止。上图c、d、e分别显示了这些项的路径,而它们对应的频繁项集汇总如下:

后缀频繁项集
e{e},{d.e},{a,d,e},{c,e},{a,e}
d{d},{c,d},{b,c,d},{a,c,d},{b,d},{a,b,d},{a,d}
c{c],{b,c},{a,b,c},{a,c}
b{b},{a,b}
a{a}

FP 增长采用分治策略将一个问题分解为较小的子问题,从而发现以某个特定后缀结尾的所有频繁项集。例如,假设对发现所有以e结尾的频繁项集感兴趣。为了实现这个目的,必须首先检查项集{e}本身是否频繁。如果它是频繁的,则考虑发现以de结尾的频繁项集子问题,接下来是ceae。依次,每一个子问题可以进一步划分为更小的子问题。通过合并这些子问题得到的结果,就可以找到所有以 e 结尾的频繁项集。这种分治策略是FP增长算法采用的关键策略。

为了更具体地说明如何解决这些子问题,考虑发现所有以e结尾的频繁项集的任务:

(1) 第一步收集包含e结点的所有路径。这些初始的路径称为前缀路径(prefix path),如图二 a 所示。

,

图二 使用FP增长算法发现以e结尾的频繁项集

(2) 由图二a中所显示的前缀路径,通过把与结点e相关联的支持度计数相加得到 e 的支持度计数。假定最小支持度为 2 ,因为{e}的支持度是 3 ,所以它是频繁项集。

(3) 由于{e}是频繁的,因此算法必须解决发现以de、ce、 be 和ae结尾的频繁项集的子问题。在解决这些子问题之前,必须先将前缀路径转化为条件 FP 树(conditional FP-tree)。除了用于发现以某特定后缀结尾的频繁项集之外,条件 FP 树的结构与FP树类似。 条件FP树通过以下步骤得到: (a) 首先,必须更新前缀路径上的支持度计数,因为某些计数包括那些不含项e的事务。例如,图二 a 中的最右边路径null→b:2→c:2→e:1,包括并不含项 e 的事务{b,c}.因此,必须将该前缀路径上的计数调整为 1 ,以反映包含{b, c, e}的事务的实际个数。 (b) 删除 e 的结点,修剪前缀路径。删除这些结点是因为,沿这些前缀路径的支持度计数已经更新,以反映包含e的那些事务,并且发现以de, ce, be和ae结尾的频繁项集的子问题不再需要结点e的信息。 (c) 更新沿前缀路径上的支持度计数之后,某些项可能不再是频繁的。例如,结点b只出现了 1 次,它的支持度计数等于 1 ,这就意味着只有一个事务同时包含b和e。因为所有以be结尾的项集一定都是非频繁的,所以在其后的分析中可以安全地忽略 b。 创建{e}条件模式树过程的实现代码为:

 
  1. cond_pat_base = fp.find_cond_pattern_base('e', headerTable)
  2. cond_pat_dataset = [] # 将条件模式基字典转化为数组
  3. for item in cond_pat_base:
  4. item_temp = list(item)
  5. item_temp.sort()
  6. for i in range(cond_pat_base[item]):
  7. cond_pat_dataset.append(item_temp)
  8. # 创建条件模式树
  9. cond_tree, cur_headtable = fp.create_fptree(cond_pat_dataset, min_support)
编程要求

根据提示,在右侧编辑器BeginEnd之间补充代码,完成 FP条件树的构建过程。

测试说明

点击评测,平台会对你编写的代码进行测试。 如程序无误,运行程序后提示程序执行成功,结果见文档输出。,输出会展示在文档中; 如程序有误,无法通过评测,且提示程序执行出错

源代码

  1. import os
  2. import time
  3. from tqdm import tqdm
  4. class Node:
  5. def __init__(self, node_name, count, parentNode):
  6. self.name = node_name
  7. self.count = count
  8. self.nodeLink = None # 根据nideLink可以找到整棵树中所有nodename一样的节点
  9. self.parent = parentNode # 父亲节点
  10. self.children = {} # 子节点{节点名字:节点地址}
  11. class Base():
  12. def update_header(self, node, targetNode): # 更新headertable中的node节点形成的链表
  13. while node.nodeLink != None:
  14. node = node.nodeLink
  15. node.nodeLink = targetNode
  16. def update_fptree(self, items, node, headerTable): # 用于更新fptree
  17. if items[0] in node.children:
  18. # 判断items的第一个结点是否已作为子结点
  19. node.children[items[0]].count += 1
  20. else:
  21. # 创建新的分支
  22. node.children[items[0]] = Node(items[0], 1, node)
  23. # 更新相应频繁项集的链表,往后添加
  24. if headerTable[items[0]][1] == None:
  25. headerTable[items[0]][1] = node.children[items[0]]
  26. else:
  27. self.update_header(headerTable[items[0]][1], node.children[items[0]])
  28. # 递归
  29. if len(items) > 1:
  30. self.update_fptree(items[1:], node.children[items[0]], headerTable)
  31. def create_fptree(self, data_set, min_support, flag=False): # FPTree构建, 建树主函数
  32. '''
  33. 根据data_set创建fp树
  34. header_table结构为
  35. {"nodename":[num,node],..} 根据node.nodelink可以找到整个树中的所有nodename
  36. '''
  37. item_count = {} # 统计各项出现次数
  38. for t in data_set: # 第一次遍历,得到频繁一项集
  39. for item in t:
  40. if item not in item_count:
  41. item_count[item] = 1
  42. else:
  43. item_count[item] += 1
  44. headerTable = {} #头表构建
  45. for k in item_count: # 剔除不满足最小支持度的项
  46. if item_count[k] >= min_support:
  47. headerTable[k] = item_count[k]
  48. freqItemSet = set(headerTable.keys()) # 满足最小支持度的频繁项集
  49. if len(freqItemSet) == 0:
  50. return None, None
  51. for k in headerTable:
  52. headerTable[k] = [headerTable[k], None] # element: [count, node]
  53. tree_header = Node('head node', 1, None)
  54. if flag:
  55. ite = tqdm(data_set)
  56. else:
  57. ite = data_set
  58. for t in ite: # 第二次遍历,建树
  59. localD = {}
  60. for item in t:
  61. if item in freqItemSet: # 过滤,只取该样本中满足最小支持度的频繁项
  62. localD[item] = headerTable[item][0] # element : count
  63. if len(localD) > 0:
  64. # 根据全局频数从大到小对单样本排序
  65. order_item = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)]
  66. # 用过滤且排序后的样本更新树
  67. self.update_fptree(order_item, tree_header, headerTable)
  68. return tree_header, headerTable
  69. def find_path(self, node, nodepath):
  70. '''
  71. 递归将node的父节点添加到路径
  72. '''
  73. if node.parent != None:
  74. nodepath.append(node.parent.name)
  75. self.find_path(node.parent, nodepath)
  76. def find_cond_pattern_base(self, node_name, headerTable):
  77. '''
  78. 根据节点名字,找出所有条件模式基
  79. '''
  80. treeNode = headerTable[node_name][1]
  81. cond_pat_base = {} # 保存所有条件模式基
  82. while treeNode != None:
  83. nodepath = []
  84. self.find_path(treeNode, nodepath)
  85. if len(nodepath) > 1:
  86. cond_pat_base[frozenset(nodepath[:-1])] = treeNode.count
  87. treeNode = treeNode.nodeLink
  88. return cond_pat_base
  89. def create_cond_fptree(self, headerTable, min_support, temp, freq_items, support_data):
  90. '''
  91. 条件树建立的过程
  92. '''
  93. ##########Begin##########
  94. ############End##########
  95. return cur_headtable

通关代码

  1. ##########Begin##########
  2. freqs = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1][0])] # 根据频繁项的总频次排序
  3. for freq in freqs: # 对每个频繁项
  4. freq_set = temp.copy()
  5. freq_set.add(freq)
  6. freq_items.add(frozenset(freq_set))
  7. if frozenset(freq_set) not in support_data: # 检查该频繁项是否在support_data中
  8. support_data[frozenset(freq_set)] = headerTable[freq][0]
  9. else:
  10. support_data[frozenset(freq_set)] += headerTable[freq][0]
  11. cond_pat_base = self.find_cond_pattern_base(freq, headerTable) # 寻找到所有条件模式基
  12. cond_pat_dataset = [] # 将条件模式基字典转化为数组
  13. for item in cond_pat_base:
  14. item_temp = list(item)
  15. item_temp.sort()
  16. for i in range(cond_pat_base[item]):
  17. cond_pat_dataset.append(item_temp)
  18. cond_tree, cur_headtable = self.create_fptree(cond_pat_dataset, min_support)
  19. if cur_headtable != None:
  20. self.create_cond_fptree(cur_headtable, min_support, freq_set, freq_items, support_data) # 递归挖掘条件FP树
  21. return cur_headtable
  22. ############End##########

第四关:根据由{e)结尾的条件FPtree树计算由(e)结尾的频繁2项集

任务描述

本关任务:认识 FPgrowth 算法计算频繁项集。

相关知识

为了完成本关任务,你需要掌握:根据由{e)结尾的条件FPtree树计算由(e)结尾的频繁2项集。

FP 增长算法的频繁项集产生

上一个关卡中,我们通过(1)(2)(3)步骤已经完成了{e}条件下的FPtree构建,下面我们接着来看如何通过该条件树计算由(e)结尾的频繁2项集。

(4) FP 增长使用 e 的条件FP树来解决发现以de, ce, be和ae结尾的频繁项集的子问题。为了发现以 de 结尾的频繁项集,从项e的条件FP树收集d的所有前缀路径.通过将与结点 d 相关联的频度计数求和,得到项集{d, e}的支持度计数。因为项集{d, e}的支持度计数等于 2 ,所以它是频繁项集。接下来,算法采用第 3 步介绍的方法构建de的条件FP树。更新了支持度计数并删除了非频繁项c之后,de 的条件FP树显示在图三d中。因为该条件FP树只包含一个支持度等于最小支持度的项a,算法提取出频繁项集{a, d, e}并转到下一-个子问题,产生以ce结尾的频繁项集。处理c的前缀路径后,只发现项集{c, e}是频繁的。接下来,算法继续解决下一个子问题并发现项集{a, e}是剩下唯一的频繁项集。 使用e的条件FP树来计算以{e}结尾的频繁2项集的过程的实现如下:

 
  1. # 将条件模式基字典转化为数组
  2. # 创建条件模式树
  3. cond_tree, cur_headtable = fp.create_fptree(cond_pat_dataset, min_support)
  4. freqItemSet = set()
  5. support_data = {}
  6. if cur_headtable != None:
  7. fp.create_cond_fptree(cur_headtable, min_support, set(), freqItemSet, support_data) # 递归挖掘条件FP树
  8. print(cur_headtable)#根据e的条件FP树计算以e为结尾的频繁二项集

FP 增长是一一个有趣的算法,它展示了如何使用事务数据集的压缩表示来有效地产生频繁项集。此外,对于某些事务数据集,FP增长算法比标准的 Apriori 算法要快几个数量级。FP增长算法的运行性能依赖于数据集的压缩因子(compaction factor)。如果生成的条件P树非常茂盛(在最坏情形下,是一棵满前缀树),则算法的性能显著下降,因为算法必须产生大量的子问题,并且需要合并每个子问题返回的结果。

编程要求

根据提示,在右侧编辑器BeginEnd之间补充代码,完成 FP 增长算法寻找频繁二项集的过程。

测试说明

点击评测,平台会对你编写的代码进行测试。 如程序无误,运行程序后提示程序执行成功,结果见文档输出。,输出会展示在文档中; 如程序有误,无法通过评测,且提示程序执行出错

通关代码(我在最后面print那里加了else,也能通关)

  1. import os
  2. import time
  3. from tqdm import tqdm
  4. class Node:
  5. def __init__(self, node_name, count, parentNode):
  6. self.name = node_name
  7. self.count = count
  8. self.nodeLink = None # 根据nideLink可以找到整棵树中所有nodename一样的节点
  9. self.parent = parentNode # 父亲节点
  10. self.children = {} # 子节点{节点名字:节点地址}
  11. class Fp_growth():
  12. def update_header(self, node, targetNode): # 更新headertable中的node节点形成的链表
  13. while node.nodeLink != None:
  14. node = node.nodeLink
  15. node.nodeLink = targetNode
  16. def update_fptree(self, items, node, headerTable): # 用于更新fptree
  17. if items[0] in node.children:
  18. # 判断items的第一个结点是否已作为子结点
  19. node.children[items[0]].count += 1
  20. else:
  21. # 创建新的分支
  22. node.children[items[0]] = Node(items[0], 1, node)
  23. # 更新相应频繁项集的链表,往后添加
  24. if headerTable[items[0]][1] == None:
  25. headerTable[items[0]][1] = node.children[items[0]]
  26. else:
  27. self.update_header(headerTable[items[0]][1], node.children[items[0]])
  28. # 递归
  29. if len(items) > 1:
  30. self.update_fptree(items[1:], node.children[items[0]], headerTable)
  31. def create_fptree(self, data_set, min_support, flag=False): # FPTree构建, 建树主函数
  32. '''
  33. 根据data_set创建fp树
  34. header_table结构为
  35. {"nodename":[num,node],..} 根据node.nodelink可以找到整个树中的所有nodename
  36. '''
  37. item_count = {} # 统计各项出现次数
  38. for t in data_set: # 第一次遍历,得到频繁一项集
  39. for item in t:
  40. if item not in item_count:
  41. item_count[item] = 1
  42. else:
  43. item_count[item] += 1
  44. headerTable = {} #头表构建
  45. for k in item_count: # 剔除不满足最小支持度的项
  46. if item_count[k] >= min_support:
  47. headerTable[k] = item_count[k]
  48. freqItemSet = set(headerTable.keys()) # 满足最小支持度的频繁项集
  49. if len(freqItemSet) == 0:
  50. return None, None
  51. for k in headerTable:
  52. headerTable[k] = [headerTable[k], None] # element: [count, node]
  53. tree_header = Node('head node', 1, None)
  54. if flag:
  55. ite = tqdm(data_set)
  56. else:
  57. ite = data_set
  58. for t in ite: # 第二次遍历,建树
  59. localD = {}
  60. for item in t:
  61. if item in freqItemSet: # 过滤,只取该样本中满足最小支持度的频繁项
  62. localD[item] = headerTable[item][0] # element : count
  63. if len(localD) > 0:
  64. # 根据全局频数从大到小对单样本排序
  65. order_item = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)]
  66. # 用过滤且排序后的样本更新树
  67. self.update_fptree(order_item, tree_header, headerTable)
  68. return tree_header, headerTable
  69. def find_path(self, node, nodepath):
  70. '''
  71. 递归将node的父节点添加到路径
  72. '''
  73. if node.parent != None:
  74. nodepath.append(node.parent.name)
  75. self.find_path(node.parent, nodepath)
  76. def find_cond_pattern_base(self, node_name, headerTable):
  77. '''
  78. 根据节点名字,找出所有条件模式基
  79. '''
  80. treeNode = headerTable[node_name][1]
  81. cond_pat_base = {} # 保存所有条件模式基
  82. while treeNode != None:
  83. nodepath = []
  84. self.find_path(treeNode, nodepath)
  85. if len(nodepath) > 1:
  86. cond_pat_base[frozenset(nodepath[:-1])] = treeNode.count
  87. treeNode = treeNode.nodeLink
  88. return cond_pat_base
  89. def create_cond_fptree(self, headerTable, min_support, temp, freq_items, support_data):
  90. '''
  91. # 创建条件树
  92. '''
  93. # 最开始的频繁项集是headerTable中的各元素
  94. freqs = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1][0])] # 根据频繁项的总频次排序
  95. for freq in freqs: # 对每个频繁项
  96. freq_set = temp.copy()
  97. freq_set.add(freq)
  98. freq_items.add(frozenset(freq_set))
  99. if frozenset(freq_set) not in support_data: # 检查该频繁项是否在support_data中
  100. support_data[frozenset(freq_set)] = headerTable[freq][0]
  101. else:
  102. support_data[frozenset(freq_set)] += headerTable[freq][0]
  103. cond_pat_base = self.find_cond_pattern_base(freq, headerTable) # 寻找到所有条件模式基
  104. cond_pat_dataset = [] # 将条件模式基字典转化为数组
  105. for item in cond_pat_base:
  106. item_temp = list(item)
  107. item_temp.sort()
  108. for i in range(cond_pat_base[item]):
  109. cond_pat_dataset.append(item_temp)
  110. # 创建条件模式树
  111. cond_tree, cur_headtable = self.create_fptree(cond_pat_dataset, min_support)
  112. if cur_headtable != None:
  113. self.create_cond_fptree(cur_headtable, min_support, freq_set, freq_items, support_data) # 递归挖掘条件FP树
  114. def generate_L(self, data_set, min_support):
  115. freqItemSet = set()
  116. support_data = {}
  117. tree_header, headerTable = self.create_fptree(data_set, min_support, flag=True) # 创建数据集的fptree
  118. # 创建各频繁一项的fptree,并挖掘频繁项并保存支持度计数
  119. self.create_cond_fptree(headerTable, min_support, set(), freqItemSet, support_data)
  120. max_l = 0
  121. for i in freqItemSet: # 将频繁项根据大小保存到指定的容器L中
  122. if len(i) > max_l: max_l = len(i)
  123. L = [set() for _ in range(max_l)]
  124. for i in freqItemSet:
  125. L[len(i) - 1].add(i)
  126. for i in range(len(L)):
  127. print("frequent item {}:{}".format(i + 1, len(L[i])))
  128. return L, support_data
  129. def generate_R(self, data_set, min_support, min_conf):
  130. L, support_data = self.generate_L(data_set, min_support)
  131. rule_list = []
  132. sub_set_list = []
  133. for i in range(0, len(L)):
  134. for freq_set in L[i]:
  135. for sub_set in sub_set_list:
  136. if sub_set.issubset(
  137. freq_set) and freq_set - sub_set in support_data: # and freq_set-sub_set in support_data
  138. conf = support_data[freq_set] / support_data[freq_set - sub_set]
  139. big_rule = (freq_set - sub_set, sub_set, conf)
  140. if conf >= min_conf and big_rule not in rule_list:
  141. # print freq_set-sub_set, " => ", sub_set, "conf: ", conf
  142. rule_list.append(big_rule)
  143. sub_set_list.append(freq_set)
  144. rule_list = sorted(rule_list, key=lambda x: (x[2]), reverse=True)
  145. return rule_list
  146. if __name__ == "__main__":
  147. min_support = 2 # 最小支持度
  148. min_conf = 0.5 # 最小置信度
  149. data_set = [['a', 'b'], ['b', 'c', 'd'], ['a','c', 'd','e'], ['a', 'd', 'e'], ['a', 'b','c'], ['b', 'c'], ['a', 'b','c','d'],
  150. ['a', 'b', 'd'], ['a', 'b', 'c'], ['b', 'c', 'e']]
  151. #根据dataset获得规则
  152. fp = Fp_growth()
  153. rule_list = fp.generate_R(data_set, min_support, min_conf)
  154. print(rule_list)
  155. ##############Begin##################
  156. # 根据由{e}结尾的条件FPtree树计算由{e}结尾的频繁2项集
  157. # 创建Fptree
  158. e_condition_tree, e_header_table = fp.create_fptree([['e']], min_support)
  159. if e_condition_tree is not None and e_header_table is not None: # 检查结果是否为空
  160. cond_pattern_base = fp.find_cond_pattern_base('e', e_header_table)
  161. cond_pattern_dataset = []
  162. for item in cond_pattern_base:
  163. item_temp = list(item)
  164. item_temp.sort()
  165. for i in range(cond_pattern_base[item]):
  166. cond_pattern_dataset.append(item_temp)
  167. e_cond_tree, cur_headtable = fp.create_fptree(cond_pattern_dataset, min_support)
  168. print(cur_headtable) # 在这里打印 cur_headtable
  169. else:
  170. print("No frequent itemsets found for the given condition.")

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号