web挖掘之Apriori算法 JAVA实现

博主初次接触数据挖掘方面的研究,从最经典最基础的Apriori算法编起,不过这是博主前几个月写的了,所以现在也是凭印象写的,有些粗糙,还请谅解。

下面讲解部分为转载

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]) 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。

JAVA代码原创,不过写的时候也是参照了部分前辈的代码,所以可能会有相似的地方,附带注释,希望大家能看懂

//Apriori频繁集生成和关联规则生成文件
package test;

import java.util.*;
import java.util.Map.Entry;
/*
 * *主要用来实现Apriori算法的频繁集挖掘与关联规则产生
 * author——郭川
 */
public class Apriori_data {
    private int countDatabase;//事务数据库中事务数量
    private Integer minSup; //最小支持度
    private Float minCon;   //最小置信度
    private Map<Integer, Set<String>> database;//事务数据库


    private Map<Set<String>, Integer> frequItemSets;//频繁项集
    private Map<Integer,Map<Set<String>, Integer>> freshFrequItemSets;//备用频繁项集
    private HashMap<Set<String>, Set<Set<String>>> associationRules;//产生的关联规则
    /*
     * 构造函数
     */
    public Apriori_data( Map<Integer, Set<String>> database,
                         Integer minSup,
                         Float minCon ) {
        this.minSup = minSup;
        this.minCon = minCon;
        this.database = database;
        countDatabase = database.size();
        frequItemSets = new HashMap<Set<String>, Integer>();
        associationRules = new HashMap<Set<String>, Set<Set<String>>>();    
        freshFrequItemSets = new HashMap<Integer, Map<Set<String>, Integer>>();
    }
    /*
     * 扫描事务集,计算频繁集1-
     * frequItemSet1存储第一个频繁项集
     * candiItemSet1存储第一个候选集
     */
    public HashMap<Set<String>, Integer> genFrequItemSets1(){
        HashMap<Set<String>, Integer>frequItemSet1 = new HashMap<Set<String>, Integer>();
        HashMap<Set<String>, Integer>candiItemSet1 = new HashMap<Set<String>, Integer>();
        candiItemSet1 = genCandiFrequ1();
        //System.out.println(candiItemSet1);
        Iterator<Map.Entry<Set<String>,Integer>>it = candiItemSet1.entrySet().iterator();

        while(it.hasNext()){
        Entry<Set<String>, Integer> entry=it.next();
        Integer v = entry.getValue();
        if(v>=minSup){
            frequItemSet1.put(entry.getKey(), v);
        }
        }

        return frequItemSet1;
    }
    /*
     * 产生频繁集1-的候选集
     * candiItemSet1存储候选集1-
     */
    public HashMap<Set<String>, Integer> genCandiFrequ1(){

        HashMap<Set<String>,Integer>candiItemSet1 = new HashMap<Set<String>, Integer>();
        Iterator<Map.Entry<Integer,Set<String>>>it = database.entrySet().iterator();
        while(it.hasNext()){

        Map.Entry<Integer, Set<String>>s = it.next();
        Set<String>item = s.getValue();

        for(String key:item){
            Set<String>value = new HashSet<String>();
            value.add(key);

            if(!candiItemSet1.containsKey(value)){
                Integer k=1;
                candiItemSet1.put(value, k);
                //System.out.println(value);
            }

            else if(candiItemSet1.containsKey(value)){
                Integer k=1+candiItemSet1.get(value);
                candiItemSet1.put(value,k);
            }
        }

        }
        //System.out.println(candiItemSet1);
        return candiItemSet1;
    }
    /*
     * 根据频繁(k-1)-集产生候选k-集
     * m=k-1;
     * freqMItemSet2为频繁(k-1)-项集
     * candiItemSet为生成的候选k-项集
     */
    public Set<Set<String>> genCandiFrequen(int m, Map<Set<String>, Integer> frequItemSets2)
    {
        Set<Set<String>>candiItemSet =new HashSet<Set<String>>();
        //System.out.println(frequItemSets2);
        Iterator<Map.Entry<Set<String>, Integer>>originIt = frequItemSets2.entrySet().iterator();
        while(originIt.hasNext()){
            Set<String> item1 = originIt.next().getKey();
            Iterator<Map.Entry<Set<String>, Integer>>currentIt = getIteratorF(item1,frequItemSets2);

            while(currentIt.hasNext()){
                Set<String> item2 = currentIt.next().getKey();
                Set<String> symmetriSet = new HashSet<String>();
                //System.out.println(item1);
                //System.out.println(item2);

                symmetriSet.addAll(item1);
                //System.out.println(symmetriSet);
                symmetriSet.retainAll(item2);
                //System.out.println(symmetriSet.size());
                if(symmetriSet.size()==m-1){
                    //System.out.println(item2);
                    Set<String> differentSet = new HashSet<String>();
                    differentSet.addAll(item1);
                    //System.out.println(differentSet);
                    differentSet.removeAll(item2);
                    differentSet.addAll(item2);

                    if(!candiItemSet.contains(differentSet)){
                        candiItemSet.add(differentSet);
                    }
                }
                }
        }
        //System.out.println(candiItemSet);
        return candiItemSet;
    }
    /*
     * 判断某一项可否被剪枝
     * judgedItem为待检查的k项
     * frequItem为(k-1)-频繁项集
     * 若该judgedItem可被添加,则返回false,否则剪枝成功,返回true
     */
    public boolean judgeForInvolve(Set<String> judgedItem, Set<Set<String>> frequItem)
    {
            //System.out.println(frequItem);
            Set<String> temp = new HashSet<String>();
            temp.addAll(judgedItem);
            for(String item:judgedItem){
                //System.out.println(item);
                temp.remove(item);
                if(frequItem.contains(temp)){
                temp.add(item);
                //System.out.println("1");
                }
                else {
                return true;
                }
            }
            return false;
    }
    /*
     * 通过剪枝操作进一步获得候选集
     * candiItem为待剪枝的候选集
     * frequItem为(k-1)-项频繁集
     */
    public Set<Set<String>> pruneForCandiItem(Set<Set<String>>candiItem, Set<Set<String>>frequItem)
    {
        //System.out.println(candiItem);
        Set<Set<String>> tempSet = new HashSet<Set<String>>();
        tempSet.addAll(candiItem);
        Iterator<Set<String>>it = candiItem.iterator();
        while(it.hasNext()){
            Set<String>set = it.next();
            //System.out.println(set);
            boolean flag = judgeForInvolve(set, frequItem);
            //System.out.println(flag);
            if(flag)
                tempSet.remove(set);
        }
        //System.out.println(tempSet);
        return tempSet;
    }
    /*
     * 获得剪枝后的候选集中各个项的支持度
     * candiItem为剪枝后的候选集
     */
    public Map<Set<String>, Integer> caculateSupport(Set<Set<String>>candiItem)
    {
        HashMap<Set<String>, Integer>candiItemMap=new HashMap<Set<String>, Integer>();
        Iterator<Set<String>>itForSet = candiItem.iterator();
        while(itForSet.hasNext()){
            Iterator<Set<String>>itForMap = database.values().iterator();
            Set<String>itemForSet = itForSet.next();
            Integer value=0;
            while(itForMap.hasNext()){
                if(itForMap.next().containsAll(itemForSet))
                    value++;
            }
            candiItemMap.put(itemForSet, value);
        }
        //System.out.println(candiItemMap);
        return candiItemMap;
    }
    /*
     * 根据(k-1)-频繁项集生成k-频繁项集
     * m=k-1
     * frequItem为(k-1)-频繁集 
     */
    public Map<Set<String>, Integer> genFrequItemK(int m, Map<Set<String>, Integer> frequItemSets2)
    {
        //System.out.println(frequItemSets2);
        Map<Set<String>, Integer>frequItemk = new HashMap<Set<String>, Integer>();
        Set<Set<String>> item = frequItemSets2.keySet();
        Set<Set<String>> candiItem = genCandiFrequen(m,frequItemSets2);//获得候选集
        //System.out.println(candiItem);
        Set<Set<String>> newCandiItem=pruneForCandiItem(candiItem,item);//对候选集进行剪枝

        //System.out.println(newCandiItem);
        Map<Set<String>, Integer> freshCandiItem= caculateSupport(newCandiItem);//计算候选集中各个项的支持度
        Iterator<Map.Entry<Set<String>, Integer>>it = freshCandiItem.entrySet().iterator();
        while(it.hasNext()){
            Entry<Set<String>,Integer>entry = it.next(); 
            Set<String> set = entry.getKey(); 
            Integer value = entry.getValue();
            if(value>=minSup)
                frequItemk.put(set,value);
        }

        return frequItemk;
    }
    /*
     * 获得fre中从Item开始之后的迭代器
     */
    public Iterator<Map.Entry<Set<String>, Integer>> getIteratorF(Set<String> Item,Map<Set<String>, Integer> frequItemSets2)
    {
        Iterator<Map.Entry<Set<String>, Integer>>it = frequItemSets2.entrySet().iterator();
        while(it.hasNext()){
            if(Item.equals(it.next().getKey()))
                break;
        }
        return it;
    }
    /*
     * 挖掘从1-到k-的所有频繁集
     */
    public void miningAllFreque(){
        Map<Set<String>, Integer>frequ=genFrequItemSets1();
        //System.out.println(frequ);
        int k=1;
        boolean flag = true;
        while(flag){
            frequItemSets=frequ;
            freshFrequItemSets.put(k,frequItemSets);
            frequ=genFrequItemK(k,frequItemSets);
            k++;
            if(frequ.isEmpty())
                flag = false;
        }
    }
    /*
     * 获得每一个频繁项集,并分别调用关联规则函数
     */
    public void mineWholeAssociation()
    {
        freshFrequItemSets.remove(1);
        //System.out.println(freshFrequItemSets);
        Iterator<Map.Entry<Integer, Map<Set<String>, Integer>>>itForMap = freshFrequItemSets.entrySet().iterator();
        while(itForMap.hasNext())
        {
            Entry<Integer,Map<Set<String>, Integer>> entry = itForMap.next();
            Iterator<Map.Entry<Set<String>, Integer>>itForIterate =entry.getValue().entrySet().iterator();
            while(itForIterate.hasNext())
            {
                mineSingleAssociation(itForIterate.next());
            }
        }
    }
    /*
     * 根据某一个频繁项集计算他的关联规则
     * 如果计算成功则计入关联规则中
     */
    public void mineSingleAssociation(Entry<Set<String>, Integer> factor)
    {
        //System.out.println(factor);
        Set<String> frequentSet = factor.getKey();
        Integer support = factor.getValue();
        Map<Set<String>,Set<String>> subset =divergeToSubsets(frequentSet);
        //System.out.println(subset);
        Iterator<Map.Entry<Set<String>, Set<String>>> it = subset.entrySet().iterator();
        while(it.hasNext()){
            Integer value = 0;
            Entry<Set<String>, Set<String>> entry = it.next();
            Set<String> subsetPart1 = entry.getKey();
            Set<String> subsetPart2 = entry.getValue();
            Iterator<Set<String>> itForSet = database.values().iterator(); 
            while(itForSet.hasNext()){
                Set<String> temp = itForSet.next();
                if(temp.containsAll(subsetPart1))
                {
                    value++;
                }
            }
            //System.out.println(support+" "+value);
            //System.out.println(subsetPart1+" "+subsetPart2);
            if((support.floatValue()/value.floatValue())>=minCon)
                {
                if(!(associationRules.containsKey(subsetPart1))){
                    Set<Set<String>> subsetPart2Set = new HashSet<Set<String>>();
                    subsetPart2Set.add(subsetPart2);
                    associationRules.put(subsetPart1, subsetPart2Set);
                }else{
                    associationRules.get(subsetPart1).add(subsetPart2);
                }
                }
        }
    }
    /*
     * 对于给定的一个频繁项集,将其分解成可数个两个互斥的子集
     */
    public Map<Set<String>, Set<String>> divergeToSubsets(Set<String> item)
    {
        //System.out.println(item);
        Map<Set<String>, Set<String>>subsets = new HashMap<Set<String>, Set<String>>();
        Integer count = item.size();
        Integer in=count/2;
        //System.out.println(in);
        Integer k=1;
        QueueL<Set<String>> queue = new QueueL<Set<String>>();
        Set<Set<String>> temp = new HashSet<Set<String>>();
        for(String tempIt:item){
            Set<String> tempSet = new HashSet<String>();
            tempSet.add(tempIt);
            queue.push(tempSet);
            temp.add(tempSet);
        }
        //System.out.println(queue.top());
        while(k<in){
            Set<String> tempSet = new HashSet<String>();
            tempSet.addAll(queue.pop());
            for(String iterate:item){
                if(!(tempSet.contains(iterate)))
                {
                    tempSet.add(iterate);
                    temp.add(tempSet);
                    queue.push(tempSet);
                    tempSet.remove(iterate);
                }
            }
            System.out.println(queue.top());
            k=queue.top().size();
        }
        Iterator<Set<String>> itForCombine = temp.iterator();
        while(itForCombine.hasNext()){
            //System.out.println(item);
            Set<String> setPart2 = new HashSet<String>();
            Set<String> setPart1 = new HashSet<String>();
            setPart2.addAll(item);
            setPart1.addAll(itForCombine.next());
            setPart2.removeAll(setPart1);
            //System.out.println(item);
            //System.out.println("1"+setPart1);
            //System.out.println("2"+setPart2);
            subsets.put(setPart1, setPart2);
            subsets.put(setPart2, setPart1);
            }
        //System.out.println(subsets);
        return subsets;
    }
    /*
     * 获取挖掘到的频繁项集
     */
    public Map<Integer, Map<Set<String>, Integer>> getFrequentSets()
    {
        return freshFrequItemSets;
    }
    /*
     * 获取挖掘到的关联规则
     */
    public Map<Set<String>, Set<Set<String>>> getAssociationRules()
    {
        return associationRules;
    }
}
//辅助类,在Apriori_data文件中用到了该队列结构
package test;
import java.util.LinkedList;

class QueueL { // 构造“队列”
private LinkedList list = new LinkedList();
public void push(T o) { // 队列
list.addLast(o);
}
public T top() { // 查看队列头的元素
return list.getFirst();
}
public T pop() {  // 队列
return list.removeFirst();
}
}
//测试文件
package test;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Apriori_test {
    private Apriori_data apriori;
    private MapSet> database;
    private Integer minSup = 2;
    private Float minCon = new Float("0.5");

    public static void main(String []args){
        Apriori_test test = new Apriori_test();
        //test.genFrequSet1();
        //test.genFrequSet2();
        test.genFrequSet();
        test.genAssociation();
    }


    Apriori_test(){
        database = new HashMapSet>();
        Set set1 = new HashSet();
        set1.add("A");
        set1.add("B");
        set1.add("C");
        set1.add("D");
        set1.add("E");
        database.put(1, set1);

        Set set2 = new HashSet();
        set2.add("A");
        set2.add("B");
        set2.add("F");
        database.put(2, set2);

        Set set3 = new HashSet();
        set3.add("C");
        set3.add("D");
        set3.add("E");
        database.put(3, set3);

        Set set4 = new HashSet();
        set4.add("B");
        set4.add("C");
        set4.add("D");
        set4.add("E");
        set4.add("F");
        database.put(4, set4);

        Set set5 = new HashSet();
        set4.add("C");
        set4.add("D");
        set4.add("E");
        set4.add("F");
        database.put(5, set5);

        apriori = new Apriori_data(database, minSup, minCon);
    }
    /*
     * 获得第一频繁项集
     */
    public void genFrequSet1()
    {
        //System.out.println(this.database);
        System.out.println("频繁项集1-"+apriori.genFrequItemSets1());
    }
    /*
     * 获得2-频繁项集
     */
    public void genFrequSet2()
    {
        System.out.println("频繁项集2-"+apriori.genFrequItemK(1, apriori.genFrequItemSets1()));
    }
    /*
     * 获得所有的频繁项集
     */
    public void genFrequSet()
    {
        apriori.miningAllFreque();
        System.out.println("频繁项集"+apriori.getFrequentSets());
    }
    /*
     * 获得所有的关联规则
     */
    public void genAssociation()
    {
        apriori.miningAllFreque();
        apriori.mineWholeAssociation();
        System.out.println("关联规则"+apriori.getAssociationRules());
    }
}

你可能感兴趣的:(编写历程,数据挖掘,web,java,apriori,数据挖掘,经典算法)