Apriori算法Java实现

  1. 关联算法的相关定义:
    1. 频繁项集:经常出现在一块的物品集合
    2. 关联规则:两种物品间可能存在很强的关系
    3. support(支持度):一个项集的数据集中包含研究的项集记录所占的比例
    4. confidence(置信度):针对某一条关联规则({尿布--》啤酒})定义,置信度={尿布,啤酒}的支持度/尿布的支持度
  2. Apriori算法原理:某个项集是频繁的,那么它的所有子集势必也是频繁的。反过来如果一个项集是非频繁项集,那么它所对应的超集就都是非频繁项集。

2.2频繁项集

Apriori是一个发现频繁项集的方法,输入参数是最小支持度和数据集。

  1. 首先生成所有单个物品的项集列表,遍历以后去掉不满足最小支持度的项集;
  2. 接下来对剩下的集合组合,生成包含两个元素的项集,去掉不满足最小支持度的项集。
  3. 重复过程直到去掉不满足最小支持度的项集。

2.3 找关联规则:

假设存在一条频繁项集,{A,B},它们之间就可能存在一条关联规则,即可表示为“……-->……”,但是反过来不一定成立。

最小support衡量频繁项集,可信度衡量关联规则。

先生成一个可能的规则表,测试每条规则的可信度,结合可信度最小要求,得到关联规则,同寻找频繁项集类似,我们可以为每个频繁项集产生许多关联规则,这样就会有很多的关联规则产生。结合Apriori原理,如果某条规则不满足最小可信度要求,那么该规则的所有子集也就不满足最小可信度要求。

实现:

1.频繁项集:

package com.cl.apriori;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Apriori {

	private double support;
	private double confidence;
	private final String ITEMSPLITE=";"; 
	
	public Apriori(){
		support=0.2;
		confidence=0.7;
		
	}
	public Apriori(double support, double confidence) {
		super();
		this.support = support;
		this.confidence = confidence;
	}
	
	/**
	 * 从src处读取所有数据,将一条条记录保存在list里
	 * @param src
	 * @return
	 * @throws IOException
	 */
	public List<String> readDataFile(String src) throws IOException{
		List recordList=new ArrayList<String>();
		InputStreamReader in=new InputStreamReader(new FileInputStream(src),"UTF-8");
		BufferedReader bin=new BufferedReader(in);

		String record="";
		while((record=bin.readLine())!=null){
			recordList.add(record);
		}
		return recordList;
	}
	/**
	 * 对每一条记录中出现的一个item统计其出现的次数,得到频繁项1的集合
	 * @param recordList
	 * @return
	 */
	public Map<String,Integer> oneFrequentSet(List<String> recordList){
		Map<String,Integer> freOne=new HashMap<String,Integer>();
		if(recordList!=null&&recordList.size()>0){
			for(String record:recordList){
				String items[]=record.split(ITEMSPLITE);
				for(String item:items){
					item+=ITEMSPLITE;
					if(freOne.get(item)==null){
						freOne.put(item, 1);
					}else{
						freOne.put(item, freOne.get(item)+1);
					}
				}
			}
		}
		return freOne;
	}
	
	/**
	 * 对项集的支持度检查,不满足支持度的剔除
	 * @param rawItemSets
	 * @param size
	 */
	public Map<String,Integer> getSupportedItemSets(Map<String,Integer> rawItemSets,int size){
		Map<String,Integer> supportedItemSets=new HashMap<String,Integer>();
		for(Entry<String,Integer> entry:rawItemSets.entrySet()){
			if(entry.getValue()*1.0>support*size){
				supportedItemSets.put(entry.getKey(),entry.getValue());
			}
		}
		if(supportedItemSets.size()==0){
			System.out.println("没有满足支持度="+support+"的频繁项集");
			return null;
		}
		return supportedItemSets;
	}
	
	/**
	 * 由k-1项构成的频繁项集构造k项频繁项集,组合
	 * @param frequentSets
	 * @return
	 */
	public Map<String,Integer> getNextCandidate(Map<String,Integer> frequentSets){
		Map<String,Integer> candidateFrequentSets=new HashMap<String,Integer>();
		List<String> freSets1=mapKeyToList(frequentSets);
		List<String> freSets2=mapKeyToList(frequentSets);
		//System.out.println(freSets1+"\n"+freSets2);
		for(int i=0;i<freSets1.size();i++){
			String record1=freSets1.get(i);
		//	System.out.println("record1:  "+record1);
			for(int j=i+1;j<freSets2.size();j++){
				String record2=freSets2.get(j);
			//	System.out.println("record2:  "+record2);
				String m=merge(record1,record2);
			//	System.out.println("m:  "+m);
				if(!containsFreuentSet(candidateFrequentSets,m)){
					candidateFrequentSets.put(m,0);
				};
			}
		}
		return candidateFrequentSets;
	}
	
	/**
	 * 判断频繁项集freSets里是否完全含有一个给定的项集m
	 * @param freSets
	 * @param m
	 * @return
	 */
	public boolean containsFreuentSet(List<String> freSets, String m) {
		
		boolean flag=false;
		for(String record:freSets){
			if(record.length()>=m.length()){
				String[] items=m.split(ITEMSPLITE);
				for(int i=0;i<items.length;i++){
					if(record.indexOf(items[i])==-1){
						flag=false;
						break;		
					}else{
						flag=true;
					}
				}
				if(flag==true){
					break;
				}
			}
		}
		return flag;
	}

	/**
	 * 判断频繁项集freSets里是否含有一个给定的项集m
	 * @param map
	 * @param m
	 * @return
	 */
	public boolean containsFreuentSet(Map<String,Integer> map, String m) {
		List<String> list=mapKeyToList(map);
		return containsFreuentSet(list, m);
	}
	/**
	 * 将map表示的频繁项集用List表示,便于找到下标处理
	 * @param map
	 * @return
	 */
	public List<String> mapKeyToList(Map<String, Integer> map) {
		List<String> list=new ArrayList<String>();
		if(map.size()!=0){
			Set<String> set=map.keySet();
			for(String s:set){
				list.add(s);
			}
		}
		return list;
	}
	
	/**
	 * 对两个项集record1和record2进行合并,去除其中重复的item项目
	 * @param record1
	 * @param record2
	 * @return
	 */
	public String merge(String record1, String record2) {
		String items2[]=record2.split(ITEMSPLITE);
		String record=record1;
		for(int i=0;i<=items2.length-1;i++){
			String item=items2[i];
			if(record1.indexOf(item)==-1){
				record+=item+ITEMSPLITE;
			}
		}
		return record;
	}
	
	/**
	 * 统计组合的频繁项集的支持度
	 * @param map
	 * @param data
	 */
	public void countCandidateFreqSet(Map<String, Integer> map,List<String> data){
		int times;
		for(Entry<String,Integer> entry:map.entrySet()){
			String str=entry.getKey();
			times=count(data,str);
			entry.setValue(times);
		}
		
	}
	
	/**
	 * 求初始data里项集m的出现的次数,即m的支持度
	 * @param data
	 * @param m
	 * @return
	 */
	public int count(List<String> data, String m) {	
		boolean flag;
		int count=0;
		for(String record:data){
			flag=true;
			if(record.length()>=m.length()){
				String items[]=m.split(ITEMSPLITE);
				for(int i=0;i<items.length;i++){
					if(record.indexOf(items[i])==-1){
						flag=false;
						break;
					}
				}
				if(flag==true){
					count++;
				}
			}
		}
		return count;
	}

	public Map<String,Integer> apriori(List<String> srcData) throws IOException{
		Map<String,Integer> freSets=new HashMap<String,Integer>();
		Map<String,Integer> candidateFreSets=new HashMap<String,Integer>();
		
		int len=srcData.size();
		
		Map<String,Integer> oneFreSets=oneFrequentSet(srcData);//获取项集为1的集合
		/*System.out.println("--------------------初始频繁1项项集-----------------------------");
		for(Entry<String, Integer> entry:oneFreSets.entrySet()){
			System.out.println(entry.getKey()+":"+entry.getValue());
		}*/
		
		oneFreSets=getSupportedItemSets(oneFreSets, len);//第一次进行清理不符合支持度的项集
		/*System.out.println("--------------------初始频繁1项项集支持度检查-----------------------------");
		for(Entry<String, Integer> entry:oneFreSets.entrySet()){
			System.out.println(entry.getKey()+":"+entry.getValue());
		}*/
		int i=1;
		while(oneFreSets!=null&&oneFreSets.size()>0){
			freSets=union(freSets,oneFreSets);          //合并上一次所求满足支持度的项集,会不会重复?
			
			//由k项项集找k+1项项集
		/*	System.out.println("--------------------初始频繁项集第"+i+"次合并的结果-----------------------------");
			for(Entry<String, Integer> entry:freSets.entrySet()){
				System.out.println(entry.getKey()+":"+entry.getValue());
			}*/
			i++;
			candidateFreSets=getNextCandidate(oneFreSets);
			/*System.out.println("--------------------组合的"+i+"项结果-----------------------------");
			for(Entry<String, Integer> entry:candidateFreSets.entrySet()){
				System.out.println(entry.getKey()+":"+entry.getValue());
			}*/
			//对候选组合的k+1项项集进行统计
			countCandidateFreqSet(candidateFreSets, srcData);
		/*	System.out.println("--------------------组合支持度计算结果-----------------------------");
			for(Entry<String, Integer> entry:candidateFreSets.entrySet()){
				System.out.println(entry.getKey()+":"+entry.getValue());
			}
			*/
			//筛选满足支持度的项集,赋值给oneFreSets
			oneFreSets=getSupportedItemSets(candidateFreSets, len);
			/*if(oneFreSets!=null){
				System.out.println("--------------------频繁项集("+i+"项)支持度筛选的结果-----------------------------");
				for(Entry<String, Integer> entry:oneFreSets.entrySet()){
					System.out.println(entry.getKey()+":"+entry.getValue());
				}	
			}*/
		}
		
		return freSets;
	}

/**
	 * 对所有满足支持度的频繁项集做一次冗余清理:频繁项集的子集也是频繁项集,项集为1的频繁项集也没有意义
	 * @param freSets
	 * @param oneFreSets
	 */
	public Map<String, Integer> union(Map<String, Integer> freSets, Map<String, Integer> oneFreSets) {
		Map<String,Integer> tmp=new HashMap<String,Integer>();
		for(Entry<String, Integer> entry:freSets.entrySet()){
			tmp.put(entry.getKey(), entry.getValue());
		}
		
		for(Entry<String, Integer> entry:freSets.entrySet()){
			String str=entry.getKey();
			if(containsFreuentSet(oneFreSets, str)){
				tmp.remove(str);
			}
		}
		
		tmp.putAll(oneFreSets);
		return tmp;
		
				
	}

调用Aprior算法就可以找出频繁项集,以上程序并未结束,下面的函数用来求关联规则 

2.关联规则:

	/**
	 * 求关联规则
	 * @param freSets:频繁项集
	 * @param data:源数据
	 * @return
	 */
	public Map<String,Double> getRelation(Map<String,Integer> freSets,List<String> data){
		Map<String,Double> results=new HashMap<String,Double>();   //规则结果:(关联规则,置信度)
		List<String> freKeys=mapKeyToList(freSets);          //频繁项集
		System.out.println(freKeys);
		List<String> subSets=new ArrayList<String>();
		String front="",end="";
		for(String freRecord:freKeys){ 
			System.out.println("\n-------------------------频繁项集------------------\n"+freRecord);
			subSets=getSubSets(freRecord);  //每个频繁项集求其真子集
			System.out.println("------------------真子集-----------------\n"+subSets);
			for(int i=0;i<subSets.size();i++){
				front=subSets.get(i);
				System.out.println("前置:"+front+"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
				for(int j=0;j<subSets.size();j++){
					end=subSets.get(j);
					if(!(isIntersected(front,end))){  //前置条件和后置条件不可以有交集
						double confidenceTest=getRealConfidence(data,subSets.get(i),subSets.get(j));
						System.out.println("前置--->后置:"+front+"--->"+end+" : "+confidenceTest);
						if(confidenceTest>confidence){
							System.out.println("put:("+front+","+end+")");
							results.put(subSets.get(i)+"--->"+subSets.get(j),confidenceTest);
						}
						
					}
				}
			}
		}
		
		return results;
	}
	/**
	 * 对频繁项集的每一条规则 freRecordFront-->freRecordEnd,求置信度
	 * @param data
	 * @param freRecordFront
	 * @param freRecordEnd
	 * @return
	 */
	public double getRealConfidence(List<String> data, String freRecordFront, String freRecordEnd) {
		String str=freRecordFront+freRecordEnd;
		return count(data,str)*1.0/count(data, freRecordFront);
	}
	/**
	 * 
	 * 对频繁项集的真子集,根据源数据data,统计每一个子集的支持度
	 * @param subSets
	 * @param data
	 * @return
	 *//*
	public Map<String, Integer> getSubSetSupport(List<String> subSets,List<String> data) {
		Map<String, Integer> map=new HashMap<String, Integer>();
		if(subSets==null||subSets.size()==0)
			return null;
		for(int i=0;i<subSets.size();i++){
			map.put(subSets.get(i), count(data, subSets.get(i)));
		}
		return map;
	}*/
	/**
	 * 对一项频繁项集求其真子集
	 * @param str
	 * @return
	 */
	public List<String> getSubSets(String str){
		List<String> subSets=new ArrayList<String>();
		String[] items=str.split(ITEMSPLITE);
		int len=items.length;
		String item="";
		String flag="";
		for(int i=1;i<(int)Math.pow(2, len)-1;i++){
			flag="";
			item="";
			int tmp=i;
			do{
				flag+=""+tmp%2;
				tmp/=2;
			}while(tmp>0);
			
			for(int j=0;j<flag.length();j++){
				if(flag.charAt(j)=='1'){
					item+=items[j]+ITEMSPLITE;
				}
			}
			subSets.add(item);
		}
		return subSets;
	}
	/**
	 * 比较两个项目是否有交集
	 * @param front
	 * @param end
	 * @return
	 */
	public boolean isIntersected(String front, String end){
		if(front==null||front.length()==0||end==null||end.length()==0){
			return false;
		}
		String[] frontItems=front.split(ITEMSPLITE);
		String[] endItems=end.split(ITEMSPLITE);
		boolean flag=false;
		for(int i=0;i<frontItems.length;i++){
			for(int j=0;j<endItems.length;j++){
				if(frontItems[i].equals(endItems[j])){
					flag=true;
					break;
				}
			}
		}
		return flag;	
	}
}

3.测试结果:

@Test
	public void testGetRelation() throws IOException{
		Apriori apriori=new Apriori();
		List<String> srcData=apriori.readDataFile("E:/test/data.txt");
		/*源数据
		 * 记录条数:9
			1;2;5;
			2;4;
			2;3;
			1;2;4;
			1;3;
			2;3;
			1;3;
			1;2;3;5;
			1;2;3;

		 */
		Map<String,Integer> map=apriori.apriori(srcData);
		for(Entry<String, Integer> entry:map.entrySet()){
			System.out.println(entry.getKey()+":"+entry.getValue());
		}
		/*频繁项集
		 * 2;1;5;:2
		2;1;3;:2
		4;2;:2
		*/
		System.out.println("**********************************************");
		Map<String,Double> resuults=apriori.getRelation(map, srcData);
		for(Entry<String, Double> entry:resuults.entrySet()){
			System.out.println(entry.getKey()+":"+entry.getValue());
		}
		/*关联规则
		4; ---> 2;:1.0
		5; ---> 2;:1.0
		1;5; ---> 2;:1.0
		5; ---> 1;:1.0
		2;5; ---> 1;:1.0
		5; ---> 2;1;:1.0
		*/

详细测试请下载相关包

  • 12
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值