赞
踩
Apriori 算法是最为经典的 关联规则挖掘算法 之一。
相关算法介绍参考博客:Apriori算法
关于关联规则有个很常见的例子:啤酒与尿布。沃尔玛在分析销售记录时,发现啤酒和尿布经常一起被购买,于是他们调整了货架,把两者放在一起,结果真的提升了啤酒的销量。
关联规则分析也称为购物篮分析,最早是为了发现超市销售数据库中不同的商品之间的关联关系。关联规则是反映一个事物与其他事物之间的关联性,若多个事物之间存在着某种关联关系,那么其中的一个事物就能通过其他事物预测到。
这里要注意,关联规则不是因果关系,有可能有因果关系,有可能没有。
定义 1 :两个不相交的非空集合 X , Y X, Y X,Y ,如果有 X → Y X →Y X→Y ,就说 X → Y X →Y X→Y 是一条关联规则。如吃咸菜的人偏爱喝粥( 咸菜->粥)就是一条关联规则。
关联规则的强度(可信度)用 支持度(support) 和 自信度(confidence) 来描述。
定义 2 :项集
X
,
Y
X, Y
X,Y 同时发生的概率称为关联规则
X
→
Y
X →Y
X→Y 的支持度(support):
最小支持度是用户或专家定义的用来衡量支持度的一个阈值,表示关联规则具有统计学意义的最低重要性。具有“统计学意义”的显著特征就是这个事件发生的概率/频率不能太低(否则就不具有推广到其他个体的价值)。
由于现实生活中,常用古典概型估计概率,因此,上式也可写为:
定义 3 :项集
X
X
X 发生的前提下,项集
Y
Y
Y 发生的概率称为关联规则
x
→
Y
x →Y
x→Y 的自信度(confidence 条件概率):
最小置信度是用户或专家定义的用来衡量置信度的一个阈值,表示关联规则的最低可靠性。同理,在古典概型中,上式也可以写为:
Apriori 算法是最经典也是最常用的挖掘频繁项集的算法,其核心思想是通过连接产生候选项及其支持度,然后通过剪枝生成频繁项集。
Apriori算法主要包含两个步骤:
1. 生成频繁项集
这一阶段找出所有满足 最小支持度的项集(具有统计学意义的组合),找出的这些项集称为频繁项集。自信度与支持度的计算涉及到多个列表的循环,极大影响频繁项集的生成时间。
2. 生成关联规则
在上一步产生的频繁项集的基础上生成满足最小自信度的规则,产生的规则称为强规则。
这里涉及两个重要的定理:
1. 如果一个集合是频繁项集,则它的所有子集都是频繁项集。 假设一个集合{A,B}是频繁项集,则它的子集{A}, {B} 都是频繁项集。
2. 如果一个集合不是频繁项集,则它的所有超集都不是频繁项集。 假设集合{A}不是频繁项集,则它的任何超集如{A,B},{A,B,C}必定也不是频繁项集。
根据定理1和定理2易知:若
X
→
Y
X →Y
X→Y 是强规则,则
X
,
Y
,
X
Y
X, Y, XY
X,Y,XY 都必须是频繁项集。
使用的编程语言:python3.6.4 (Anaconda3)
使用的编辑器:pycharm
使用的模块:pandas、itertools(标准库模块)
# !/usr/bin/env python # -*- coding:utf-8 -*- # version: Python 3.6.4 on win64 # author: Suranyi time: 8/6 # title: Apriori算法 import os,itertools import numpy as np import pandas as pd class Apriori(object): def __init__(self, itemSets, minSupport=0.5, minConf=0.7, sort = False): self.itemSets = itemSets self.minSupport = minSupport self.minConf = minConf self.sort = sort self.__Initialize() def __Initialize(self): self.__item() self.__creat_matrix() self.update(minSupport=self.minSupport, minConf=self.minConf) def __item(self): '''获取项目元素列表''' self.item = [] for itemSet in self.itemSets: for item in itemSet: if item not in self.item: self.item.append(item) self.item.sort() def __creat_matrix(self): '''将项集转为pandas.DataFrame数据类型''' self.data = pd.DataFrame(columns=self.item) for i in range(len(self.itemSets)): self.data.loc[i, self.itemSets[i]] = 1 def __candidate_itemsets_l1(self): '''创建单项频繁项集及L1''' self.L1 = self.data.loc[:, self.data.sum(axis=0) / len(self.itemSets) >= self.minSupport] self.L1_support_selects = dict(self.L1.sum(axis=0) / len(self.itemSets)) # 只作为分母,不进行拆分 def __candidate_itemsets_lk(self): '''根据L1创建多项频繁项集Lk,非频繁项集的任何超集都不是频繁项集''' last_support_selects = self.L1_support_selects.copy() # 初始化 while last_support_selects: new_support_selects = {} for last_support_select in last_support_selects.keys(): for L1_support_name in set(self.L1.columns) - set(last_support_select.split(',')): columns = sorted([L1_support_name] + last_support_select.split(',')) # 新的列名:合并后排序 count = (self.L1.loc[:, columns].sum(axis=1) == len(columns)).sum() if count / len(self.itemSets) >= self.minSupport: new_support_selects[','.join(columns)] = count / len(self.itemSets) self.support_selects.update(new_support_selects) last_support_selects = new_support_selects.copy() # 作为新的 Lk,进行下一轮更新 def __support_selects(self): '''支持度选择''' self.__candidate_itemsets_l1() self.__candidate_itemsets_lk() self.item_Conf = self.L1_support_selects.copy() self.item_Conf.update(self.support_selects) def __confidence_selects(self): '''生成关联规则,其中support_selects已经按照长度大小排列''' for groups, Supp_groups in self.support_selects.items(): groups_list = groups.split(',') for recommend_len in range(1, len(groups_list)): for recommend in itertools.combinations(groups_list, recommend_len): items = ','.join(sorted(set(groups_list) - set(recommend))) Conf = Supp_groups / self.item_Conf[items] if Conf >= self.minConf: self.confidence_select.setdefault(items, {}) self.confidence_select[items].setdefault(','.join(recommend),{'Support': Supp_groups, 'Confidence': Conf}) def show(self,**kwargs): '''可视化输出''' if kwargs.get('data'): select = kwargs['data'] else: select = self.confidence_select items = [] value = [] for ks, vs in select.items(): items.extend(list(zip([ks] * vs.__len__(), vs.keys()))) for v in vs.values(): value.append([v['Support'], v['Confidence']]) index = pd.MultiIndex.from_tuples(items, names=['Items', 'Recommend']) self.rules = pd.DataFrame(value, index=index, columns=['Support', 'Confidence']) if self.sort or kwargs.get('sort'): result = self.rules.sort_values(by=['Support', 'Confidence'], ascending=False) else: result = self.rules.copy() return result def update(self, **kwargs): '''用于更新数据''' if kwargs.get('minSupport'): self.minSupport = kwargs['minSupport'] self.support_selects = {} # 用于储存满足支持度的频繁项集 self.__support_selects() if kwargs.get('minConf'): self.minConf = kwargs['minConf'] self.confidence_select = {} # 用于储存满足自信度的关联规则 self.__confidence_selects() print(self.show()) if kwargs.get('file_name'): file_name = kwargs['file_name'] if file_name.endswith(".xlsx"): self.show().to_excel(f'{file_name}') else: self.show().to_excel(f'{file_name}.xlsx') self.apriori_rules = self.rules.copy() def __get_Recommend_list(self,itemSet): '''输入数据,获取关联规则列表''' self.recommend_selects = {} itemSet = set(itemSet) & set(self.apriori_rules.index.levels[0]) if itemSet: for start_str in itemSet: for end_str in self.apriori_rules.loc[start_str].index: start_list = start_str.split(',') end_list = end_str.split(',') self.__creat_Recommend_list(start_list, end_list, itemSet) def __creat_Recommend_list(self,start_list,end_list,itemSet): '''迭代创建关联规则列表''' if set(end_list).issubset(itemSet): start_str = ','.join(sorted(start_list+end_list)) if start_str in self.apriori_rules.index.levels[0]: for end_str in self.apriori_rules.loc[start_str].index: start_list = start_str.split(',') end_list = end_str.split(',') self.__creat_Recommend_list(sorted(start_list),end_list,itemSet) elif not set(end_list) & itemSet: start_str = ','.join(start_list) end_str = ','.join(end_list) self.recommend_selects.setdefault(start_str, {}) self.recommend_selects[start_str].setdefault(end_str, {'Support': self.apriori_rules.loc[(start_str, end_str), 'Support'], 'Confidence': self.apriori_rules.loc[(start_str, end_str), 'Confidence']}) def get_Recommend(self,itemSet,**kwargs): '''获取加权关联规则''' self.recommend = {} self.__get_Recommend_list(itemSet) self.show(data = self.recommend_selects) items = self.rules.index.levels[0] for item_str in items: for recommends_str in self.rules.loc[item_str].index: recommends_list = recommends_str.split(',') for recommend_str in recommends_list: self.recommend.setdefault(recommend_str,0) self.recommend[recommend_str] += self.rules.loc[(item_str,recommends_str),'Support'] * self.rules.loc[(item_str,recommends_str),'Confidence'] * self.rules.loc[item_str,'Support'].mean()/(self.rules.loc[item_str,'Support'].sum()*len(recommends_list)) result = pd.Series(self.recommend,name='Weight').sort_values(ascending=False) result.index.name = 'Recommend' result = result/result.sum() result = 1/(1+np.exp(-result)) print(result) if kwargs.get('file_name'): file_name = kwargs['file_name'] if file_name.endswith(".xlsx"): excel_writer = pd.ExcelWriter(f'{file_name}') else: excel_writer = pd.ExcelWriter(f'{file_name}.xlsx') result.to_excel(excel_writer,'推荐项目及权重') self.rules.to_excel(excel_writer, '关联规则树状表') self.show().to_excel(excel_writer, '总关联规则树状表') self.show(sort = True).to_excel(excel_writer, '总关联规则排序表') excel_writer.save() return result def str2itemsets(strings, split=','): '''将字符串列表转化为对应的集合''' itemsets = [] for string in strings: itemsets.append(sorted(string.split(split))) return itemsets if __name__ == '__main__': # 1.导入数据 data = pd.read_excel(r'apriori算法实现.xlsx', index=False) # 2.关联规则中不考虑多次购买同一件物品,删除重复数据 data = data.drop_duplicates() # 3.初始化列表 itemSets = [] # 3.按销售单分组,只有1件商品的没有意义,需要进行过滤 groups = data.groupby(by='销售单明细') for group in groups: if len(group[1]) >= 2: itemSets.append(group[1]['商品编码'].tolist()) # 4.训练 Apriori ap = Apriori(itemSets, minSupport=0.03, minConf=0.5) ap.get_Recommend('2BYP206,2BYW001-,2BYW013,2BYX029'.split(','))
参考数据文件: apriori算法实现.xlsx,该数据为某药店销售单数据,已做脱敏处理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。