Java实现Apriori算法,挖掘关联规则

实现过程中参考了这位博主的方法,大家可以去看下这个算法的原理,在这里就不过多阐述。不过做了点小小修改。

  • 最小置信度计算如下。
    最小置信度计算如下:
    minconf(A→B) = minsup(AB) / minsup(A);
  • 在计算最小支持度时,最好不要存事务出现的概率,存取事务出现的次数为最佳;如果存概率的话,最好用事务出现的总次数除以事务数据库里面的事务总数(个人建议),因为java里面浮点型存取的是一个近似。这里我刚好碰到了这个问题,最开始我存的是每个事务的概率,然后相加,然而在与最小支持度比较的时候就出问题了。通过一个例子来说明:
public class MyTest {
	static double d = 0;
	static float s;
	static int m;
	
	public static void main(String[] args) {
		s = 12;
		m = 12;
		for(int i = 1 ; i <= 6 ;i++) {
			d += 1.0 / m;
			System.out.println(d);
		}
		System.out.println(6 / s);
	}
}

以上代码运行结果:

这就出问题了:
通过计算会发现,6个1/12相加最终结果为0.4999999……,而6/12等于0.5,两者不相等。大家可以下去试一下,说不定以后会遇到这个情况,所以我上面才给了大家建议。接下来进入正题。

事务数据库是从文件里面读取的,文件的内容如下:
在这里插入图片描述
运行效果:
最小支持度:4
最小置信度:0.7
在这里插入图片描述
代码如下:

package a1;

import java.util.*;
import java.io.*;
public class DataDeal {
	static double min_support = 4;
	static double min_confident = 0.7;
	//文件路径,我的是在当前项目下,大家换成自己的文件路径就是了
	static String filePath = System.getProperty("user.dir") + "\\src\\a1\\data.txt";
	static ArrayList<ArrayList<String>> D = new ArrayList<ArrayList<String>>();//事务数据库D
	static HashMap<ArrayList<String>, Integer> C = new HashMap<ArrayList<String>, Integer>();//项目集C
	static HashMap<ArrayList<String>, Integer> L = new HashMap<ArrayList<String>, Integer>();//候选集L
	// 用于存取候选集每次计算结果(即存放所有的频繁项集L),最后计算关联规则,就不用再次遍历事务数据库。
	static HashMap<ArrayList<String>, Integer> L_ALL = new HashMap<ArrayList<String>, Integer>();
	//从文件中读取内容,返回事务集
	public static ArrayList<ArrayList<String>> readTable(String filePath){
		ArrayList<ArrayList<String>> t = new ArrayList<ArrayList<String>>();
		ArrayList<String> t1 = null;
		File file = new File(filePath);
		try {
			InputStreamReader isr = new InputStreamReader(new FileInputStream(file));
			BufferedReader bf = new BufferedReader(isr);
			String str = null;
			while((str = bf.readLine()) != null) {
				t1 = new ArrayList<String>();
				String[] str1 = str.split(",");
				for(int i = 1; i < str1.length ; i++) {
					t1.add(str1[i]);
				}
				t.add(t1);
			}
			bf.close();
			isr.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("文件不存在!");
		}
		System.out.println("\nD:"+t);
		return t;
	}
	//剪枝步:从候选集C中删除小于最小支持度的,并放入频繁集L中
	public static void pruning(HashMap<ArrayList<String>, Integer> C,HashMap<ArrayList<String>, Integer> L) {
		L.clear();
		// 根据项目集生成候选集
		L.putAll(C);
		// 删除少于最小支持度的元素
		ArrayList<ArrayList<String>> delete_key = new ArrayList<ArrayList<String>>();
		for (ArrayList<String> key : L.keySet()) {
			if (L.get(key) < min_support) {
				delete_key.add(key);
			}
		}
		for (int i = 0; i < delete_key.size(); i++) {
			L.remove(delete_key.get(i));
		}
	}
 
	/**
	 * 初始化事务数据库、项目集、候选集
	 */
	public static void init() {
		//将文件中的数据放入集合D中
		D = readTable(filePath);
		// 扫描事务数据库。生成项目集,支持度=该元素在事务数据库出现的次数/事务数据库的事务数
		for (int i = 0; i < D.size(); i++) {
			for (int j = 0; j < D.get(i).size(); j++) {
				String[] e = { D.get(i).get(j) };
				//将数组e转化为List
				ArrayList<String> item = new ArrayList<String>(Arrays.asList(e));
				//map中是否包含指定的键
				if (!C.containsKey(item)) {
					C.put(item, 1);
					//System.out.println(C.get(item));
				} else {
					C.put(item, C.get(item) + 1);
					//System.out.println(C.get(item));
				}
			}
		}
		//System.out.println("D.size= "+D.size());
		pruning(C, L);// 剪枝
		//将频繁项集放入集合中
		L_ALL.putAll(L);
	}
 
	// 两个整数集求并集
	public static ArrayList<String> arrayListUnion(ArrayList<String> arraylist1, ArrayList<String> arraylist2) {
		ArrayList<String> arraylist = new ArrayList<String>();
		arraylist.addAll(arraylist1);
		arraylist.addAll(arraylist2);
		//将ArrayList转化为HashSet去掉重复元素,再将HashSet转换为ArrayList
		arraylist = new ArrayList<String>(new HashSet<String>(arraylist));
		return arraylist;
	}
 
	/**
	 * 迭代求出最终的候选频繁集
	 * 
	 * @param C
	 *            完成初始化的项目集
	 * @param L
	 *            完成初始化的候选集
	 * @return 最终的候选频繁集
	 */
	public static HashMap<ArrayList<String>, Integer> iteration(HashMap<ArrayList<String>, Integer> C,HashMap<ArrayList<String>, Integer> L) {
		HashMap<ArrayList<String>, Integer> L_temp = new HashMap<ArrayList<String>, Integer>();// 用于判断是否结束剪枝的临时变量
		String str = null;
		int t = 1;// 迭代次数
		while (L.size() > 0) {// 一旦被剪枝后剪干净,剪枝之前则是最终要求的结果。
			t++;
			L_temp.clear();
			L_temp.putAll(L);
			// 一、连接步
			C.clear();
			// 1.将L中的项以一定的规则两两匹配
			ArrayList<ArrayList<String>> L_key = new ArrayList<ArrayList<String>>(L.keySet());
			for (int i = 0; i < L_key.size(); i++) {
				for (int j = i + 1; j < L_key.size(); j++) {
					ArrayList<String> C_item = new ArrayList<String>();
					C_item = new ArrayList<String>(arrayListUnion(L_key.get(i),
							L_key.get(j)));
					if (C_item.size() == t) {
						C.put(C_item, 0);// 频繁项集的所有非空子集都必须是频繁的
					}
				}
			}
			// 2.通过扫描D,计算此项的支持度
			for (ArrayList<String> key : C.keySet()) {
				for (int i = 0; i < D.size(); i++) {
					if (D.get(i).containsAll(key)) {
						C.put(key, C.get(key) + 1 );
					}
				}
			}
			str = C.toString();
			
			 System.out.println("候选"+t+"项集:C: \n"+C);
			// 二、剪枝步
			pruning(C, L);
			 System.out.println("频繁"+t+"项集:L: \n"+L+"\n");
			 str = L.toString();
			 
			 //System.out.println("===");
			L_ALL.putAll(L);
		}
		return L_temp;
	}
 
	// 求一个集合的所有子集
	public static ArrayList<ArrayList<String>> getSubset(ArrayList<String> L) {
		if (L.size() > 0) {
			ArrayList<ArrayList<String>> result = new ArrayList<ArrayList<String>>();
			for (int i = 0; i < Math.pow(2, L.size()); i++) {// 集合子集个数=2的该集合长度的乘方
				ArrayList<String> subSet = new ArrayList<String>();
				int index = i;// 索引从0一直到2的集合长度的乘方-1
				for (int j = 0; j < L.size(); j++) {
					// 通过逐一位移,判断索引那一位是1,如果是,再添加此项
					if ((index & 1) == 1) {// 位与运算,判断最后一位是否为1
						subSet.add(L.get(j));
					}
					index >>= 1;// 索引右移一位
				}
				result.add(subSet); // 把子集存储起来
			}
			return result;
		} else {
			return null;
		}
	}
 
	// 判断两个集合相交是否为空
	public static boolean intersectionIsNull(ArrayList<String> l1,
			ArrayList<String> l2) {
		Set<String> s1 = new HashSet<String>(l1);
		Set<String> s2 = new HashSet<String>(l2);
 
		s1.retainAll(s2);
		if (s1.size() > 0) {
			return false;
		} else {
			return true;
		}
	}
 
	/**
	 * 根据最终的关联集,根据公式计算出各个关联事件
	 */
	public static void connection() {
		for (ArrayList<String> key : L_ALL.keySet()) {// 对最终的关联集各个事件进行判断
			ArrayList<ArrayList<String>> key_allSubset = getSubset(key);
			//得到所有频繁集中每个集合的子集
			// System.out.println(key_allSubset);
			for (int i = 0; i < key_allSubset.size(); i++) {
				ArrayList<String> item_pre = key_allSubset.get(i);//得到一个真子集
				if (0 < item_pre.size() && item_pre.size() < key.size()) {// 判断是否是非空真子集
					// 各个非空互补真子集之间形成关联事件
					double item_pre_support = L_ALL.get(item_pre);//得到真子集的支持度度
					//System.out.println("itempre="+item_pre_support);
					for (int j = 0; j < key_allSubset.size(); j++) {
						ArrayList<String> item_post = key_allSubset.get(j);
						if (0 < item_post.size()
								&& item_post.size() < key.size()
								&& arrayListUnion(item_pre, item_post).equals(key)
								&& intersectionIsNull(item_pre, item_post))
						//不相交的两个非空真子集,相并为频繁项集
						{
							double d = L_ALL.get(arrayListUnion(item_pre, item_post));
							//double item_post_support = L_ALL.get(item_post);// 互补真子集的支持度比则是事件的置信度
							//System.out.println("item_post="+item_post_support);
							double confident = d
									/ item_pre_support; // 事件的置信度
							if (confident > min_confident) {// 如果事件的置信度大于最小置信度
								System.out.print(item_pre + "==>" + item_post );// 则是一个关联事件
								System.out.println("==>" + confident);
							}
						}
 
					}
				}
			}
		}
	}
	
	public static void main(String[] args) {
		DataDeal apriori = new DataDeal();
        apriori.init();
        System.out.println("候选1项集:C:\n"+apriori.C); 
        System.out.println("频繁1项集:L:\n"+apriori.L+"\n");
        apriori.L = apriori.iteration(apriori.C, apriori.L);
        System.out.println("关联规则如下:");
        apriori.connection();
	}
}

转载地址:https://blog.csdn.net/yongh701/article/details/53640085

  • 8
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值