上一部分,我们讨论了apriori是如何减少子项集频繁度搜寻的。并且介绍了规则生成过程。
传送:https://blog.csdn.net/montecarlostyle/article/details/79850029
新问题提出
在类似商品关联挖掘的应用中,可能会遇到一个问题:那些具有较低支持度但是价值较高(高利润)的项目集不会生成。由于它们的支持度太低,在普通apriori算法迭代的过程中被抛弃了。例如,{食品加工机, 平底锅}是一个有意义的项目集,它的支持度=0.006%.如果我们把最小支持度调低为0.005%,我们可以利用apriori算法得到这个频繁项集,但同时会得到更多的无意义的频繁项目集。
由于商品之间的性质不同,有的商品是利润高的耐用品,有的是廉价的消耗品。耐用品购买的频率较少,但是利润普遍较高。对于这种情况,我们对它们使用统一的最小支持度就太不公平了。
思考
**以上问题的产生是因为我们对不同类型的商品使用了相同的支持度。**我们可以对每个项目(商品)都指定一个最小支持度(Minimum Item Support, MIS)。那么,一条规则R的最小支持度就是R中所有项目中最低的MIS值。我们称一条规则
i
1
,
i
2
,
.
.
.
,
i
k
−
>
i
k
+
1
,
.
.
.
,
i
r
i_1, i_2, ..., i_k -> i_{k+1}, ..., i_r
i1,i2,...,ik−>ik+1,...,ir
满足它的最小支持度,如果这条规则在数据集中的真实支持度大于或等于它们中的最小MIS值
m
i
n
(
M
I
S
(
i
1
)
,
M
I
S
(
i
2
)
,
.
.
.
,
M
I
S
(
i
r
)
)
min(MIS(i_1), MIS(i_2), ..., MIS(i_r))
min(MIS(i1),MIS(i2),...,MIS(ir))。这样,包含稀有项目的规则,其最小支持度很小,同时,频繁项目规则的最小支持度很大。
给每个项目设定MIS产生的问题——向下封闭性失效
下面我们用一个例子探讨这个方法有什么副作用。设一个数据集中有4个项目1, 2, 3, 4.它们的最小项目支持度分别为:
MIS(1)=10% MIS(2)=20% MIS(3)=5% MIS(4)=6%
假设{1, 2, 3}和{1, 2, 4}是我们要的频繁项(它们的最小支持度很低)。算法在第二轮搜索后得到项集{1, 2},支持度为9%,不满足项目1和2的最小支持度(10%)。按原则,它将要被丢弃。问题来了…把它丢弃之后,第三轮就不会生成{1, 2, 3}和{1, 2, 4}(为什么?回忆一下向下封闭性,通过其他方式生成{1, 2, 3}之后,它的子集{1, 2}不是频繁项集,立马又被剪掉了)。这样可能有点遭…向下封闭性失效了。难道要放弃剪枝,回归穷举法么?
补救——设置新的剪枝规则
我们先分析一下问题,刚才的例子中,{1, 2, 3}的最小支持度小于{1, 2}的最小支持度。因为加了个MIS比较低的3进来,{1, 2, 3}就可以是频繁项。我是不是频繁项集,你{1, 2}说了不算,关键看{2, 3},{1, 3}。下面有个解决思路:记下给频繁项{1, 2, 3}贡献最低支持度的那个元素3,剪枝过程中,只要3参与的子集有一个不是频繁项集,那{1, 2, 3}肯定也不是频繁项集。如果3参与的都是呢?那就留着这个候选项,一会儿送到全事件集里搜寻,看{1, 2, 3}的真实支持度是否真的大于3的MIS。
在实际操作中,每次都要寻找项集里的最小MIS项太麻烦了,则开始就按照MIS大小做个排序。以后项集的最小支持度就等于项集中第一个元素的MIS,多方便。另外,防止过小MIS的项目与大MIS项目相组合(假设90%的客人都要买口香糖,那就不能设定出一个规则:买了一台挖掘机的人通常也会买一个口香糖。),我们接着设置一个阈值φ,如果两个项集的最小支持度差别大于这个阈值,就不能进行合集操作。
——————————————————————————————————————————
Python实现
需要的数据有两种。transaction.txt存放购买记录,ms.txt说明每种商品的MIS值。
Beef,Bread
Bread,Clothes
Beef,Bread,Milk
Cheese,Boots
Beef,Bread,Cheese,Shoes
Beef,Bread,Cheese,Milk
Bread,Milk,Clothes
Milk:0.5
Bread:0.7
Clothes:0.25
Beef:0.25
Cheese:0.25
Boots:0.25
Shoes:0.25
数据文件要和代码放在同一目录。
结果如下图
Python代码
#-----------------------------------------------------
# function:MS-Apriori
# author:hanshuo
# date:2018-4-07
# tools:Python 2.7.6
# system:linux or Windows
#-----------------------------------------------------
import copy
def msApriori(tFileName = 'transaction.txt', msFileName = 'ms.txt', phi = 1):
T = transactionGet(tFileName)
MS = msGet(msFileName)
msListSorted = sorted(MS.items(), key = lambda x:x[1])
goodList = [i[0] for i in msListSorted]
n = len(T)
l = supCount(goodList, T)
L = []
for values in l:
if values[-1] >= msListSorted[0][-1]:
L.append(values)
itemSup = dict(L)
f1 = []
for items in L:
if items[1] >= MS[items[0]]:
f1.append(items)
print f1
c2 = leve2CandidateGen(L, MS, phi, n)
f2 = supCount(c2, T)
F = []
for eachIssue in f2:
if eachIssue[-1] >= MS[(eachIssue[0][0],)]:
F.append(eachIssue)
print F
f = f2
while True:
F = []
c = msCandidateGen(f, phi, itemSup, MS)
f = supCount(c, T)
for eachIssue in f:
if eachIssue[-1] >= MS[(eachIssue[0][0],)]:
F.append(eachIssue)
if F == []:
break
print F
def supCount(nonCountList, transactionList):
countList = []
n = float(len(transactionList))
for values in nonCountList:
count = 0
for issue in transactionList:
if set(values).issubset(set(issue)):
count += 1
countList.append((values, count/n))
return countList
def msGet(fileName = 'ms.txt'):
f = open(fileName)
msList = []
for line in f.readlines():
lineTuple = ((line.strip().split(':')[0],), eval(line.strip().split(':')[1]))
msList.append(lineTuple)
return dict(msList)
def transactionGet(fileName = 'transaction.txt'):
f = open(fileName)
transactionList = []
for line in f.readlines():
lineTuple = line.strip().split(',')
transactionList.append(lineTuple)
return transactionList
def leve2CandidateGen(L, ms, phi, n):
c2 = []
for i in range(len(L)):
if L[i][1] >= ms[L[i][0]]:
for values in L[i+1:]:
if (values[1] >= ms[L[i][0]]) & ((abs(L[i][1] - values[1])) <= phi):
c2.append((L[i][0][0], values[0][0]))
return c2
def msCandidateGen(f, phi, itemSup, ms):
c = []
for i in range(len(f)):
for value in f[i+1:]:
temp = list(copy.copy(f[i][0]))
if (abs(itemSup[(f[i][0][-1],)] - itemSup[(value[0][-1],)]) <= phi) & (f[i][0][:-1] == value[0][:-1]):
temp.append(value[0][-1])
c.append(tuple(temp))
for issue in c:
for item in issue:
issueCarbon = list(copy.copy(issue))
issueCarbon.remove(item)
if (item in issue) | (ms[(issue[1],)] == ms[(issue[0],)]):
if tuple(issueCarbon) not in [item[0] for item in f]:
c.remove(issue)
break
return c