频繁模式挖掘apriori算法介绍及Java实现

频繁模式是频繁地出现在数据集中的模式(如项集、子序列或者子结构)。例如,频繁地同时出现在交易数据集中的商品(如牛奶和面包)的集合是频繁项集。

一些基本概念

支持度:support(A=>B)=P(A并B)

置信度:confidence(A=>B)=P(B|A)

频繁k项集:如果项集I的支持度满足预定义的最小支持度阈值,则称I为频繁项集,包含k个项的项集称为k项集。

算法思想

Apriori算法是Agrawal和R. Srikant于1994年提出,为布尔关联规则挖掘频繁项集的原创性算法。通过名字可以看出算法基于这样一个事实:算法使用频繁项集性质的先验知识。apriori算法使用一种成为逐层搜索的迭代算法,其中k项集用于探索(k+1)项集。首先,通过扫描数据库,累计每个项的计数,并搜集满足最小支持度的项,找出频繁1项集的集合。该集合记为L1。然后,使用L1找出频繁2项集的集合L2,使用L2找出L3,如此下去,直到不能再找到频繁k项集。

可以想象,该算法基本思路计算复杂度是非常大的。为了提高频繁项集的产生效率,使用先验性质(频繁项集的所有非空子集也一定是频繁的;换句话说,若某个集合存在一个非空子集不是频繁项集,则该集合不是频繁项集)来压缩搜索空间。

如何在算法中使用先验性质?为了理解这一点,我们考察如何使用Lk-1找出Lk,其中k>=2。主要由两步构成:连接步和剪枝步。

连接步:为找出Lk,通过将Lk-1与自身相连接产生候选集k项集的集合。该候选集的集合记为Ck。设l1和l2是Lk-1中的项集。记号li[j]表示li的第j项(例如,l1[k-2]表示l1的倒数第2项)。为了有效实现,apriori算法假定事务或项集中的项按字典序排列。对于(k-1)项集li,这意味着把项排序,使得li[1]<li[2]<...<li[k-1]。连接Lk-1和Lk-1;其中Lk-1的元素是可连接的,如果它们前(k-2)项相同。即Lk-1的元素l1和l2是可连接的,如果(l1[1]=l2[1])^(l1[2]=l2[2])^...^(l1[k-2]=l2[k-2])^(l1[k-1]<l2[k-1])。条件l1[k-1]<l2[k-1]是简单保证不产生重复。连接l1和l2产生的结果项集是{l1[1],l1[2],...,l1[k-1],l2[k-1]}

剪枝步: CK是LK的超集,也就是说,CK的成员可能是也可能不是频繁的。通过扫描所有的事务(交易),确定CK中每个候选的计数,判断是否小于最小支持度计数,如果不是,则认为该候选是频繁的。为了压缩Ck,可以利用Apriori性质:任一频繁项集的所有非空子集也必须是频繁的,反之,如果某个候选的非空子集不是频繁的,那么该候选肯定不是频繁的,从而可以将其从CK中删除。(该步利用了标红的先验性质)

图例

伪代码

算法:Apriori
输入:D - 事务数据库;min_sup - 最小支持度计数阈值
输出:L - D中的频繁项集
方法:
     L1=find_frequent_1-itemsets(D); // 找出所有频繁1项集
     For(k=2;Lk-1!=null;k++){
        Ck=apriori_gen(Lk-1); // 产生候选,并剪枝
        For each 事务t in D{ // 扫描D进行候选计数
            Ct =subset(Ck,t); // 得到t的子集
            For each 候选c 属于 Ct
                         c.count++;
        }
        Lk={c属于Ck | c.count>=min_sup}
}
Return L=所有的频繁集;

Procedure apriori_gen(Lk-1:frequent(k-1)-itemsets)
      For each项集l1属于Lk-1
              For each项集 l2属于Lk-1
                       If((l1[1]=l2[1])&&( l1[2]=l2[2])&&…….
&& (l1[k-2]=l2[k-2])&&(l1[k-1]<l2[k-1])) then{
                   c=l1连接l2 //连接步:产生候选
                   if has_infrequent_subset(c,Lk-1) then
                       delete c; //剪枝步:删除非频繁候选
                   else add c to Ck;
                  }
          Return Ck;

Procedure has_infrequent_sub(c:candidate k-itemset; Lk-1:frequent(k-1)-itemsets)
        For each(k-1)-subset s of c
            If s不属于Lk-1 then
               Return true;
        Return false;

Java实现

该java代码基本上是严格按照伪代码的流程写的,比较容易理解。

package com.zhyoulun.apriori;

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

public class Apriori2
{
	private final static int SUPPORT = 2; // 支持度阈值
	private final static double CONFIDENCE = 0.7; // 置信度阈值
	private final static String ITEM_SPLIT = ";"; // 项之间的分隔符
	private final static String CON = "->"; // 项之间的分隔符

	/**
	 * 算法主程序
	 * @param dataList
	 * @return
	 */
	public Map<String, Integer> apriori(ArrayList<String> dataList)
	{
		Map<String, Integer> stepFrequentSetMap = new HashMap<>();
		stepFrequentSetMap.putAll(findFrequentOneSets(dataList));

		Map<String, Integer> frequentSetMap = new HashMap<String, Integer>();//频繁项集
		frequentSetMap.putAll(stepFrequentSetMap);

		while(stepFrequentSetMap!=null && stepFrequentSetMap.size()>0)
		{
			Map<String, Integer> candidateSetMap = aprioriGen(stepFrequentSetMap);

			Set<String> candidateKeySet = candidateSetMap.keySet();

			//扫描D,进行计数
			for(String data:dataList)
			{
				for(String candidate:candidateKeySet)
				{
					boolean flag = true;
					String[] strings = candidate.split(ITEM_SPLIT);
					for(String string:strings)
					{
						if(data.indexOf(string+ITEM_SPLIT)==-1)
						{
							flag = false;
							break;
						}
					}
					if(flag)
						candidateSetMap.put(candidate, candidateSetMap.get(candidate)+1);
				}
			}

			//从候选集中找到符合支持度的频繁项集
			stepFrequentSetMap.clear();
			for(String candidate:candidateKeySet)
			{
				Integer count = candidateSetMap.get(candidate);
				if(count>=SUPPORT)
					stepFrequentSetMap.put(candidate, count);
			}

			// 合并所有频繁集
			frequentSetMap.putAll(stepFrequentSetMap);
		}

		return frequentSetMap;
	}

	/**
	 * find frequent 1 itemsets
	 * @param dataList
	 * @return
	 */
	private Map<String, Integer> findFrequentOneSets(ArrayList<String> dataList)
	{
		Map<String, Integer> resultSetMap = new HashMap<>();

		for(String data:dataList)
		{
			String[] strings = data.split(ITEM_SPLIT);
			for(String string:strings)
			{
				string += ITEM_SPLIT;
				if(resultSetMap.get(string)==null)
				{
					resultSetMap.put(string, 1);
				}
				else {
					resultSetMap.put(string, resultSetMap.get(string)+1);
				}
			}
		}
		return resultSetMap;
	}

	/**
	 * 根据上一步的频繁项集的集合选出候选集
	 * @param setMap
	 * @return
	 */
	private Map<String, Integer> aprioriGen(Map<String, Integer> setMap)
	{
		Map<String, Integer> candidateSetMap = new HashMap<>();

		Set<String> candidateSet = setMap.keySet();
		for(String s1:candidateSet)
		{
			String[] strings1 = s1.split(ITEM_SPLIT);
			String s1String = "";
			for(String temp:strings1)
				s1String += temp+ITEM_SPLIT;

			for(String s2:candidateSet)
			{
				String[] strings2 = s2.split(ITEM_SPLIT);

				boolean flag = true;
				for(int i=0;i<strings1.length-1;i++)
				{
					if(strings1[i].compareTo(strings2[i])!=0)
					{
						flag = false;
						break;
					}
				}
				if(flag && strings1[strings1.length-1].compareTo(strings2[strings1.length-1])<0)
				{
					//连接步:产生候选
					String c = s1String+strings2[strings2.length-1]+ITEM_SPLIT;
					if(hasInfrequentSubset(c, setMap))
					{
						//剪枝步:删除非频繁的候选
					}
					else {
						candidateSetMap.put(c, 0);
					}
				}
			}
		}

		return candidateSetMap;
	}

	/**
	 * 使用先验知识,判断候选集是否是频繁项集
	 * @param candidate
	 * @param setMap
	 * @return
	 */
	private boolean hasInfrequentSubset(String candidateSet, Map<String, Integer> setMap)
	{
		String[] strings = candidateSet.split(ITEM_SPLIT);

		//找出候选集所有的子集,并判断每个子集是否属于频繁子集
		for(int i=0;i<strings.length;i++)
		{
			String subString = "";
			for(int j=0;j<strings.length;j++)
			{
				if(j!=i)
				{
					subString += strings[j]+ITEM_SPLIT;
				}
			}

			if(setMap.get(subString)==null)
				return true;
		}

		return false;
	}

	/**
	 * 由频繁项集产生关联规则
	 * @param frequentSetMap
	 * @return
	 */
	public Map<String, Double> getRelationRules(Map<String, Integer> frequentSetMap)
	{
		Map<String, Double> relationsMap = new HashMap<>();
		Set<String> keySet = frequentSetMap.keySet();

		for(String key:keySet)
		{
			List<String> keySubset = subset(key);
			for(String keySubsetItem:keySubset)
			{
				//子集keySubsetItem也是频繁项
				Integer count = frequentSetMap.get(keySubsetItem);
				if(count!=null)
				{
					Double confidence = (1.0*frequentSetMap.get(key))/(1.0*frequentSetMap.get(keySubsetItem));
					if(confidence>CONFIDENCE)
						relationsMap.put(keySubsetItem+CON+expect(key, keySubsetItem), confidence);
				}
			}
		}

		return relationsMap;
	}

	/**
	 * 求一个集合所有的非空真子集
	 *
	 * @param sourceSet
	 * @return
	 * 为了以后可以用在其他地方,这里我们不是用递归的方法
	 *
	 * 参考:http://blog.163.com/[email protected]/blog/static/3980524020109784356915/
	 * 思路:假设集合S(A,B,C,D),其大小为4,拥有2的4次方个子集,即0-15,二进制表示为0000,0001,...,1111。
	 * 对应的子集为空集,{D},...,{A,B,C,D}。
	 */
	private List<String> subset(String sourceSet)
	{
		List<String> result = new ArrayList<>();

		String[] strings = sourceSet.split(ITEM_SPLIT);
		//非空真子集
		for(int i=1;i<(int)(Math.pow(2, strings.length))-1;i++)
		{
			String item = "";
			String flag = "";
			int ii=i;
			do
			{
				flag += ""+ii%2;
				ii = ii/2;
			} while (ii>0);
			for(int j=flag.length()-1;j>=0;j--)
			{
				if(flag.charAt(j)=='1')
				{
					item = strings[j]+ITEM_SPLIT+item;
				}
			}
			result.add(item);
		}

		return result;
	}

	/**
	 * 集合运算,A/B
	 * @param A
	 * @param B
	 * @return
	 */
	private String expect(String stringA,String stringB)
	{
		String result = "";

		String[] stringAs = stringA.split(ITEM_SPLIT);
		String[] stringBs = stringB.split(ITEM_SPLIT);

		for(int i=0;i<stringAs.length;i++)
		{
			boolean flag = true;
			for(int j=0;j<stringBs.length;j++)
			{
				if(stringAs[i].compareTo(stringBs[j])==0)
				{
					flag = false;
					break;
				}
			}
			if(flag)
				result += stringAs[i]+ITEM_SPLIT;
		}

		return result;
	}

	public static void main(String[] args)
	{
		ArrayList<String> dataList = new ArrayList<>();
		dataList.add("1;2;5;");
		dataList.add("2;4;");
		dataList.add("2;3;");
		dataList.add("1;2;4;");
		dataList.add("1;3;");
		dataList.add("2;3;");
		dataList.add("1;3;");
		dataList.add("1;2;3;5;");
		dataList.add("1;2;3;");

		System.out.println("=数据集合==========");
		for(String string:dataList)
		{
			System.out.println(string);
		}

		Apriori2 apriori2 = new Apriori2();

		System.out.println("=频繁项集==========");

		Map<String, Integer> frequentSetMap = apriori2.apriori(dataList);
		Set<String> keySet = frequentSetMap.keySet();
		for(String key:keySet)
		{
			System.out.println(key+" : "+frequentSetMap.get(key));
		}

		System.out.println("=关联规则==========");
		Map<String, Double> relationRulesMap = apriori2.getRelationRules(frequentSetMap);
		Set<String> rrKeySet = relationRulesMap.keySet();
		for (String rrKey : rrKeySet)
		{
			System.out.println(rrKey + "  :  " + relationRulesMap.get(rrKey));
		}

	}
}

计算结果

=数据集合==========
1;2;5;
2;4;
2;3;
1;2;4;
1;3;
2;3;
1;3;
1;2;3;5;
1;2;3;
=频繁项集==========
1;2; : 4
1;3; : 4
5; : 2
2;3; : 4
4; : 2
2;4; : 2
1;5; : 2
3; : 6
2; : 7
1; : 6
1;2;5; : 2
1;2;3; : 2
2;5; : 2
=关联规则==========
4;->2;  :  1.0
5;->1;2;  :  1.0
5;->1;  :  1.0
1;5;->2;  :  1.0
5;->2;  :  1.0
2;5;->1;  :  1.0

参考:

http://blog.csdn.net/zjd950131/article/details/8071414

http://www.cnblogs.com/zacard-orc/p/3646979.html

数据挖掘:概念与技术

转载请注明出处:http://blog.csdn.net/zhyoulun/article/details/41978401

时间: 2024-10-10 09:17:37

频繁模式挖掘apriori算法介绍及Java实现的相关文章

频繁模式挖掘-Apriori算法

DM实验,写的比较二,好多情况还没有考虑,后续有时间会修改. 开始的时候数据结构没设计好导致写到后面费了很大的劲.不过还好python的列表有起死回生的功效... 数据集:database.txt I1,I2,I5 I2,I4 I2,I3 I1,I2,I4 I1,I3 I2,I3 I1,I3 I1,I2,I3,I5 I1,I2,I3 apriori.py #coding=utf-8 """ author:messiandzcy apriori.py date:2014.12.

【甘道夫】并行化频繁模式挖掘算法FP Growth及其在Mahout下的命令使用

今天调研了并行化频繁模式挖掘算法PFP Growth及其在Mahout下的命令使用,简单记录下试验结果,供以后查阅: 环境:Jdk1.7 + Hadoop2.2.0单机伪集群 +  Mahout0.6(0.8和0.9版本都不包含该算法.Mahout0.6可以和Hadoop2.2.0和平共处有点意外orz) 部分输入数据,输入数据一行代表一个购物篮: 4750,19394,25651,6395,5592 26180,10895,24571,23295,20578,27791,2729,8637 7

Apriori算法学习和java实现

关联规则挖掘可以发现大量数据中项集之间有趣的关联或相关联系.一个典型的关联规则挖掘例子是购物篮分析,即通过发现顾客放入其购物篮中的不同商品之间的联系,分析顾客的购物习惯,从而可以帮助零售商指定营销策略,引导销售等.国外有"啤酒与尿布"的故事,国内有泡面和火腿的故事.本文以Apriori算法为例介绍关联规则挖掘并以java实现. 什么是关联规则: 对于记录的集合D和记录A,记录B,A,B属于D:  A--->B  [support(A->B)=p(AUB) ,confiden

[ML&amp;DL] 频繁项集Apriori算法

频繁项集Apriori算法 Reference 数据挖掘十大算法之Apriori详解 Apriori算法详解之[一.相关概念和核心步骤] 关联分析之Apriori算法 haha 算法理解部分主要是前两个链接,写的很靠谱.在实际中再配合上hadoop的mapreduce.

推荐系统第4周--- 基于频繁模式的推荐系统和关联规则挖掘Apriori算法

数据挖掘:关联规则挖掘 关联规则挖掘:Apriori算法 提高Apriori的效率 基于散列的算法基于FP tree的算法

高效频繁模式挖掘算法PrePost和FIN的C++源码

PrePost的C++源码见http://www.cis.pku.edu.cn/faculty/system/dengzhihong/Source%20Code/prepost.cpp. 算法内容参见论文:A New Algorithm for Fast Mining Frequent Itemsets Using N-Lists) 论文免费下载地址:http://info.scichina.com:8084/sciFe/EN/abstract/abstract508369.shtml  或ht

静态频繁子图挖掘算法用于动态网络——gSpan算法研究

摘要 随着信息技术的不断发展,人类可以很容易地收集和储存大量的数据,然而,如何在海量的数据中提取对用户有用的信息逐渐地成为巨大挑战.为了应对这种挑战,数据挖掘技术应运而生,成为了最近一段时期数据科学的和人工智能领域内的研究热点.数据集中的频繁模式作为一种有价值的信息,受到了人们的广泛关注,成为了数据挖掘技术研究领域内的热门话题和研究重点. 传统的频繁模式挖掘技术被用来在事务数据集中发现频繁项集,然而随着数据挖掘技术应用到非传统领域,单纯的事务数据结构很难对新的领域的数据进行有效的建模.因此,频繁

FP-Tree频繁模式树算法

参考资料:http://blog.csdn.net/sealyao/article/details/6460578 更多数据挖掘算法:https://github.com/linyiqun/DataMiningAlgorithm 介绍 FP-Tree算法全称是FrequentPattern Tree算法,就是频繁模式树算法,他与Apriori算法一样也是用来挖掘频繁项集的,不过不同的是,FP-Tree算法是Apriori算法的优化处理,他解决了Apriori算法在过程中会产生大量的候选集的问题,

Apriori 算法 理论

关联规则的基本模型-规则 关联规则的基本模型-置信度 关联规则的基本模型-支持度 关联规则基本概念 频繁项集举例 关联规则举例 Apriori的步骤 Apriori的重要性质 Apriori算法实例-产生频繁项集 Apriori算法实例-产生关联规则 Apriori算法思想总结 Apriori算法代码 由L(k-1)生成候选集Ck 从频繁项集中挖掘关联规则 由频繁项集产生关联规则--频繁项集性质 由频繁项集产生关联规则