概述
首先,找出频繁 1- 项集的集合。该集合记作L1。L1 用于找频繁2- 项集的集合 L2,而L2 用于找L2,如此下去,直到不能找到 k- 项集。每找一个 Lk 需要一次数据库扫描。为提高频繁项集逐层产生的效率,一种称作Apriori 性质的重 要性质 用于压缩搜索空间。其运行定理在于一是频繁项集的所有非空子集都必须也是频繁的,二是非频繁项集的所有父集都是非频繁的。步骤
1.扫描数据库,对每个候选项进行计数,并得到支持数
2.比较候选项支持度计数与最小支持度minSupport,产生1维最大项目集L1
连接步
若有两个k-1项集,每个项集按照“属性-值”(一般按值)的字母顺序进行排序。如果两个k-1项集>的前k-2个项相同,而最后一个项不同,则证明它们是可连接的,即这个k-1项集可以联姻,即可连接生成k
项集。使如有两个3项集:{a,
b, c}{a, b, d},这两个3项集就是可连接的,它们可以连接生成4项集{a, b, c, d}。又如两个3项集{a, b,
c}{a, d, e},这两个3项集显示是不能连接生成3项集的。
3.由L1产生候选项
剪枝步 若一个项集的子集不是频繁项集,则该项集肯定也不是频繁项集。这个很好理解,举一个例子,若存在3项集{a, b,
c},如果它的2项子集{a, b}的支持度即同时出现的次数达不到阈值,则{a, b,
c}同时出现的次数显然也是达不到阈值的。因此,若存在一个项集的子集不是频繁项集,那么该项集就应该被无情的舍弃
4.扫描D,对每个候选项集进行支持度计数
5.比较候选项支持度计数与最小支持度minSupport,产生2维最大项目集L2
6.由L2产生候选项集C3
7.比较候选项支持度计数与最小支持度minSupport,产生3维最大项目集L3
8.从频繁集中挖掘相关规则
一条规则P➞H的可信度定义为support(P | H)/support(P),其中“|”表示P和H的并集。可见可信度的计算是基于项集的支持度的。
下图给出了从项集{0,1,2,3}产生的所有关联规则,其中阴影区域给出的是低可信度的规则。可以发现如果{0,1,2}➞{3}是一条低可信度规则,那么所有其他以3作为后件(箭头右部包含3)的规则均为低可信度的。
可以观察到,如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求。以下图为例,假设规则{0,1,2} ➞ {3}并不满足最小可信度要求,那么就知道任何左部为{0,1,2}子集的规则也不会满足最小可信度要求。可以利用关联规则的上述性质属性来减少需要测试的规则数目,类似于Apriori算法求解频繁项集。
Java代码实现
package testApriori;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.runner.manipulation.Sortable;
public class Apriori {
private final static int SUPPORT = 2; // 支持度阈值
private final static double CONFIDENCE = 0.5; // 置信度阈值
private final static String ITEM_SPLIT = ";"; // 项之间的分隔符
private final static String CON = "->"; // 项之间的分隔符
private final static List<String> transList = new ArrayList<String>(); // 所有交易
static {// 初始化交易记录
transList.add("1;2;5;");
transList.add("2;4;");
transList.add("2;3;");
transList.add("1;2;4;");
transList.add("1;3;");
transList.add("2;3;");
transList.add("1;3;");
transList.add("1;2;3;5;");
transList.add("1;2;3;");
}
public Map<String, Integer> getFC() {
Map<String, Integer> frequentCollectionMap = new HashMap<String, Integer>();// 所有的频繁集
frequentCollectionMap.putAll(getItem1FC());
Map<String, Integer> itemkFcMap = new HashMap<String, Integer>();
itemkFcMap.putAll(getItem1FC());
while (itemkFcMap != null && itemkFcMap.size() != 0) {
Map<String, Integer> candidateCollection = getCandidateCollection(itemkFcMap);
Set<String> ccKeySet = candidateCollection.keySet();
// 对候选集项进行累加计数
for (String trans : transList) {
for (String candidate : ccKeySet) {
boolean flag = true;// 用来判断交易中是否出现该候选项,如果出现,计数加1
String[] candidateItems = candidate.split(ITEM_SPLIT);
for (String candidateItem : candidateItems) {
if (trans.indexOf(candidateItem + ITEM_SPLIT) == -1) {
flag = false;
break;
}
}
if (flag) {
Integer count = candidateCollection.get(candidate);
candidateCollection.put(candidate, count + 1);
}
}
}
// 从候选集中找到符合支持度的频繁集项
itemkFcMap.clear();
for (String candidate : ccKeySet) {
if (candidateCollection.get(candidate) >= SUPPORT) {
itemkFcMap.put(candidate, candidateCollection.get(candidate));
}
}
// 合并所有频繁集
frequentCollectionMap.putAll(itemkFcMap);
}
return frequentCollectionMap;
}
private Map<String, Integer> getCandidateCollection(
Map<String, Integer> itemkFcMap) {
Map<String, Integer> candidateCollection = new HashMap<String, Integer>();
Set<String> itemkSet1 = itemkFcMap.keySet();
Set<String> itemkSet2 = itemkFcMap.keySet();
for (String itemk1 : itemkSet1) {
for (String itemk2 : itemkSet2) {
// 进行连接
String[] tmp1 = itemk1.split(ITEM_SPLIT);
String[] tmp2 = itemk2.split(ITEM_SPLIT);
String c = "";
if (tmp1.length == 1) {
if (tmp1[0].compareTo(tmp2[0]) < 0) {
c = tmp1[0] + ITEM_SPLIT + tmp2[0] + ITEM_SPLIT;
}
} else {
boolean flag = true;
for (int i = 0; i < tmp1.length - 1; i++) {
if (!tmp1[i].equals(tmp2[i])) {
flag = false;
break;
}
}
if (flag
&& (tmp1[tmp1.length - 1]
.compareTo(tmp2[tmp2.length - 1]) < 0)) {
c = itemk1 + tmp2[tmp2.length - 1] + ITEM_SPLIT;
}
}
// 进行剪枝
boolean hasInfrequentSubSet = false;
if (!c.equals("")) {
String[] tmpC = c.split(ITEM_SPLIT);
for (int i = 0; i < tmpC.length; i++) {
String subC = "";
for (int j = 0; j < tmpC.length; j++) {
if (i != j) {
subC = subC + tmpC[j] + ITEM_SPLIT;
}
}
if (itemkFcMap.get(subC) == null) {
hasInfrequentSubSet = true;
break;
}
}
} else {
hasInfrequentSubSet = true;
}
if (!hasInfrequentSubSet) {
candidateCollection.put(c, 0);
}
}
}
return candidateCollection;
}
private Map<String, Integer> getItem1FC() {
Map<String, Integer> sItem1FcMap = new HashMap<String, Integer>();
Map<String, Integer> rItem1FcMap = new HashMap<String, Integer>();// 频繁1项集
for (String trans : transList) {
String[] items = trans.split(ITEM_SPLIT);
for (String item : items) {
Integer count = sItem1FcMap.get(item + ITEM_SPLIT);
if (count == null) {
sItem1FcMap.put(item + ITEM_SPLIT, 1);
} else {
sItem1FcMap.put(item + ITEM_SPLIT, count + 1);
}
}
}
Set<String> keySet = sItem1FcMap.keySet();
for (String key : keySet) {
Integer count = sItem1FcMap.get(key);
if (count >= SUPPORT) {
rItem1FcMap.put(key, count);
}
}
return rItem1FcMap;
}
public Map<String, Double> getRelationRules(
Map<String, Integer> frequentCollectionMap) {
Map<String, Double> relationRules = new HashMap<String, Double>();
Set<String> keySet = frequentCollectionMap.keySet();
for (String key : keySet) {
double countAll = frequentCollectionMap.get(key);
String[] keyItems = key.split(ITEM_SPLIT);
if (keyItems.length > 1) {
List<String> source = new ArrayList<String>();
Collections.addAll(source, keyItems);
List<List<String>> result = new ArrayList<List<String>>();
buildSubSet(source, result);// 获得source的所有非空子集
System.out.println("----------\n"+source.toString()+"\n"+result.toString()+"\n-----------------\n");
for (List<String> resultItemList : result) {
if (resultItemList.size() < source.size()) {// 只处理真子集
List<String> otherList = new ArrayList<String>();
for (String sourceItem : source) {
if (!resultItemList.contains(sourceItem)) {
otherList.add(sourceItem);
}
}
String reasonStr = "";// 前置
String resultStr = "";// 结果
for (String item : resultItemList) {
reasonStr = reasonStr + item + ITEM_SPLIT;
}
for (String item : otherList) {
resultStr = resultStr + item + ITEM_SPLIT;
}
double countReason = frequentCollectionMap
.get(reasonStr);
double itemConfidence = countAll / countReason;// 计算置信度
if (itemConfidence >= CONFIDENCE) {
String rule = reasonStr + CON + resultStr;
relationRules.put(rule, itemConfidence);
}
}
}
}
}
return relationRules;
}
private void buildSubSet(List<String> sourceSet, List<List<String>> result) {
// 仅有一个元素时,递归终止。此时非空子集仅为其自身,所以直接添加到result中
if (sourceSet.size() == 1) {
List<String> set = new ArrayList<String>();
set.add(sourceSet.get(0));
result.add(set);
} else if (sourceSet.size() > 1) {
// 当有n个元素时,递归求出前n-1个子集,在于result中
buildSubSet(sourceSet.subList(0, sourceSet.size() - 1), result);
int size = result.size();// 求出此时result的长度,用于后面的追加第n个元素时计数
// 把第n个元素加入到集合中
List<String> single = new ArrayList<String>();
single.add(sourceSet.get(sourceSet.size() - 1));
result.add(single);
// 在保留前面的n-1子集的情况下,把第n个元素分别加到前n个子集中,并把新的集加入到result中;
// 为保留原有n-1的子集,所以需要先对其进行复制
List<String> clone;
for (int i = 0; i < size; i++) {
clone = new ArrayList<String>();
for (String str : result.get(i)) {
clone.add(str);
}
clone.add(sourceSet.get(sourceSet.size() - 1));
result.add(clone);
}
}
}
public static void main(String[] args) {
Apriori apriori = new Apriori();
Map<String, Integer> frequentCollectionMap = apriori.getFC();
System.out.println("----------------频繁集" + "----------------");
Set<String> fcKeySet = frequentCollectionMap.keySet();
for (String fcKey : fcKeySet) {
System.out.println(fcKey + " : "
+ frequentCollectionMap.get(fcKey));
}
Map<String, Double> relationRulesMap = apriori
.getRelationRules(frequentCollectionMap);
System.out.println("----------------关联规则" + "----------------");
Set<String> rrKeySet = relationRulesMap.keySet();
for (String rrKey : rrKeySet) {
System.out.println(rrKey + " : " + relationRulesMap.get(rrKey));
}
}
}