表1某超市的交易数据库
交易号TID | 顾客购买的商品 | 交易号TID | 顾客购买的商品 |
---|---|---|---|
T1 | 面包,奶油,牛奶,茶 | T6 | 面包,茶 |
T2 | 面包,奶油,牛奶 | T7 | 啤酒,牛奶,茶 |
T3 | 蛋糕,牛奶 | T8 | 面包,茶 |
T4 | 牛奶,茶叶 T9 | 面包,奶油,牛奶,茶 | |
T5 | 面包,蛋糕,牛奶 | T10 | 面包,牛奶,茶 |
定义一:
设I = {i1,i2,…,im},是m个不同的项目的集合,每个ik称为一个项目。项目的集合我称为项集。其元素的个数称为项集的长度,长度为k的项集称为k-项集。引例中每个商品就是一个项目,项集为I = {面包,啤酒,蛋糕,奶油,牛奶,茶},我的长度为6.
定义二:
每笔交易T是项集I的一个子集。对应每一个交易有一个唯一标识交易号,记作TID。交易全体构成了交易数据库D,|D|等于D中交易的个数。引例中包含10笔交易,因此|D|=10。
定义三:
对于项集X,设定count(X⊆T)为交易集D中包含X的交易的数量,则项集X的支持度为: s u p p o r t ( X ) = c o u n t ( X ⊆ T ) / ∣ D ∣ support(X)=count(X⊆T)/|D| support(X)=count(X⊆T)/∣D∣
引例中X={bread, milk}出现在T1,T2,T5,T9和T10中,所以支持度为0.5。
定义四:
最小支持度是项集的最小支持阀值,记为SUPmin,代表了用户关心的关联规则的最低重要性。支持度不小于SUPmin 的项集称为频繁集,长度为k的频繁集称为k-频繁集。如果设定SUPmin为0.3,引例中{bread, milk}的支持度是0.5,所以是2-频繁集。
定义五:
关联规则是一个蕴含式: R : X ⇒ Y R:X⇒Y R:X⇒Y
其中X⊂I,Y⊂I,并且X∩Y=⌀。表示项集X在某一交易中出现,则导致Y以某一概率也会出现。用户关心的关联规则,可以用两个标准来衡量:支持度和可信度。
定义六:
关联规则R的支持度是交易集同时包含X和Y的交易数与|D|之比。即:
s u p p o r t ( X ⇒ Y ) = c o u n t ( X ⋂ Y ) / ∣ D ∣ support(X⇒Y)=count(X⋃Y)/|D| support(X⇒Y)=count(X⋂Y)/∣D∣
支持度反映了X、Y同时出现的概率。关联规则的支持度等于频繁集的支持度。
定义七:
关联规则R的可信度是指包含X和Y的交易数与包含X的交易数之比。即:
c o n f i d e n c e ( X ⇒ Y ) = s u p p o r t ( X ⇒ Y ) / s u p p o r t ( X ) confidence(X⇒Y)=support(X⇒Y)/support(X) confidence(X⇒Y)=support(X⇒Y)/support(X)
可信度反映了如果交易中包含X,则交易包含Y的概率。一般来说,只有支持度和可信度较高的关联规则才是用户感兴趣的。
定义八:
设定关联规则的最小支持度和最小可信度为SUPmin和CONFmin。规则R的支持度和可信度均不小于SUPmin和CONFmin ,则称为强关联规则。关联规则挖掘的目的就是找出强关联规则,从而指导商家的决策。
这八个定义包含了关联规则相关的几个重要基本概念,关联规则挖掘主要有两个问题:
首先来看一个频繁集的性质。
定理:如果项目集X是频繁集,那么它的非空子集都是频繁集。
根据定理,已知一个k-频繁集的项集X,X的所有k-1阶子集都肯定是频繁集,也就肯定可以找到两个k-1频繁集的项集,它们只有一项不同,且连接后等于X。这证明了通过连接k-1频繁集产生的k-候选集覆盖了k-频繁集。同时,如果k-候选集中的项集Y,包含有某个k-1阶子集不属于k-1频繁集,那么Y就不可能是频繁集,应该从候选集中裁剪掉。Apriori算法就是利用了频繁集的这个性质。
假如有项目集合T={1,2,3,4,5},有事务集T:
事务集T | 集合内容 | 事务集T | 集合内容 |
---|---|---|---|
T1 | 1,2,3 | T5 | 1,3,5 |
T2 | 1,2,4 | T6 | 2,4,5 |
T3 | 1,3,4 | T7 | 1,2,3,4 |
T4 | 1,2,3,5 |
设定SUPmin=3/7,CONFmin=5/7。
1、生成频繁项目集:
1-频繁项目集:{1},{2},{3},{4},{5}
2-频繁项目集:
根据1-频繁项目集生成所有的包含2个元素的项目集:任意取两个只有最后一个元素不同的1-频繁项目集,求其并集,由于每个1-频繁项目集元素只有一个,所以生成的项目集如下:
{1,2},{1,3},{1,4},{1,5}
{2,3},{2,4},{2,5}
{3,4},{3,5}
{4,5}
计算它们的支持度,发现只有{1,2},{1,3},{1,4},{2,3},{2,4},{2,5}的支持度满足要求,因此求得2-频繁项目集:
{1,2},{1,3},{1,4},{2,3},{2,4}
3-频繁项目集:
因为{1,2},{1,3},{1,4}除了最后一个元素以外都相同,所以求{1,2},{1,3}的并集得到{1,2,3}, {1,2}和{1,4}的并集得到{1,2,4},{1,3}和{1,4}的并集得到{1,3,4}。但是由于{1,3,4}的子集{3,4}不在2-频繁项目集中,所以需要把{1,3,4}剔除掉。然后再来计算{1,2,3}和{1,2,4}的支持度,发现{1,2,3}的支持度为3/7 ,{1,2,4}的支持度为2/7,所以需要把{1,2,4}剔除。同理可以对{2,3},{2,4}求并集得到{2,3,4} ,但是{2,3,4}的支持度不满足要求,所以需要剔除掉。
因此得到3-频繁项目集:{1,2,3}。
到此频繁项目集生成过程结束。注意生成频繁项目集的时候,频繁项目集中的元素个数最大值为事务集中事务中含有的最大元素个数,即若事务集中事务包含的最大元素个数为k,那么最多能生成k-频繁项目集,这个原因很简单,因为事务集合中的所有事务都不包含(k+1)个元素,所以不可能存在(k+1)-频繁项目集。在生成过程中,若得到的频繁项目集个数小于2,生成过程也可以结束了。
2、生成强关联规则:
关联规则:
{1,2} => 3,置信度({1,2} => 3)= 3/4
{1,3} => 2,置信度({1,3} => 2)= 3/5
{2,3} => 1,置信度({2,3} => 1)= 3/3
2 => {1,3},置信度(2 => {1,3})= 3/5
1 => {2,3},置信度(1 => {2,3})= 3/6
满足CONFmin = 5/7的强关联规则有:{1,2} => 3和{2,3} => 1
Apriori 算法
from __future__ import print_function
import pandas as pd
#自定义连接函数,用于实现L_{k-1}到C_k的连接
def connect_string(x, ms):
x = list(map(lambda i:sorted(i.split(ms)), x))
l = len(x[0])
r = []
for i in range(len(x)):
for j in range(i,len(x)):
if x[i][:l-1] == x[j][:l-1] and x[i][l-1] != x[j][l-1]:
r.append(x[i][:l-1]+sorted([x[j][l-1],x[i][l-1]]))
return r
#寻找关联规则的函数
def find_rule(d, support, confidence, ms = u'--'):
result = pd.DataFrame(index=['support', 'confidence']) #定义输出结果
support_series = 1.0*d.sum()/len(d) #支持度序列
column = list(support_series[support_series > support].index) #初步根据支持度筛选
k = 0
while len(column) > 1:
k = k+1
print(u'\n正在进行第%s次搜索...' %k)
column = connect_string(column, ms)
print(u'数目:%s...' %len(column))
sf = lambda i: d[i].prod(axis=1, numeric_only = True) #新一批支持度的计算函数
#创建连接数据,这一步耗时、耗内存最严重。当数据集较大时,可以考虑并行运算优化。
d_2 = pd.DataFrame(list(map(sf,column)), index = [ms.join(i) for i in column]).T
support_series_2 = 1.0*d_2[[ms.join(i) for i in column]].sum()/len(d) #计算连接后的支持度
column = list(support_series_2[support_series_2 > support].index) #新一轮支持度筛选
support_series = support_series.append(support_series_2)
column2 = []
for i in column: #遍历可能的推理,如{A,B,C}究竟是A+B-->C还是B+C-->A还是C+A-->B?
i = i.split(ms)
for j in range(len(i)):
column2.append(i[:j]+i[j+1:]+i[j:j+1])
cofidence_series = pd.Series(index=[ms.join(i) for i in column2]) #定义置信度序列
for i in column2: #计算置信度序列
cofidence_series[ms.join(i)] = support_series[ms.join(sorted(i))]/support_series[ms.join(i[:len(i)-1])]
for i in cofidence_series[cofidence_series > confidence].index: #置信度筛选
result[i] = 0.0
result[i]['confidence'] = cofidence_series[i]
result[i]['support'] = support_series[ms.join(sorted(i.split(ms)))]
result = result.T.sort_values(['confidence','support'], ascending = False) #结果整理,输出,DataFrame要用sort_values
pri
nt(u'\n结果为:
')
print(result)
return result
调用Apriori算法
#-*- coding: utf-8 -*-
from __future__ import print_function
import pandas as pd
from apriori import * #导入自行编写的apriori函数
import time #导入时间库用来计算用时
inputfile = '../data/apriori.txt' #输入事务集文件
data = pd.read_csv(inputfile, header=None, dtype = object)
start = time.clock() #计时开始
print(u'\n转换原始数据至0-1矩阵...')
ct = lambda x : pd.Series(1, index = x[pd.notnull(x)]) #转换0-1矩阵的过渡函数
b = map(ct, data.as_matrix()) #用map方式执行
c=list(b)# Dataframe参数不能是迭代器,故对b进行转换
data = pd.DataFrame(c).fillna(0) #实现矩阵转换,空值用0填充
print(data)
end = time.clock() #计时结束
print(u'\n转换完毕,用时:%0.2f秒' %(end-start))
del b #删除中间变量b,节省内存
support = 0.06 #最小支持度
confidence = 0.75 #最小置信度
ms = '---' #连接符,默认'--',用来区分不同元素,如A--B。需要保证原始表格中不含有该字符
start = time.clock() #计时开始
print(u'\n开始搜索关联规则...')
find_rule(data, support, confidence, ms)
end = time.clock() #计时结束
print(u'\n搜索完成,用时:%0.2f秒' %(end-start))