目录

FP树的基础知识

疑问与数据构建的想法

Python代码


FP树是用来挖掘最大频繁k项集的一种数据结构,相对来说难度较大,因为在前辈们的博客中,对于FP树的实现讲的是比较清楚了,但是对于FP的编程思路却提的很少。在这里做一个简单的梳理。

FP树的基础知识

首先请花一些时间认真阅读以下两篇博文。

Apriori算法原理总结

FP Tree算法原理总结

疑问与数据构建的想法

Q:项头表里面存储的节点链表,具体有什么用?该怎么理解?

A:博文中说到“第三部分是节点链表。所有项头表里的1项频繁集都是一个节点链表的头,它依次指向FP树中该1项频繁集出现的位置。这样做主要是方便项头表和FP Tree之间的联系查找和更新,也好理解”。。。尴尬,我当时看到这里就想说,我没理解。后面回顾发现,这里要存储节点链表,是因为提取条件模式基的时候,是从叶子节点开始找的。而很有可能存在多个item相同的叶子节点,例如下图的D。如果需要构建D的条件模式基,需要对5个条件FP树整合后来获得条件模式基,这是节点链表存在的原因。

Q:node类应该有什么属性?

A:item、count、parent_id、child_id_list(存在多个子节点)

Q:对于多路径的条件模式基,是怎么合成一个的?

A:以下图的FP树为例:

如果要提取G的条件模式基,对于父节点为C的G,条件模式基1为:

C:1 -> A:1 -> null

对于父节点为E的G,条件模式基2为:

E:4 -> C:4 -> A:4 -> null

合并以上两个条件模式基,得出条件模式基为:

E:4 -> C:5 -> A:5 -> null。

Q:FP树什么是事务?

A:如下,一条数据即是一个事务。TID即是Transaction ID的缩写。

Q:FP编程的算法链是怎么样的?

A:整体分三步

1.扫描数据,得到大于等于最低支持度的频繁一项集;扫描数据,将读到的原始数据剔除非频繁1项集,并按照支持度降序排列;读入数据集,建立FP树。项头表组成为{item:id_list},FP树建立完成时也得到了项头表。

2.根据项头表正序遍历(注意,这里和博客不太一样,是从根节点到)来获取条件FP树。获取FP树是一个递归过程,在suffix_item_parent_id(后缀项的父节点id)为根节点id时终止递归。

3.利用条件FP树来获取条件模式基,条件模式基递归挖掘,递归结束条件也是挖掘到根节点,得到与项头表的项相关的所有频繁k项集。

Q:两个递归,是怎么个递归法?

A:分开回答。这里后缀项的意思是频繁集的后缀项。例如一项频繁集['E']的后缀项就是它本身,二项频繁集[‘A’,‘E’]的后缀项为‘E’,同时二项频繁集又作为三项频繁集的后缀项。

1.对于获取条件FP树的递归,初步能想到的函数为get_path(fp_tree, suffix_items_parent_id, suffix_items_count)。每次获取一个父节点,如果父节点id和根节点id一致,就终止递归。

2.对于条件模式基递归挖掘频繁k项集,这里要稍微复杂一些。但是抓住递归的要领,就是每次递归参数的变化以及递归的终止条件。ok,那先确定挖掘频繁k项集的核心参数,def extract_frequent_k_itemsets(condition_fp_tree, suffix_items)。对于条件模式树,是否需要变化,例如在利用频繁一项集的条件模式树和频繁一项集挖掘出频繁二项集之后,想要利用频繁二项集来继续递归挖掘频繁三项集时,此时的条件模式树和之前是不一样的。或者说,后缀项为频繁一项集的条件FP树和后缀项为频繁二项集的条件FP树是不一样的。

还是以上面的图为例,

在后缀项为G时,G的条件FP树如下所示:

于是我们挖掘出G的频繁二项集['E','G']是count是4,此时基于['E','G']再来挖掘频繁三项集时,它的条件FP树不再是上图的

E:4 -> C:5 -> A:5 -> null,而是变成了C:5-> A:5->null。在每次改变模式树和后缀项的递归下,一直得出了频繁三项集、四项集、k项集。

Python代码

在Spark的MLIB中,应该有FP树的包,这里的代码基本是对下面这篇博文代码的照搬,感谢前辈的分享。跑下代码,再多调试,相信感受会更深刻。

关联分析-FPTree

from collections import defaultdict, Counter, deque
import math
import copy
from pandas import DataFrame as df
import numpy as np
import pandas as pd# 根据刘建平博客思路来尝试
# 树型结构最基础的类就是node和Tree
# FP树的节点链表有什么用处?如何建立?如何处理? 在后续搜索频繁模式树时会使用,在这里不建立节点链表,使用节点列表来代替,存储在频繁模式树中。
class node:def __init__(self, item, count, parent_id):self.item = itemself.count = countself.parent_id = parent_idself.child_id_list = []class FP_Tree:def __init__(self, minsup=0.5):self.minsup = minsupself.minsup_num = Noneself.transaction_num = None  # 一个传入数据集的事务数目self.item_head = defaultdict(list)  # 项头表{['A']:[1,2,4,5]}由item和对应的id list组成self.fp_tree = defaultdict()  # 键为节点id,值为nodeself.max_node_id = 0  # 当前树的最大节点id,用于插入节点时,新建node_id。self.frequent_one_itemsets = defaultdict(lambda: 0)  # 频繁一项集self.frequent_k_itemsets = []  # 频繁k项集self.frequent_k_itemsets_sup = []self.sort_keys = Nonedef ini_param(self, data):self.transaction_num = len(data)self.minsup_num = math.ceil(self.transaction_num * self.minsup)self.get_frequent_one_itemsets(data)self.create_fp_tree(data)def get_frequent_one_itemsets(self, data):c = Counter()for t in data:c += Counter(t)for key, value in c.items():if value >= self.minsup_num:self.frequent_one_itemsets[key] = value# 已初步获取频繁一项集,按照出现次数逆序排序self.frequent_one_itemsets = dict(sorted(self.frequent_one_itemsets.items(), key=lambda x: x[1], reverse=True))# 此处已获得了已排序的频繁一项集,考虑到后续的事务需要按照其键的顺序来进行排序和筛选,再返回FP_Tree的初始化函数加成员self.sort_keys = sorted(self.frequent_one_itemsets, key=self.frequent_one_itemsets.get, reverse=True)returndef create_fp_tree(self, data):frequent_one_itemsets_keys = set(self.frequent_one_itemsets.keys())# 创建根节点self.fp_tree[0] = node(item=None, count=0, parent_id=-1)for transaction in data:transaction = list(set(transaction) & frequent_one_itemsets_keys)  # 去除非频繁项if len(transaction) > 0:transaction = sorted(transaction, key=self.sort_keys.index)  # 对事务筛选后进行排序parent_id = 0for item in transaction:parent_id = self.insert_fptree(parent_id, item)returndef insert_fptree(self, parent_id, item):child_id_list = self.fp_tree[parent_id].child_id_listfor child_id in child_id_list:child_node = self.fp_tree[child_id]if child_node.item == item:self.fp_tree[child_id].count += 1return child_id# if return降低圈复杂度的同时,再判断当前的父节点的子节点中没有项与之匹配,所以新建子节点,更新项头表和树self.max_node_id += 1next_node_id = copy.copy(self.max_node_id)self.fp_tree[next_node_id] = node(item=item, count=1, parent_id=parent_id)  # 更新树,添加节点self.fp_tree[parent_id].child_id_list.append(next_node_id)  # 更新父节点的孩子列表self.item_head[item].append(next_node_id)  # 项头表的建立是和树的建立一并进行的return next_node_iddef get_frequent_k_itemsets(self, data):self.ini_param(data)# 现在已经构造好的数据类型有fp树,项头表,频繁一项集。现在提取频繁k项集# 这时候需要用到项头表里面的节点列表来向上搜索条件FP树,后通过条件FP树形成条件模式基,递归得出频繁k项集suffix_items_list = []suffix_items_id_list = []for key, value in self.frequent_one_itemsets.items():suffix_items = [key]suffix_items_list.append(suffix_items)suffix_items_id_list.append(self.item_head[key])self.frequent_k_itemsets.append(suffix_items)self.frequent_k_itemsets_sup.append(value)# self.frequent_k_itemsets = copy.deepcopy(self.frequent_one_itemsets)# 这里的前缀树是fp树的深拷贝,这样处理和Python的传对象方式有关。即在Python中基础类型是传值,复杂类型是传引用。pre_tree = copy.deepcopy(self.fp_tree)self.dfs_search(pre_tree, suffix_items_list, suffix_items_id_list)returndef dfs_search(self, pre_tree, suffix_items_list, suffix_items_id_list):for suffix_items, suffix_items_ids in zip(suffix_items_list, suffix_items_id_list):# 这时候需要用到项头表里面的节点列表来向上搜索条件FP树condition_fp_tree = self.get_condition_fp_tree(pre_tree, suffix_items_ids)# 根据条件模式基,获取频繁k项集new_suffix_items_list, new_suffix_items_id_list = self.extract_frequent_k_itemsets(condition_fp_tree,suffix_items)if new_suffix_items_list:  # 如果后缀有新的项添加进来,则继续递归深度搜索# 以开始的单项'G'后缀项为例,经过第一次提取k项频繁集后。单一后缀变为新的后缀项列表[['C', 'G'], ['A', 'G'],# ['E', 'G']],其计数5 5 4也加入到k项集的计数列表里面去了,new_suffix_items_id_list记录了新的后缀项节点id。# 此时把原本的pre_tree参数变为条件树,原本的单一后缀项参数变为new_suffix_items_list, 原本的后缀项id列表参数变# 为新的id项列表参数。# 在这样的递归过程中完成了对k项频繁集的挖掘。self.dfs_search(condition_fp_tree, new_suffix_items_list, new_suffix_items_id_list)returndef get_condition_fp_tree(self, pre_tree, suffix_items_ids):condition_tree = defaultdict()# 从各个后缀叶节点出发,综合各条路径形成条件FP树for suffix_items_id in suffix_items_ids:suffix_items_count = copy.copy(pre_tree[suffix_items_id].count)suffix_items_parent_id = pre_tree[suffix_items_id].parent_id# 后缀项父节点id为0的话,表示已经搜索到了根节点if suffix_items_parent_id == 0:continueself.get_path(pre_tree, condition_tree, suffix_items_parent_id, suffix_items_count)return condition_treedef get_path(self, pre_tree, condition_tree, suffix_items_parent_id, suffix_items_count):# 根据后缀的某个叶节点的父节点出发,选取出路径,并更新计数。suffix_item_count为后缀的计数if suffix_items_parent_id == 0:returnelse:if suffix_items_parent_id not in condition_tree.keys():# 把后缀计数赋值给页节点计数parent_node = copy.deepcopy(pre_tree[suffix_items_parent_id])parent_node.count = suffix_items_countcondition_tree[suffix_items_parent_id] = parent_nodeelse:  # 如果叶节点有多个,则路径可能重复,计数叠加condition_tree[suffix_items_parent_id].count += suffix_items_countsuffix_items_parent_id = condition_tree[suffix_items_parent_id].parent_idself.get_path(pre_tree, condition_tree, suffix_items_parent_id, suffix_items_count)returndef extract_frequent_k_itemsets(self, condition_fp_tree, suffix_items):# 根据条件模式基,提取频繁项集, suffix_item为该条件模式基对应的后缀# 返回新的后缀,以及新添加项(将作为下轮的叶节点)的idnew_suffix_items_list = []  # 后缀中添加的新项new_item_head = defaultdict(list)  # 基于当前的条件FP树,更新项头表, 新添加的后缀项item_sup_dict = defaultdict(int)for key, val in condition_fp_tree.items():item_sup_dict[val.item] += val.count  # 对项出现次数进行统计new_item_head[val.item].append(key)for item, sup in item_sup_dict.items():if sup >= self.minsup_num:  # 若条件FP树中某个项是频繁的,则添加到后缀中current_item_set = [item] + suffix_itemsself.frequent_k_itemsets.append(current_item_set)self.frequent_k_itemsets_sup.append(sup)new_suffix_items_list.append(current_item_set)else:new_item_head.pop(item)return new_suffix_items_list, new_item_head.values()def csv_file_to_df(csv_path):csv_file = csv_pathcsv_data = pd.read_csv(csv_file, low_memory=False)csv_df = pd.DataFrame(csv_data)return csv_dfdef print_max_frequent_k_itemsets(frequent_k_itemsets, frequent_k_itemsets_sup):num_list = [len(itemset) for itemset in frequent_k_itemsets]max_k_index_list = [i for i, x in enumerate(num_list) if x == max(num_list)]for index in max_k_index_list:print("max_frequent_k_itemsets:\n{}: {}".format(frequent_k_itemsets[index], frequent_k_itemsets_sup[index]))# max_frequent_k_itemsets = max(frequent_k_itemsets, key=len)# index = frequent_k_itemsets.index(max_frequent_k_itemsets)# print("{}: {}".format(max_frequent_k_itemsets, frequent_k_itemsets_sup[index]))if __name__ == '__main__':data1 = [list('ABCEFO'), list('ACG'), list('EI'), list('ACDEG'), list('ACEGL'),list('EJ'), list('ABCEFP'), list('ACD'), list('ACEGM'), list('ACEGN')]csv_df2 = csv_file_to_df("flowevent201901261500_cluster1.csv")csv_df_reduced2 = csv_df2[['srcIP', 'srcPort', 'dstIP', 'dstPort', 'rtt', 'erspanip']]fp_tree2 = FP_Tree(minsup=0.2)fp_tree2.get_frequent_k_itemsets((np.array(csv_df_reduced2).tolist()))print_max_frequent_k_itemsets(fp_tree2.frequent_k_itemsets, fp_tree2.frequent_k_itemsets_sup)

Python实现FP树相关推荐

  1. 详解python实现FP-TREE进行关联规则挖掘(带有FP树显示功能)附源代码下载(3)

    详解python实现FP-TREE进行关联规则挖掘(带有FP树显示功能)附源代码下载(3) 上一节简单讲了下FP树的生成,在这一节我将描述FP树的挖掘过程. 首先我们回顾一下要挖掘的特征项及样本空间: ...

  2. python中fp是什么意思_详解python实现FP-TREE进行关联规则挖掘(带有FP树显示功能)附源代码下载(3)...

    详解python实现FP-TREE进行关联规则挖掘(带有FP树显示功能)附源代码下载(3) 上一节简单讲了下FP树的生成,在这一节我将描述FP树的挖掘过程. 首先我们回顾一下要挖掘的特征项及样本空间: ...

  3. A-Priori算法及其优化(FP树)

    引子 在本文中,我们将通过示例先了解A-Priori算法,其基本思路是:若一个集合的子集不是频繁项集,那么该集合也不可能是频繁项集.基于此,该算法可以通过检查小集合而去掉大部分不合格的大集合. 接着, ...

  4. fp算法例题_Kmeans算法找相似商品、FP树找频繁项集

    第一题:用Kmeans处理数据集 数据集下载地址 运行环境 python3.7.PyCharm 2018.2.4 (Community Edition) 思路 根据所给数据集及其说明可以看出数据集共有 ...

  5. python判断 t1 树是否有与 t2 树拓扑结构完全相同的子树

    python判断 t1 树是否有与 t2 树拓扑结构完全相同的子树 # 给定彼此独立的两棵二叉树,判断 t1 树是否有与 t2 树拓扑结构完全相同的子树. class Node():def __ini ...

  6. python实现kd树以及最近邻查找算法

    python实现kd树以及最近邻查找算法 一.kd树简介 二.kd树生成 1.确定切分域 2.确定数据域 3.理解递归树 4.python实现递归树代码 三.kd树上的最近邻查找算法 1.生成搜索路径 ...

  7. FP-growth:FP树的构建

    前言 FP-growth是一个非常好的频繁项集发现算法,广泛应用于搜索引擎中(找出经常在一起出现的词对)相对于Apriori算法,FP-growth算法只用对数据库进行两次扫描,因此更适用于处理大数据 ...

  8. FP-growth算法发现频繁项集(一)——构建FP树

    常见的挖掘频繁项集算法有两类,一类是Apriori算法,另一类是FP-growth.Apriori通过不断的构造候选集.筛选候选集挖掘出频繁项集,需要多次扫描原始数据,当原始数据较大时,磁盘I/O次数 ...

  9. Python绘制分形树

    Python绘制分形树 任务要求 1.树干初始长度为50. 2.每次绘制完成树枝时,画笔右转20度. 3.绘制下一段树枝时,长度减少15,.重复2-3操作直至终止. 4.终止条件:树枝长度小于5,此时 ...

最新文章

  1. VIRTUAL COMMUNITY INFORMATICS
  2. SQL查询数据并插入新表
  3. SpringAMQP--入门案例的消息接收
  4. logback 的 access 模块
  5. android仿ios弹框_在“提示”框中:iOS外观(在Android上运行),Google Maps作为Time Machine,下载Wii游戏保存...
  6. 【跃迁之路】【599天】程序员高效学习方法论探索系列(实验阶段356-2018.09.27)...
  7. PrimeFaces Mobile入门
  8. dictionary 用linQ排序
  9. es6中class类的全方面理解(三)------静态方法
  10. 低代码发展专访系列之一:低代码平台产品的使用者都是谁?
  11. TCP协议的滑动窗口协议以及流量控制
  12. Text Particle Systems
  13. 如何防止数据库中的信息被“脱库”-笔记
  14. 思维转换感悟与区块链视频资料分享
  15. windows环境下curl安装
  16. win7站点服务器配置,IIS 7.0安装配置方法图文教程(win7)
  17. 谷歌f12能看到php代码吗,谷歌浏览器F12基本用法
  18. 梯度消失和梯度爆炸原因,表现,解决方案
  19. 傅里叶分析之掐死教程(完整版)更新于2014.06.06
  20. STM32内部RAM在线调试配置方法及详细说明(基于Keil开发工具)

热门文章

  1. Java实现输出特殊偏旁的汉字的功能
  2. HTTP/HTPPS协议
  3. Java通过快递鸟-查询物流
  4. 黎想首谈微信公众号搜索排名的规则?怎样提高排名?
  5. 2022RWCTF体验赛web的wp
  6. CCS6.1最后安装出现报错如下
  7. IIS报错:HTTP 错误 404.0 - Not Found 您要找的资源已被删除、已更名或暂时不可用。最可能的原因: 指定的目录或文件在 Web 服务器上不存在。 URL 拼写错误。。。。
  8. 第二次练车,练了一天倒库
  9. 新兴研究将如何更好地应对社会挑战?我们等你来共同探讨!
  10. 加速更新DNS解析记录的方法