关联规则和Apriori算法的Java实现

Apriori算法两大定理:

1.A subset of a frequent itemset must be frequent:
频繁项集的所有非空子集都是频繁项目集

2.The supersets of any infrequent itemset cannot be frequent
任何不频繁项集的超集都是非频繁项目集

Apriori算法例子

// An highlighted block
Set.of("1","3","4")
Set.of("2","3","5")
Set.of("1","2","3","5")
Set.of("2","5")
minSupport=50%
minConfidence=80%

用Java简单实现Apriori算法



import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class Apriori {
	List<Set<String>> allFrequentSet=new ArrayList<Set<String>>();
	List<Set<String>> data=new ArrayList<Set<String>>();
	double minSupport;
	double confidence;
	public Apriori(List<Set<String>> data,double minSupport,double confidence) {
		this.data=data;
		this.minSupport=minSupport;
		this.confidence=confidence;
		List<Set<String>> itemSet = createItemSet(data);
		List<Set<String>> sc=null;
		int k=0;
		while(!itemSet.isEmpty()) {
			System.out.println("K \t:"+(++k));
			System.out.println("itemSet:"+itemSet);
			Map<Set<String>, Integer> frequent = frequentSet(itemSet,this.data);
			System.out.println("frequent:"+frequent);
			sc = scan(frequent,this.minSupport);
			System.out.println("scan"+sc);
			itemSet=candidateSet(sc);
		}
		associationRules(sc);
	
	}
	
	//关联规则
	public void associationRules(List<Set<String>> sc) {
		System.out.println("    lk\t\t 关联规则  \t\t confidence \t support \t 是否是强规则");
		sc.forEach(set->{
			List<Set<String>> subset = nonEmptySubset(set);
			int y=support(set,data);
			double support = 1.0*y/data.size();
			subset.forEach(sub->{
				int x=support(sub,data);
				HashSet<String> target = new HashSet<String>(set);
				target.removeAll(sub);
				double conf = 1.0*y/x;
				System.out.println(set+"\t"+sub+"-->"+target+
						"\t"+y+"/"+x+"("+String.format("%.2f", conf)+")\t"+
						y+"/"+data.size()+"("+String.format("%.2f", support)+
						")\t"+(conf>this.confidence));
			});
		});
	}
	//计算频繁项目集的支持度
	public Integer support(Set<String> set,List<Set<String>> data){
		return  (int) data.stream()
				.filter(d->d.containsAll(set))
				.count();
	}
	//生成候选项集
	//安算法来讲应该是将候选项集按一定规则排序 将两个集合只有最后一个元素不同的合并
	//个人理解:
	//对任意频繁集A B属于S  若  C = A 并 B 是频繁的  则  D=C- (A 交  B) 也是频繁的
	public  List<Set<String>> candidateSet(List<Set<String>> list) {//候选项集
		List<Set<String>> candidate=new ArrayList<Set<String>>();
		for(int i=0;i<list.size();i++) {
			for(int j=i+1;j<list.size();j++) {
				Set<String> item = list.get(i);
				Set<String> compare = list.get(j);
				if(item.size()==1){
					Set<String> unionn=new HashSet<String>(item);
					unionn.addAll(compare);
					candidate.add(unionn);
				}else{
					Set<String> intersection=new HashSet<String>(item);
					intersection.retainAll(compare);
					if(!intersection.isEmpty()) {
						Set<String> unionn=new HashSet<String>(item);
						unionn.addAll(compare);
						Set<String> difference =new HashSet<String>(unionn);
						difference.removeAll(intersection);
						if(allFrequentSet.contains(difference)) {
							candidate.add(unionn);
						}
					}
				}
			}
		}
		candidate = candidate.stream().distinct().collect(Collectors.toList());
		return candidate;
	}
	//扫描满足最小支持度的频繁项目集
	public  List<Set<String>> scan(Map<Set<String>, Integer> frequent,double minSupport) {
		List<Set<String>> list=new ArrayList<Set<String>>();
		for(Set<String> key :frequent.keySet()) {
			if(frequent.get(key)>=minSupport*data.size()) {
				list.add(key);
				allFrequentSet.add(key);
			}
		}
		return list;
	}
	//建立频繁项目集与支持度的映射关系
	public  Map<Set<String>, Integer> frequentSet(List<Set<String>> candidate,List<Set<String>> data) {//频繁项集
		Map<Set<String>,Integer> frequent=new HashMap();
		candidate.forEach(can->{
			Integer sup = support(can,data);
			frequent.put(can,sup);
		});
		return  frequent;
	}
	//创建事务集
	public  List<Set<String>> createItemSet(List<Set<String>> data) {
		List<Set<String>> itemSet=new ArrayList< Set<String> >();
		Set<String> set =new HashSet<String>();
		data.forEach(set::addAll);
		set.forEach(e->itemSet.add(new HashSet<String>() {{add(e);}}));
		return itemSet;
	}
	//获取给定集合的所有非空真子集
	public  List<Set<String>>  nonEmptySubset(Set<String> set){
		List<Set<String>> list =new ArrayList<Set<String>>();
		List<HashSet<String>> arr = set.stream()
				.map(s->new HashSet<String>() {{add(s);}})
				.collect(Collectors.toList());
		//子集个数位2^n 
		//遍历二进制序列的每一位,若为“1”则保留
		int max=(int) Math.pow(2,arr.size());
		for(int i=0;i<max;i++) {
			Set<String> tmp =new HashSet<String>();
			String bt = Integer.toBinaryString(i);
			while(arr.size()>bt.length()) {
				bt="0"+bt;
			}
			for(int j=0;j<arr.size()&&j<bt.length();j++) {
				if(bt.charAt(j)=='1') {
					tmp.addAll(arr.get(j));
				}
			}
			if(!tmp.isEmpty()&&!tmp.containsAll(set)) {
				list.add(tmp);
			}
		}
		return list;
	}
}



测试数据

public static void main(String[] args) {
		List<Set<String>> data=new ArrayList<Set<String>>();
		
		data.add(Set.of("1","3","4"));
		data.add(Set.of("2","3","5"));
		data.add(Set.of("1","2","3","5"));
		data.add(Set.of("2","5"));
		
		Apriori t=new Apriori(data,0.5,0.8);
	}

控制台输出:

K 	:1
itemSet:[[1], [2], [3], [4], [5]]
frequent:{[1]=2, [2]=3, [3]=3, [4]=1, [5]=3}
scan[[1], [2], [3], [5]]
K 	:2
itemSet:[[1, 2], [1, 3], [1, 5], [2, 3], [2, 5], [3, 5]]
frequent:{[1, 2]=1, [1, 3]=2, [2, 3]=2, [1, 5]=1, [2, 5]=3, [3, 5]=2}
scan[[1, 3], [2, 3], [2, 5], [3, 5]]
K 	:3
itemSet:[[2, 3, 5]]
frequent:{[2, 3, 5]=2}
scan[[2, 3, 5]]
    lk		 	关联规则  	      	confidence   	support 	是否是强规则
[2, 3, 5]	   	[5]-->[2, 3]		2/3(0.67)		2/4(0.50)	false
[2, 3, 5]		[3]-->[2, 5]		2/3(0.67)		2/4(0.50)	false
[2, 3, 5]		[3, 5]-->[2]		2/2(1.00)		2/4(0.50)	true
[2, 3, 5]		[2]-->[3, 5]		2/3(0.67)		2/4(0.50)	false
[2, 3, 5]		[2, 5]-->[3]		2/3(0.67)		2/4(0.50)	false
[2, 3, 5]		[2, 3]-->[5]		2/2(1.00)		2/4(0.50)	true

Apriori算法有两个致命的性能瓶颈:

1.多次扫描事务数据库,需要很大的I/O负载

2.可能产生庞大的侯选集

主要的改进方法有:

1.基于数据分割(Partition)的方法:基本原理是“在一个划分中的支持度小于最小支持度的k-项集不 可能是全局频繁的”。
2.基于散列(Hash)的方法:基本原理是“在一个hash桶内支持度小于最小支持度的k-项集不可能是全局频繁的”。
3.基于采样(Sampling)的方法:基本原理是“通过采样技术,评估被采样的子集中,并依次来估计k-项集的全局频度”。
4.其他:如,动态删除没有用的事务:“不包含任何Lk的事务对未来的扫描结果不会产生影响,因而可以删除”。

个人理解:

关联规则是一个条件概率

一条很强的关联规则不一定是代表它有意义

两件事情相关但不能代表它们有必然的联系

猜你喜欢

转载自blog.csdn.net/qq_39464369/article/details/88381035