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

一些基本概念

支持度: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代码基本上是严格依照伪代码的流程写的。比較easy理解。

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/xiaohui_1123@126/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

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

  1. 频繁模式挖掘 Apriori算法 FP-tree

    啤酒 尿布 组合营销 X=>Y,其中x属于项集I,Y属于项集I,且X.Y的交集等于空集. 2类算法 Apriori算法 不断地构造候选集.筛选候选集来挖掘出频繁项集,需要多次扫描原始数据.磁盘I ...

  2. 数据挖掘(七):Apriori算法:频繁模式挖掘

    1 算法思想 算法使用频繁项集性质的先验知识.Apriori使用一种称作逐层搜索的迭代方法,k项集用于探索(k+1)项集.首先,通过扫描数据库,累积每个项的计数,并收集满足最小支持度的项,找出频繁1项 ...

  3. 频繁模式挖掘中Apriori、FP-Growth和Eclat算法的实现和对比

    最近上数据挖掘的课程,其中学习到了频繁模式挖掘这一章,这章介绍了三种算法,Apriori.FP-Growth和Eclat算法:由于对于不同的数据来说,这三种算法的表现不同,所以我们本次就对这三种算法在 ...

  4. 频繁模式挖掘中Apriori、FP-Growth和Eclat算法的实现和对比(Python实现)

    最近上数据挖掘的课程,其中学习到了频繁模式挖掘这一章,这章介绍了三种算法,Apriori.FP-Growth和Eclat算法:由于对于不同的数据来说,这三种算法的表现不同,所以我们本次就对这三种算法在 ...

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

    今天调研了并行化频繁模式挖掘算法PFP Growth及其在Mahout下的命令使用,简单记录下试验结果,供以后查阅: 环境:Jdk1.7 + Hadoop2.2.0单机伪集群 +  Mahout0.6 ...

  6. 八、频繁模式挖掘Frequent Pattern Mining

    频繁模式挖掘(Frequent Pattern Mining): 频繁项集挖掘是通常是大规模数据分析的第一步,多年以来它都是数据挖掘领域的活跃研究主题.建议用户参考维基百科的association r ...

  7. 关联规则—频繁项集Apriori算法

    频繁模式和对应的关联或相关规则在一定程度上刻画了属性条件与类标号之间的有趣联系,因此将关联规则挖掘用于分类也会产生比较好的效果.关联规则就是在给定训练项集上频繁出现的项集与项集之间的一种紧密的联系.其 ...

  8. Apriori算法介绍(Python实现)

    导读: 随着大数据概念的火热,啤酒与尿布的故事广为人知.我们如何发现买啤酒的人往往也会买尿布这一规律?数据挖掘中的用于挖掘频繁项集和关联规则的Apriori算法可以告诉我们.本文首先对Apriori算 ...

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

    数据挖掘:关联规则挖掘

随机推荐

  1. webService 客户端接口调用【java】

    最近实际项目中使用到了WebService,简单总结下使用方式: 1.拿到接口:http://*******:8080/osms/services/OrderWebService?wsdl 我们可以将 ...

  2. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home ...

  3. C#使用DirectoryEntry操作IIS创建网站和虚拟路径

    原文:http://www.cnblogs.com/Aiooioo/archive/2011/05/30/cs-iis.html 在.Net中我们可以使用内置的类DirectoryEntry来承载II ...

  4. Response.ContentType 详细列表 (转载)

    不同的ContentType 会影响客户端所看到的效果.默认的ContentType为 text/html 也就是网页格式.代码如: <% response.ContentType =" ...

  5. oracle索引学习

    查看执行状态: 选中代码直接按F5,或者点击Tools===>>Explain Plan 一.索引的注意事项: 当任何单个查询要检索的行少于或者等于整个表行数的10%时,索引就非常有用.这 ...

  6. shell参数

    shell获取当前执行脚本的路径 filepath=$(cd "$(dirname "$0")"; pwd) 脚本文件的绝对路径存在了环境变量filepath中 ...

  7. 0118——UIButtton

    1.Button的定义 UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; Button有六种类型 enum { UI ...

  8. C++程序中不同变量、函数在内存中内存中的分布情况

    一.一个C++编译的程序占用的内存分为以下几个部分 1.栈区:由编译器自动分配 存放函数的参数值,局部变量的值等,操作方式类似于数据结构中的栈. 2.堆区:一般由程序员分配释放,若程序员不释放,程序结 ...

  9. 退出ssh,程序继续运行的解决办法

    对Unix.Linux类服务器维护经常是通过ssh完成的,而有些操作执行时间较长,如:更新程序.文件备份.软件编译安装等.此时如果断开ssh连接的话,更新程序就会随之被中断.如何保证断开ssh后仍旧能 ...

  10. CentOS 安装redis2.8.13 提醒"libc.so.6: version `GLIBC_2.14' not found"系统的glibc版本太低

    以下在系统CentOS 6.3 x86_64上操作 1.试图运行程序,提示"libc.so.6: version `GLIBC_2.14' not found",原因是系统的gli ...