1 Apriori介绍

Apriori算法使用频繁项集的先验知识,使用一种称作逐层搜索的迭代方法,k项集用于探索(k+1)项集。首先,通过扫描事务(交易)记录,找出所有的频繁1项集,该集合记做L1,然后利用L1找频繁2项集的集合L2,L2找L3,如此下去,直到不能再找到任何频繁k项集。最后再在所有的频繁集中找出强规则,即产生用户感兴趣的关联规则。

其中,Apriori算法具有这样一条性质:任一频繁项集的所有非空子集也必须是频繁的。因为假如P(I)< 最小支持度阈值,当有元素A添加到I中时,结果项集(A∩I)不可能比I出现次数更多。因此A∩I也不是频繁的。

2   连接步和剪枝步

在上述的关联规则挖掘过程的两个步骤中,第一步往往是总体性能的瓶颈。Apriori算法采用连接步和剪枝步两种方式来找出所有的频繁项集。

1)  连接步

为找出Lk(所有的频繁k项集的集合),通过将Lk-1(所有的频繁k-1项集的集合)与自身连接产生候选k项集的集合。候选集合记作Ck。设l1和l2是Lk-1中的成员。记li[j]表示li中的第j项。假设Apriori算法对事务或项集中的项按字典次序排序,即对于(k-1)项集li,li[1]<li[2]<……….<li[k-1]。将Lk-1与自身连接,如果(l1[1]=l2[1])&&(
l1[2]=l2[2])&&……..&& (l1[k-2]=l2[k-2])&&(l1[k-1]<l2[k-1]),那认为l1和l2是可连接。连接l1和l2 产生的结果是{l1[1],l1[2],……,l1[k-1],l2[k-1]}。

2)  剪枝步

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

(Tip:为什么要压缩CK呢?因为实际情况下事务记录往往是保存在外存储上,比如数据库或者其他格式的文件上,在每次计算候选计数时都需要将候选与所有事务进行比对,众所周知,访问外存的效率往往都比较低,因此Apriori加入了所谓的剪枝步,事先对候选集进行过滤,以减少访问外存的次数。)

3   Apriori算法实例

交易ID

商品ID列表

T100

I1,I2,I5

T200

I2,I4

T300

I2,I3

T400

I1,I2,I4

T500

I1,I3

T600

I2,I3

T700

I1,I3

T800

I1,I2,I3,I5

T900

I1,I2,I3

上图为某商场的交易记录,共有9个事务,利用Apriori算法寻找所有的频繁项集的过程如下:

详细介绍下候选3项集的集合C3的产生过程:从连接步,首先C3={{I1,I2,I3},{I1,I2,I5},{I1,I3,I5},{I2,I3,I4},{I2,I3,I5},{I2,I4,I5}}(C3是由L2与自身连接产生)。根据Apriori性质,频繁项集的所有子集也必须频繁的,可以确定有4个候选集{I1,I3,I5},{I2,I3,I4},{I2,I3,I5},{I2,I4,I5}}不可能时频繁的,因为它们存在子集不属于频繁集,因此将它们从C3中删除。注意,由于Apriori算法使用逐层搜索技术,给定候选k项集后,只需检查它们的(k-1)个子集是否频繁。

3. Apriori伪代码

算法: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;

 

4. 由频繁项集产生关联规则

Confidence(A->B)=P(B|A)=support_count(AB)/support_count(A)

关联规则产生步骤如下:

1)  对于每个频繁项集l,产生其所有非空真子集;

2)  对于每个非空真子集s,如果support_count(l)/support_count(s)>=min_conf,则输出 s->(l-s),其中,min_conf是最小置信度阈值。

例如,在上述例子中,针对频繁集{I1,I2,I5}。可以产生哪些关联规则?该频繁集的非空真子集有{I1,I2},{I1,I5},{I2,I5},{I1
},{I2}和{I5},对应置信度如下:

I1&&I2->I5            confidence=2/4=50%

I1&&I5->I2            confidence=2/2=100%

I2&&I5->I1            confidence=2/2=100%

I1 ->I2&&I5            confidence=2/6=33%

I2 ->I1&&I5            confidence=2/7=29%

I5 ->I1&&I2            confidence=2/2=100%

如果min_conf=70%,则强规则有I1&&I5->I2,I2&&I5->I1,I5
->I1&&I2。

5. Apriori Java代码

package com.apriori;

import java.util.ArrayList;

import java.util.Collections;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Set;

public class Apriori {

private final static
int SUPPORT = 2; // 支持度阈值

private final static
double CONFIDENCE = 0.7; // 置信度阈值

private final static
String ITEM_SPLIT=";"; // 项之间的分隔符

private final static
String CON="->"; // 项之间的分隔符

private final static
List<String> transList=new ArrayList<String>(); //所有交易

static{//初始化交易记录

transList.add("1;2;5;");

transList.add("2;4;");

transList.add("2;3;");

transList.add("1;2;4;");

transList.add("1;3;");

transList.add("2;3;");

transList.add("1;3;");

transList.add("1;2;3;5;");

transList.add("1;2;3;");

}

public Map<String,Integer>
getFC(){

Map<String,Integer> frequentCollectionMap=new
HashMap<String,Integer>();//所有的频繁集

frequentCollectionMap.putAll(getItem1FC());

Map<String,Integer> itemkFcMap=new
HashMap<String,Integer>();

itemkFcMap.putAll(getItem1FC());

while(itemkFcMap!=null&&itemkFcMap.size()!=0){

Map<String,Integer>
candidateCollection=getCandidateCollection(itemkFcMap);

Set<String>
ccKeySet=candidateCollection.keySet();

//对候选集项进行累加计数

for(String trans:transList){

for(String
candidate:ccKeySet){

boolean
flag=true;// 用来判断交易中是否出现该候选项,如果出现,计数加1

String[]
candidateItems=candidate.split(ITEM_SPLIT);

for(String
candidateItem:candidateItems){

if(trans.indexOf(candidateItem+ITEM_SPLIT)==-1){

flag=false;

break;

}

}

if(flag){

Integer
count=candidateCollection.get(candidate);

candidateCollection.put(candidate,
count+1);

}

}

}

//从候选集中找到符合支持度的频繁集项

itemkFcMap.clear();

for(String candidate:ccKeySet){

Integer
count=candidateCollection.get(candidate);

if(count>=SUPPORT){

itemkFcMap.put(candidate,
count);

}

}

//合并所有频繁集

frequentCollectionMap.putAll(itemkFcMap);

}

return frequentCollectionMap;

}

private Map<String,Integer>
getCandidateCollection(Map<String,Integer> itemkFcMap){

Map<String,Integer>
candidateCollection=new HashMap<String,Integer>();

Set<String>
itemkSet1=itemkFcMap.keySet();

Set<String>
itemkSet2=itemkFcMap.keySet();

for(String
itemk1:itemkSet1){

for(String
itemk2:itemkSet2){

//进行连接

String[]
tmp1=itemk1.split(ITEM_SPLIT);

String[]
tmp2=itemk2.split(ITEM_SPLIT);

String
c="";

if(tmp1.length==1){

if(tmp1[0].compareTo(tmp2[0])<0){

c=tmp1[0]+ITEM_SPLIT+tmp2[0]+ITEM_SPLIT;

}

}else{

boolean
flag=true;

for(int
i=0;i<tmp1.length-1;i++){

if(!tmp1[i].equals(tmp2[i])){

flag=false;

break;

}

}

if(flag&&(tmp1[tmp1.length-1].compareTo(tmp2[tmp2.length-1])<0)){

c=itemk1+tmp2[tmp2.length-1]+ITEM_SPLIT;

}

}

//进行剪枝

boolean
hasInfrequentSubSet = false;

if
(!c.equals("")) {

String[]
tmpC = c.split(ITEM_SPLIT);

for
(int i = 0; i < tmpC.length; i++) {

String
subC = "";

for
(int j = 0; j < tmpC.length; j++) {

if
(i != j) {

subC
= subC+tmpC[j]+ITEM_SPLIT;

}

}

if
(itemkFcMap.get(subC) == null) {

hasInfrequentSubSet
= true;

break;

}

}

}else{

hasInfrequentSubSet=true;

}

if(!hasInfrequentSubSet){

candidateCollection.put(c,
0);

}

}

}

return
candidateCollection;

}

private Map<String,Integer>
getItem1FC(){

Map<String,Integer>
sItem1FcMap=new HashMap<String,Integer>();

Map<String,Integer>
rItem1FcMap=new HashMap<String,Integer>();//频繁1项集

for(String
trans:transList){

String[]
items=trans.split(ITEM_SPLIT);

for(String
item:items){

Integer
count=sItem1FcMap.get(item+ITEM_SPLIT);

if(count==null){

sItem1FcMap.put(item+ITEM_SPLIT,
1);

}else{

sItem1FcMap.put(item+ITEM_SPLIT,
count+1);

}

}

}

Set<String>
keySet=sItem1FcMap.keySet();

for(String
key:keySet){

Integer
count=sItem1FcMap.get(key);

if(count>=SUPPORT){

rItem1FcMap.put(key,
count);

}

}

return
rItem1FcMap;

}

public Map<String,Double>
getRelationRules(Map<String,Integer> frequentCollectionMap){

Map<String,Double>
relationRules=new HashMap<String,Double>();

Set<String>
keySet=frequentCollectionMap.keySet();

for
(String key : keySet) {

double
countAll=frequentCollectionMap.get(key);

String[]
keyItems = key.split(ITEM_SPLIT);

if(keyItems.length>1){

List<String>
source=new ArrayList<String>();

Collections.addAll(source,
keyItems);

List<List<String>>
result=new ArrayList<List<String>>();

buildSubSet(source,result);//获得source的所有非空子集

for(List<String>
itemList:result){

if(itemList.size()<source.size()){//只处理真子集

List<String>
otherList=new ArrayList<String>();

for(String
sourceItem:source){

if(!itemList.contains(sourceItem)){

otherList.add(sourceItem);

}

}

String
reasonStr="";//前置

String
resultStr="";//结果

for(String
item:itemList){

reasonStr=reasonStr+item+ITEM_SPLIT;

}

for(String
item:otherList){

resultStr=resultStr+item+ITEM_SPLIT;

}

double
countReason=frequentCollectionMap.get(reasonStr);

double
itemConfidence=countAll/countReason;//计算置信度

if(itemConfidence>=CONFIDENCE){

String
rule=reasonStr+CON+resultStr;

relationRules.put(rule,
itemConfidence);

}

}

}

}

}

return
relationRules;

}

private  void
buildSubSet(List<String> sourceSet, List<List<String>> result) {

// 仅有一个元素时,递归终止。此时非空子集仅为其自身,所以直接添加到result中

if
(sourceSet.size() == 1) {

List<String>
set = new ArrayList<String>();

set.add(sourceSet.get(0));

result.add(set);

}
else if (sourceSet.size() > 1) {

// 当有n个元素时,递归求出前n-1个子集,在于result中

buildSubSet(sourceSet.subList(0,
sourceSet.size() - 1), result);

int
size = result.size();// 求出此时result的长度,用于后面的追加第n个元素时计数

// 把第n个元素加入到集合中

List<String>
single = new ArrayList<String>();

single.add(sourceSet.get(sourceSet.size()
- 1));

result.add(single);

// 在保留前面的n-1子集的情况下,把第n个元素分别加到前n个子集中,并把新的集加入到result中;

// 为保留原有n-1的子集,所以需要先对其进行复制

List<String>
clone;

for
(int i = 0; i < size; i++) {

clone
= new ArrayList<String>();

for
(String str : result.get(i)) {

clone.add(str);

}

clone.add(sourceSet.get(sourceSet.size()
- 1));

result.add(clone);

}

}

}

public static void
main(String[] args){

Apriori
apriori=new Apriori();

Map<String,Integer>
frequentCollectionMap=apriori.getFC();

System.out.println("----------------频繁集"+"----------------");

Set<String>
fcKeySet=frequentCollectionMap.keySet();

for(String
fcKey:fcKeySet){

System.out.println(fcKey+"  :  "+frequentCollectionMap.get(fcKey));

}

Map<String,Double> relationRulesMap=apriori.getRelationRules(frequentCollectionMap);

System.out.println("----------------关联规则"+"----------------");

Set<String> rrKeySet=relationRulesMap.keySet();

for(String rrKey:rrKeySet){

System.out.println(rrKey+"  :  "+relationRulesMap.get(rrKey));

}

}

}

转载自:http://blog.csdn.net/zjd950131/article/details/8071414

更多2


Apriori算法第二篇----详细分析和代码实现的更多相关文章

  1. Apriori算法第一篇

    摘要: Apriori算法是产生k项高频项目组的一般手段.算法概要:首先产生k项高频项目集合Lk,自身链接形成k+1项的项目结合C(k+1),然后剪枝(去掉以前去掉的不满足支持度的高频),生成K=1项 ...

  2. Apriori算法在购物篮分析中的运用

    购物篮分析是一个很经典的数据挖掘案例,运用到了Apriori算法.下面从网上下载的一超市某月份的数据库,利用Apriori算法进行管理分析.例子使用Python+MongoDB 处理过程1 数据建模( ...

  3. hibernate缓存机制详细分析 复制代码 内部资料 请勿转载 谢谢合作

    您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...

  4. java动态代理实现与原理详细分析(代码层面解释了AOP的实现)

    关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式    代理模式是常用的java设计模式, ...

  5. 管理后台界面 详细分析(内含代码 |【前端】)RuoYi

    最近在做的一个后台管理 因为关于隐私原因 只方便展示个别页面代码 不会上传项目 注意是前端代码 我把项目代码地址放在最后了 如有需要可自取学习   我会为各位兄弟详细的介绍其中各个属性的含义和用法,记 ...

  6. synchronized关键字的详细分析和代码实例

    在Java中,一般都是通过同步机制来解决线程安全问题的,在JDK 5.0之后又新增了Lock的方式来实现线程安全.所以说实现线程安全方式一共有三种方法 方式一: synchronized(同步监视器) ...

  7. 第二篇--上传git 代码

    准备工作: 首先,注册一个GitHub账号. 接着,新建一个仓库. 最后,下载Git 上传代码步骤: 第一步,新建一个本地文件夹作为本地仓库,进入该文件夹. 右击选择Git Bash Here ,输入 ...

  8. Python进阶【第二篇】编写Python代码

    一.第一句Python代码——Hello Word 在 /home/dev/ 目录下创建 hello.py 文件,内容如下: print "hello,world" 执行 hell ...

  9. vue学习指南:第二篇(详细Vue基础) - Vue的指令

    一. Vue 的介绍 1. vue是一个 mvvm 的框架.(面试官经常会问的),angular 是 mvc的框架. 2. vm 是 vum 的实例,这个实例存在计算机内存中,主要干两件大事: 1. ...

随机推荐

  1. tableView滚动的时候会 最后一行显示不完全的问题

    问题可能原因 1:tableView高度的设置不正确,应该是屏幕的高度减去上面的高度(包括状态栏以及navigationBar的高度).正确设置了tableView的高度之后,才可以正常滚动到最后一行 ...

  2. Java多态(一)

    父类: public class Parent { public String name; private String pass; public void say1(AA aa){ System.o ...

  3. wpf应用程序 打印标签

    新建一个wpf应用程序,Xaml如下: <Window x:Class="CreateBarCodeDemo.MainWindow" xmlns="http://s ...

  4. 安装MSYS2过程遇到的问题及解决记录

    1.在安装结束后按照官方教程开始更新系统是遇到了如下的错误 could not open file /var/lib/pacman/sync/msys32.db: Unrecognized archi ...

  5. C# 垃圾回收机制(转)

    摘要:今天我们漫谈C#中的垃圾回收机制,本文将从垃圾回收机制的原理讲起,希望对大家有所帮助. GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由 ...

  6. java应用测试报告生成(一): sonarqube配合Jenkins生成测试报告及覆盖率

    环境准备: 1.Jenkins集成环境(安装 sonarqube插件) 2.安装sonarqube服务(下载sonarqube安装包并解压,目录到"sonarqube-5.4/bin/lin ...

  7. Spring Boot 系列教程7-EasyUI-datagrid

    jQueryEasyUI jQuery EasyUI是一组基于jQuery的UI插件集合体,而jQuery EasyUI的目标就是帮助web开发者更轻松的打造出功能丰富并且美观的UI界面.开发者不需要 ...

  8. 【转】linux grep命令详解

    简介 grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它 ...

  9. svn删除用户

    如果安装有svn客户端,右键打开svn settings 然后就可以重新登录svn用户了 或者删除此文件

  10. 初识Selenium(三)

    浅谈基于Selenium的Web自动化测试框架 发表于:2011-4-25 10:58  作者:邵育亮   来源:51Testing软件测试网原创 字体:大 中 小 | 上一篇 | 下一篇 | 打印 ...