Apriori算法和FPGrowth算法挖掘规则计算频繁项间的置信度
博主在进行了Apriori算法和FPgrowth算法的学习与完成置信度计算之后写下此篇文章,没有过多的理论介绍,理论学习可以 点击这里进行查看,此篇文章主要写出实现功能的代码,有些中间结果并未输出。但最终的置信度是进行了输出的。
数据准备
此处演示所用的数据集为书中作业题目的数据集:
dataset = [
[‘M’, ‘O’, ‘N’, ‘K’, ‘E’, ‘Y’],
[‘D’, ‘O’, ‘N’, ‘K’, ‘E’, ‘Y’],
[‘M’, ‘A’, ‘K’, ‘E’],
[‘M’, ‘U’, ‘C’, ‘K’, ‘Y’],
[‘C’, ‘O’, ‘O’, ‘K’, ‘I’, ‘E’]
]
另外,你使用的数据也可以来至文本文件,最终的结果也可以保存至文件中,如有需要可以联系我!微:18385457714
Apriori算法:
apriori算法流程
输入:数据集合D,支持度阈值α
输出:最大的频繁k项集
1)扫描整个数据集,得到所有出现过的数据,作为候选频繁1项集。k=1,频繁0项集为空集。
2)挖掘频繁k项集
a) 扫描数据计算候选频繁k项集的支持度
b) 去除候选频繁k项集中支持度低于阈值的数据集,得到频繁k项集。如果得到的频繁k项集为空,则直接返回频繁k-1项集的集合作为算法结果,算法结束。如果得到的频繁k项集只有一项,则直接返回频繁k项集的集合作为算法结果,算法结束。
c) 基于频繁k项集,连接生成候选频繁k+1项集。
3) 令k=k+1,转入步骤2。
实现代码
import itertools
def item(dataset): # 求第一次扫描数据库后的 候选集,(它没法加入循环)
c1 = [] # 存放候选集元素
for x in dataset: # 就是求这个数据库中出现了几个元素,然后返回
for y in x:
if [y] not in c1:
c1.append([y])
c1.sort()
# print(c1)
return c1
def get_frequent_item(dataset, c, min_support):
cut_branch = {} # 用来存放所有项集的支持度的字典
for x in c:
for y in dataset:
if set(x).issubset(set(y)): # 如果 x 不在 y中,就把对应元素后面加 1
cut_branch[tuple(x)] = cut_branch.get(tuple(x),
0) + 1 # cut_branch[y] = new_cand.get(y, 0)表示如果字典里面没有想要的关键词,就返回0
print("----------------候选集--------------------")
print(cut_branch)
Fk = [] # 支持度大于最小支持度的项集, 即频繁项集
sup_dataK = {} # 用来存放所有 频繁 项集的支持度的字典
for i in cut_branch:
if cut_branch[i] >= min_support: # Apriori定律1 小于支持度,则就将它舍去,它的超集必然不是频繁项集
Fk.append(list(i))
sup_dataK[i] = cut_branch[i]
print("=============频繁项集==============")
print(Fk)
return Fk, sup_dataK
def get_candidate(Fk, K): # 求第k次候选集
ck = [] # 存放产生候选集
for i in range(len(Fk)):
for j in range(i + 1, len(Fk)):
L1 = list(Fk[i])[:K - 2]
L2 = list(Fk[j])[:K - 2]
L1.sort()
L2.sort() # 先排序,在进行组合
if L1 == L2:
if K > 2: # 第二次求候选集,不需要进行减枝,因为第一次候选集都是单元素,且已经减枝了,组合为双元素肯定不会出现不满足支持度的元素
new = list(set(Fk[i]) ^ set(Fk[j])) # 集合运算 对称差集 ^ (含义,集合的元素在t或s中,但不会同时出现在二者中)
# new表示,这两个记录中,不同的元素集合
# 为什么要用new? 比如 1,2 1,3 两个合并成 1,2,3 我们知道1,2 和 1,3 一定是频繁项集,但 2,3呢,我们要判断2,3是否为频繁项集
# Apriori定律1 如果一个集合不是频繁项集,则它的所有超集都不是频繁项集
else:
new = set()
for x in Fk:
if set(new).issubset(set(x)) and list(
set(Fk[i]) | set(Fk[j])) not in ck: # 减枝 new是 x 的子集,并且 还没有加入 ck 中
ck.append(list(set(Fk[i]) | set(Fk[j])))
print(ck)
return ck
def Apriori(dataset, min_support=2):
c1 = item(dataset) # 返回一个二维列表,里面的每一个一维列表,都是第一次候选集的元素
f1, sup_1 = get_frequent_item(dataset, c1, min_support) # 求第一次候选集
F = [f1] # 将第一次候选集产生的频繁项集放入 F ,以后每次扫描产生的所有频繁项集都放入里面
sup_data = sup_1 # 一个字典,里面存放所有产生的候选集,及其支持度
K = 2 # 从第二个开始循环求解,先求候选集,在求频繁项集
while (len(F[K - 2]) > 1): # k-2是因为F是从0开始数的 #前一个的频繁项集个数在2个或2个以上,才继续循环,否则退出
ck = get_candidate(F[K - 2], K) # 求第k次候选集
fk, sup_k = get_frequent_item(dataset, ck, min_support) # 求第k次频繁项集
F.append(fk) # 把新产生的候选集假如F
sup_data.update(sup_k) # 字典更新,加入新得出的数据
K += 1
return F, sup_data # 返回所有频繁项集, 以及存放频繁项集支持度的字典
def generate_association_rules(patterns, confidence_threshold):
"""
Given a set of frequent itemsets, return a dict
of association rules in the form
{(left): ((right), confidence)}
"""
rules = []
for itemset in patterns.keys():
upper_support = patterns[itemset]
for i in range(1, len(itemset)):
for antecedent in itertools.combinations(itemset, i):
antecedent = tuple(sorted(antecedent))
consequent = tuple(sorted(set(itemset) - set(antecedent)))
if antecedent in patterns:
lower_support = patterns[antecedent]
confidence = float(upper_support) / lower_support
if confidence >= confidence_threshold:
rules.append([antecedent, consequent, confidence])
return rules
def printPatterns(patterns):
keys1 = list(patterns.keys())
values1 = list(patterns.values())
for i in range(len(keys1)):
keys1[i] = list(keys1[i])
for i in range(len(keys1)):
for j in range(len(keys1[i])):
print(keys1[i][j], end=" ")
for i2 in range(10 - 2 * len(keys1[i])):
print(" ", end="")
print(" : ", end="")
print(values1[i], end="\n")
def printRules2(rlues):
keys1 = []
values1 = []
for i in range(len(rules)):
keys1.append(list(rules[i][0]))
values1.append(list(rules[i][1]))
for i in range(len(keys1)):
for j in range(len(keys1[i])):
print(keys1[i][j], end=" ")
for i2 in range(10 - 2 * len(keys1[i])):
print(" ", end="")
print(" --> ", end="")
for i1 in range(len(values1[i])):
print(values1[i][i1], end=" ")
for i3 in range(10 - 2 * len(values1[i])):
print(" ", end="")
print(": " + str(rules[i][2]), end="\n")
if __name__ == '__main__':
dataset = [
['M', 'O', 'N', 'K', 'E', 'Y'],
['D', 'O', 'N', 'K', 'E', 'Y'],
['M', 'A', 'K', 'E'],
['M', 'U', 'C', 'K', 'Y'],
['C', 'O', 'O', 'K', 'I', 'E']
] # 装入数据 二维列表
F, sup_data = Apriori(dataset, min_support=3) # 最小支持度设置为3
rules = generate_association_rules(sup_data, 0.8) # 置信度(条件概率)删选
print("各频繁集及其出现次数如下", end="\n")
printPatterns(sup_data)
print('---------------------------------')
print("各强关联规则及其置信度如下", end="\n")
printRules2(rules)
执行结果示例:
FP-growth算法
FP-growth算法优点
FP-growth算法比Apriori算法效率更高,在整个算法执行过程中,只需遍历数据集2次,就能够完成频繁模式发现,其发现频繁项集的基本过程如下:
第一次:构建FP树
第二次:从FP树中挖掘频繁项集
FP-growth算法流程
FP-growth的一般流程如下:
1:先扫描一遍数据集,得到频繁项为1的项目集,定义最小支持度(项目出现最少次数),删除那些小于最小支持度的项目,然后将原始数据集中的条目按项目集中降序进行排列。
2:第二次扫描,创建项头表(从上往下降序),以及FP树。
3:对于每个项目(可以按照从下往上的顺序)找到其条件模式基(CPB,conditional patten base),递归调用树结构,删除小于最小支持度的项。如果最终呈现单一路径的树结构,则直接列举所有组合;非单一路径的则继续调用树结构,直到形成单一路径即可。
实现代码
# -*- coding: utf-8 -*-
from tqdm import tqdm
def load_data(): # 根据路径加载数据集
ans = [] # 将数据保存到该数组
reader = [
['M', 'O', 'N', 'K', 'E', 'Y'],
['D', 'O', 'N', 'K', 'E', 'Y'],
['M', 'A', 'K', 'E'],
['M', 'U', 'C', 'K', 'Y'],
['C', 'O', 'O', 'K', 'I', 'E']
]
for row in reader:
row = list(set(row)) # 去重,排序
row.sort()
ans.append(row) # 将添加好的数据添加到数组
return ans # 返回处理好的数据集,为二维数组
def show_confidence(rule):
index = 1
for item in rule:
s = " {:<4d} {:.3f} {}=>{}".format(index, item[2], str(list(item[0])), str(list(item[1])))
index += 1
print(s)
class Node:
def __init__(self, node_name, count, parentNode):
self.name = node_name
self.count = count
self.nodeLink = None # 根据nideLink可以找到整棵树中所有nodename一样的节点
self.parent = parentNode # 父亲节点
self.children = {} # 子节点{节点名字:节点地址}
class Fp_growth_plus():
def data_compress(self, data_set):
data_dic = {}
for i in data_set:
if frozenset(i) not in data_dic:
data_dic[frozenset(i)] = 1
else:
data_dic[frozenset(i)] += 1
return data_dic
def update_header(self, node, targetNode): # 更新headertable中的node节点形成的链表
while node.nodeLink != None:
node = node.nodeLink
node.nodeLink = targetNode
def update_fptree(self, items, count, node, headerTable): # 用于更新fptree
if items[0] in node.children:
# 判断items的第一个结点是否已作为子结点
node.children[items[0]].count += count
else:
# 创建新的分支
node.children[items[0]] = Node(items[0], count, node)
# 更新相应频繁项集的链表,往后添加
if headerTable[items[0]][1] == None:
headerTable[items[0]][1] = node.children[items[0]]
else:
self.update_header(headerTable[items[0]][1], node.children[items[0]])
# 递归
if len(items) > 1:
self.update_fptree(items[1:], count, node.children[items[0]], headerTable)
def create_fptree(self, data_dic, min_support, flag=False): # 建树主函数
'''
根据data_dic创建fp树
header_table结构为
{"nodename":[num,node],..} 根据node.nodelink可以找到整个树中的所有nodename
'''
item_count = {} # 统计各项出现次数
for t in data_dic: # 第一次遍历,得到频繁一项集
for item in t:
if item not in item_count:
item_count[item] = data_dic[t]
else:
item_count[item] += data_dic[t]
headerTable = {}
for k in item_count: # 剔除不满足最小支持度的项
if item_count[k] >= min_support:
headerTable[k] = item_count[k]
freqItemSet = set(headerTable.keys()) # 满足最小支持度的频繁项集
if len(freqItemSet) == 0:
return None, None
for k in headerTable:
headerTable[k] = [headerTable[k], None] # element: [count, node]
tree_header = Node('head node', 1, None)
if flag:
ite = tqdm(data_dic)
else:
ite = data_dic
for t in ite: # 第二次遍历,建树
localD = {}
for item in t:
if item in freqItemSet: # 过滤,只取该样本中满足最小支持度的频繁项
localD[item] = headerTable[item][0] # element : count
if len(localD) > 0:
# 根据全局频数从大到小对单样本排序
order_item = [v[0] for v in sorted(localD.items(), key=lambda x: x[1], reverse=True)]
# 用过滤且排序后的样本更新树
self.update_fptree(order_item, data_dic[t], tree_header, headerTable)
return tree_header, headerTable
def find_path(self, node, nodepath):
'''
递归将node的父节点添加到路径
'''
if node.parent != None:
nodepath.append(node.parent.name)
self.find_path(node.parent, nodepath)
def find_cond_pattern_base(self, node_name, headerTable):
'''
根据节点名字,找出所有条件模式基
'''
treeNode = headerTable[node_name][1]
cond_pat_base = {} # 保存所有条件模式基
while treeNode != None:
nodepath = []
self.find_path(treeNode, nodepath)
if len(nodepath) > 1:
cond_pat_base[frozenset(nodepath[:-1])] = treeNode.count
treeNode = treeNode.nodeLink
return cond_pat_base
def create_cond_fptree(self, headerTable, min_support, temp, freq_items, support_data):
# 最开始的频繁项集是headerTable中的各元素
freqs = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1][0])] # 根据频繁项的总频次排序
for freq in freqs: # 对每个频繁项
freq_set = temp.copy()
freq_set.add(freq)
freq_items.add(frozenset(freq_set))
if frozenset(freq_set) not in support_data: # 检查该频繁项是否在support_data中
support_data[frozenset(freq_set)] = headerTable[freq][0]
else:
support_data[frozenset(freq_set)] += headerTable[freq][0]
cond_pat_base = self.find_cond_pattern_base(freq, headerTable) # 寻找到所有条件模式基
# 创建条件模式树
cond_tree, cur_headtable = self.create_fptree(cond_pat_base, min_support)
if cur_headtable != None:
self.create_cond_fptree(cur_headtable, min_support, freq_set, freq_items, support_data) # 递归挖掘条件FP树
def generate_L(self, data_set, min_support):
data_dic = self.data_compress(data_set)
freqItemSet = set()
support_data = {}
tree_header, headerTable = self.create_fptree(data_dic, min_support, flag=True) # 创建数据集的fptree
# 创建各频繁一项的fptree,并挖掘频繁项并保存支持度计数
self.create_cond_fptree(headerTable, min_support, set(), freqItemSet, support_data)
max_l = 0
for i in freqItemSet: # 将频繁项根据大小保存到指定的容器L中
if len(i) > max_l: max_l = len(i)
L = [set() for _ in range(max_l)]
for i in freqItemSet:
L[len(i) - 1].add(i)
for i in range(len(L)):
print("frequent item {}:{}".format(i + 1, len(L[i])))
return L, support_data
def generate_R(self, data_set, min_support, min_conf):
L, support_data = self.generate_L(data_set, min_support)
rule_list = []
sub_set_list = []
for i in range(0, len(L)):
for freq_set in L[i]:
for sub_set in sub_set_list:
if sub_set.issubset(
freq_set) and freq_set - sub_set in support_data: # and freq_set-sub_set in support_data
conf = support_data[freq_set] / support_data[freq_set - sub_set]
big_rule = (freq_set - sub_set, sub_set, conf)
if conf >= min_conf and big_rule not in rule_list:
# print freq_set-sub_set, " => ", sub_set, "conf: ", conf
rule_list.append(big_rule)
sub_set_list.append(freq_set)
rule_list = sorted(rule_list, key=lambda x: (x[2]), reverse=True)
return rule_list
if __name__ == "__main__":
min_support = 3 # 最小支持度
min_conf = 0.8 # 最小置信度
data_set = load_data()
print(data_set)
fp = Fp_growth_plus()
rule_list = fp.generate_R(data_set, min_support, min_conf)
print("confidence:")
show_confidence(rule_list)
执行结果示例
如果你刚好需要使用,那么修改为你需要的数据集、最小支持度,最小置信度即可,
数据集、最小支持度和置信度的位置
希望能对你有用,欢迎评论和指出错误,欢迎一键三连,就不设置收费才能看了。希望大家多多支持!!!此篇文章也有参考借鉴其他大佬的文章进行修改创造,如有侵权,请联系删除。