首先我们来看,什么是规则?规则形如”如果…那么…(If…Then…)”,前者为条件,后者为结果。关联规则挖掘用于寻找给定数据集中项之间的有趣的关联或相关关系。关联规则揭示了数据项间的未知的依赖关系,根据所挖掘的关联关系,可以从一个数据对象的信息来推断另一个数据对象的信息。例如购物篮分析。牛奶 ⇒ 面包 [支持度:3%,置信度:40%]
假设有如下表的购买记录。
顾客 | 项目 |
1 | orange juice, coke |
2 | milk, orange juice, window cleaner |
3 | orange juice, detergent |
4 | orange juice, detergent, coke |
5 | window cleaner |
将上表整理一下,得到如下的一个2维表
| Orange | Win Cl | Milk | Coke | Detergent |
Orange | 4 | 1 | 1 | 2 | 2 |
WinCl | 1 | 2 | 1 | 0 | 0 |
Milk | 1 | 1 | 1 | 0 | 0 |
Coke | 2 | 0 | 0 | 2 | 1 |
Detergent | 1 | 0 | 0 | 0 | 2 |
上表中横栏和纵栏的数字表示同时购买这两种商品的交易条数。如购买有Orange的交易数为4,而同时购买Orange和Coke的交易数为2。
置信度表示了这条规则有多大程度上值得可信。设条件的项的集合为A,结果的集合为B。置信度计算在A中,同时也含有B的概率。即Confidence(A==>B)=P(B|A)。例如计算"如果Orange则Coke"的置信度。由于在含有Orange的4条交易中,仅有2条交易含有Coke.其置信度为0.5。
支持度计算在所有的交易集中,既有A又有B的概率。例如在5条记录中,既有Orange又有Coke的记录有2条。则此条规则的支持度为2/5=0.4。现在这条规则可表述为,如果一个顾客购买了Orange,则有50%的可能购买Coke。而这样的情况(即买了Orange会再买Coke)会有40%的可能发生。
再来考虑下述情况。
项 | 支持度 |
A | 0.45 |
B | 0.42 |
C | 0.4 |
A and B | 0.25 |
A and C | 0.2 |
B and C | 0.15 |
A,B,and C | 0.05 |
可得到下述规则
规则 | 置信度 |
If B and C then A | 0.05/0.15*100%=33.33% |
If A and C then B | 0.05/0.20*100%=25% |
If A and B then C | 0.05/0.25*100%=20% |
上述的三条规则,哪一条规则有用呢?
对于规则" If B and C then A",同时购买B和C的人中,有33.33%会购买A。而单项A的支持度有0.45,也就是说在所有交易中,会有45%的人购买A.看来使用这条规则来进行推荐,还不如不推荐,随机对顾客进荐好了。
为此引入另外一个量,即提升度(Lift),以度量此规则是否可用。描述的是相对于不用规则,使用规则可以提高多少。有用的规则的提升度大于1。计算方式为Lift(A==>B)=Confidence(A==>B)/Support(B)=Support(A==>B)/(Support(A)*Support(B))。在上例中,Lift(If B and C The A)=0.05/(0.15*0.45)=0.74。而Lift(If A then B)=0.25/(0.45*0.42)=1.32。也就是说对买了A的人进行推荐B,购买概率是随机推荐B的1.32倍。
如何产生规则呢。可以分两步走。
首先找出频繁集(frequent itemset)。所谓频繁集指满足最小支持度或置信度的集合。其次从频繁集中找出强规则(strong rules)。强规则指既满足最小支持度又满足最小置信度的规则。
我们来看如何产生频繁集。
这其中有一个定理。即频繁集的子集也一定是频繁集。比如,如果{A,B,C}是一个3项的频繁集,则其子集{A,B},{B,C},{A,C}也一定是2项的频繁集。为方便,可以把含有k项的集合称之为k-itemsets.
下面以迭代的方式找出频繁集。首先找出1-itemsets的频繁集,然后使用这个1-itemsets,进行组合,找出2-itemsets的频繁集。如此下去,直到不再满足最小支持度或置信度的条件为止。这其中重要的两步骤分别是连接(join)和剪枝(prune).即从(k-1)-itemsets中的项进行组合,产生备选集(Candidate itemsets)。再从备选集中,将不符合最小支持度或置信度的项删去。例如
Frequent 2-itemsets | Candidate 3-itemsets | Frqquent 3-itemsets | ||
I1,I2 | ==> | I1,I2,I4 | ==> | I1,I2,I4 |
I1,I4 | I2,I3,I4 | |||
I2,I3 | ||||
I2,I4 |
下面我们再来看一个详细的例子。
设最小支持度为2,以Ck表示k-itemsets备选集,以Lk表示k-itemsets频繁集。
ID | Items | Itemset | Sup. count | Itemset | Itemset | |||
100 | I1,I2,I5 | I1 | 6 | I1 | I1,I2 | |||
200 | I2,I4 | ==>C1: | I2 | 7 | ==>L1: | I2 | ==>C2 | I1,I3 |
300 | I2,I3 | I3 | 6 | I3 | I1,I4 | |||
400 | I1,I2,I4 | I4 | 2 | I4 | I1,I5 | |||
500 | I1,I3 | I5 | 2 | I5 | I2,I3 | |||
600 | I2,I3 | I2,I4 | ||||||
700 | I1,I3 | I2,I5 | ||||||
800 | I1,I2,I3,I5 | I3,I4 | ||||||
900 | I1,I2,I3 | I3,I5 | ||||||
I4,I5 |
对C2进行扫描,计算支持度。
Itemset | Sup. count | Itemset | Itemset | Sup. count | Itemset | |||
I1,I2 | 4 | ==> L2: | I1,I2 | ==> C3 | I1,I2,I3 | 2 | ==> L3: | I1,I2,I3 |
I1,I3 | 4 | I1,I3 | I1,I2,I5 | 2 | I1,I2,I5 | |||
I1,I4 | 1 | I1,I5 | ||||||
I1,I5 | 2 | I2,I3 | ||||||
I2,I3 | 4 | I2,I4 | ||||||
I2,I4 | 2 | I2,I5 | ||||||
I2,I5 | 2 | |||||||
I3,I4 | 0 | |||||||
I3,I5 | 1 | |||||||
I4,I5 | 0 |
对于频繁集中的每一项k-itemset,可以产生非空子集,对每一个子集,可以得到满足最小置信度的规则了。例如考虑{I1,I2,I5}。其子集有{I1,I2}, {I1,I5}, {I2,I5}, {I1}, {I2}, {I5}。可以产生规则,{I1,I2} => {I5} (50%), {I1,I5} =>{I2} (100%), {I2,I5} =>{I1} (100%),{I1} => {I2,I5} (33%), {I2} =>{I1,I5} (29%), {I5} =>{I1,I2} (100%)。
也不是每个数据集都有产生强规则。例如"Thinkpad notebook" 和"Canon printer"一起可能很难产生有效规则。因为恰好一起买这两个牌子的产品的顾客太少。但不妨将Thinkpad notebook放到Notebook这一层次上考虑,而Canon printer放到printer这一去层次上考虑。这样的话,一起买notebook和printer的顾客就较多了。也即Multilevel association rules。
图1所示为Apriori算法挖掘频繁集的过程,其中最小支持度为20%。
![关联规则算法Apriori的学习与实现 关联规则算法Apriori的学习与实现](http://www.zclw.net/article/UploadPic/2010-5/20105891548812.jpg)
另外,有一个辅助类ProperSubsetCombination用于计算一个频繁项集的真子集,采用组合原理,基于数值编码原理实现的组合求解集合的真子集。
算法实现
(一)核心类
Apriori算法的核心实现类为AprioriAlgorithm,实现的Java代码如下所示:
package org.shirdrn.datamining.association;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class AprioriAlgorithm {
private Map<Integer, Set<String>> txDatabase; // 事务数据库
private Float minSup; // 最小支持度
private Float minConf; // 最小置信度
private Integer txDatabaseCount; // 事务数据库中的事务数
private Map<Integer, Set<Set<String>>> freqItemSet; // 频繁项集集合
private Map<Set<String>, Set<Set<String>>> assiciationRules; // 频繁关联规则集合
public AprioriAlgorithm(Map<Integer, Set<String>> txDatabase,Float minSup,Float minConf) {
this.txDatabase = txDatabase;
this.minSup = minSup;
this.minConf = minConf;
this.txDatabaseCount = this.txDatabase.size();
freqItemSet = new TreeMap<Integer, Set<Set<String>>>();
assiciationRules = new HashMap<Set<String>, Set<Set<String>>>();
}
public Map<Set<String>, Float> getFreq1ItemSet() {
Map<Set<String>, Float> freq1ItemSetMap = new HashMap<Set<String>, Float>();
Map<Set<String>, Integer> candFreq1ItemSet = this.getCandFreq1ItemSet();
Iterator<Map.Entry<Set<String>, Integer>> it = candFreq1ItemSet.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<Set<String>, Integer> entry = it.next();
// 计算支持度
Float supported = new Float(entry.getValue().toString())/new Float(txDatabaseCount);
if(supported>=minSup) {
freq1ItemSetMap.put(entry.getKey(), supported);
}
}
return freq1ItemSetMap;
}
public Map<Set<String>, Integer> getCandFreq1ItemSet() {
Map<Set<String>, Integer> candFreq1ItemSetMap = new HashMap<Set<String>, Integer>();
Iterator<Map.Entry<Integer, Set<String>>> it = txDatabase.entrySet().iterator();
// 统计支持数,生成候选频繁1-项集
while(it.hasNext()) {
Map.Entry<Integer, Set<String>> entry = it.next();
Set<String> itemSet = entry.getValue();
for(String item : itemSet) {
Set<String> key = new HashSet<String>();
key.add(item.trim());
if(!candFreq1ItemSetMap.containsKey(key)) {