关联规则挖掘Apriori算法的实现

253 阅读5分钟

关联规则挖掘Apriori算法的实现

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。

关于作者

  • 作者介绍

🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆、阿里云专家博主51CTO专家博主
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻


1.根据给定的事务数据库,支持数阈值2和置信度阈值0.7,编写代码生成频繁项目集及对应的关联规则。

第一部分:根据如下给定的事务数据库,支持数阈值2和置信度阈值0.7,编写代码生成频繁项目集及对应的关联规则。 新建Apriori.java

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Apriori {
	private final static int SUPPORT = 2;
	private final static String ITEM_SPLIT = ";";
	private final static String CON = "->";
	private final static double CONFIDENCE = 0.7;
	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;// 用来判断交易中是否出现该候选项
					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) {
				Integer count = candidateCollection.get(candidate);
				if (count >= SUPPORT) {
					itemkFcMap.put(candidate, count);
				}
			}
			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>();// 存放候选1项集
		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的所有非空子集
				for (List<String> itemList : result) {
					if (itemList.size() < source.size()) {// 只处理真子集
						List<String> otherList = new ArrayList<String>();
						for (String sourceItem : source) {
							if (!itemList.contains(sourceItem)) {
								otherList.add(sourceItem);
							}
						}
						String reasonStr = "";// 前置
						String resultStr = "";// 结果
						for (String item : itemList) {
							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));
		}
	}
}

2.利用weka工具对天气数据、美国国会议员投票信息、超市购物篮数据进行关联规则挖掘,并分析挖掘结果

第二部分:利用weka工具对天气数据、美国国会议员投票信息、超市购物篮数据进行关联规则挖掘,并分析挖掘结果。

1.加载 weather.nomianl.arff 数据,切换至 Associate 标签页,依次选择 choose->Apriori 算法,如下如所示:

点击 Choose 右边的文本框,弹出 Apriori 的设置参数:

参数设置好,点击 start,运行 Apriori 算法,结果如下

参数对应意思:
=== Run information ===	//运行信息
Scheme(执行的方案):   weka.associations.Apriori -N 10 -T 0 -C 0.9 -D 0.05 -U 1.0 -M 0.1 -S -1.0 -c -1 
Relation(关系):     weather.symbolic
Instances(实例):    14
Attributes(属性):   5
              Outlook、temperature、humidity、windy、play
=== Associator model (full training set) ===//关联器模型(完整训练集)
Apriori算法
Minimum support: 0.15 (2 instances)	//最小支持度0.15
Minimum metric <confidence>: 0.9	//最小置信度: 0.9
Number of cycles performed: 17	//执行的循环数:17
Generated sets of large itemsets:	//生成的频繁项集
Size of set of large itemsets L(1): 12	//项集1:12
Size of set of large itemsets L(2): 47	//项集2:47
Size of set of large itemsets L(3): 39	//项集3:39
Size of set of large itemsets L(4): 6	////项集4:6

设置参数 outputItemSets 为 true,再次运行 Apriori 算法,会生成各频繁项目集及他们的支 持数如下:

  1. 加载 vote.arff 数据集,该数据集中各属性含义如下:

3.加载 supermarket.arff 数据集,运行 Apriori 算法,结果如下:

第一条规则:饼干+冷冻食品+水果+高总额==>面包和蛋糕

第二条规则:烘烤所需+饼干+水果+高总额==>面包和蛋糕

第三条规则:烘烤所需+冷冻食品+水果+高总额==>面包和蛋糕

第四条规则:饼干+水果+蔬菜+高总额==>面包和蛋糕

第五条规则:聚会零食+水果+高总额==>面包和蛋糕

第六条规则:饼干+冷冻食品+蔬菜+高总额==>面包和蛋糕

第七条规则:烘烤所需+饼干+蔬菜+高总额==> 面包和蛋糕

第八条规则:饼干+水果+高总额==>面包和蛋糕

第九条规则:冷冻食品+水果+蔬菜+高总额===>面包和蛋糕

第十条规则:冷冻食品+水果+高总额:==>面包和蛋糕

发现十条关联规则中,可以看出一些信息:

1、 购买饼干、冷冻食品等速食,会采购些水果、蔬菜

2、 购买饼干、冷冻食品以及水果、蔬菜,会购买面包和蛋糕

3、 购买上述食品,单次采购量大,总金额高

4、 总金额高的交易,一般都会购买面包和蛋糕