淘先锋技术网

首页 1 2 3 4 5 6 7

Data mining数据挖掘——FPTree

定义就不解释了,这里只说思路和代码。

以下是我对FPtree算法的个人见解,欢迎指正,共勉。

思路

FPtree是为了解决apriori频繁访问数据库的缺点,它主要有两个过程:1、build-FPtree、2、growth-FPtree

以一个例子来介绍它的思想,事务库初始如下,最小支持度的阈值是2:

a b e 
b d 
b c 
a b d 
a c 
b c 
a c 
a b c e 
a b c 

一、build-FPtree

准备工作:

1、首先我们对出现的每个item进行统计其支持度(相当于频繁1-项目集),并删除小于阈值的item,最后对其进行排序:

b=====>7
c=====>6
a=====>6
e=====>2
d=====>2

2、利用1)的结果对原始数据做两点操作:删除非频繁1-项目集元素和按照支持度重新对每条事务进行排序:

排序整理后的项目集:
b a e 
b d 
b c 
b a d 
c a 
b c 
c a 
b c a e 
b c a 
建FPtree

在这里插入图片描述

总的一条就是共前缀!例如bae和bad就可以共ba,这样它们的计数为b2、a2、d1;但是bca和ca就不能共前缀!!!

二、growth-FPtree

核心思想:
FP-growth(tree,a)
{
if(tree只包含单个路径){
     对于路径中的所有结点,找到它们组合的所有情况,对于每种情况(B),将a并入到其中,此时每种情况就是一个频繁项目,它的支持度为B中结点的最小支持度,将每个B(含有a的B)加入到频繁项目集中
      }
else{
       if(a(后缀式)!=null){
       将a与分别与此时项头表中的每个元素进行组合,并加入到频繁项目集中,其支持度为项头表中元素的支持度
       }
       
       for each ai 在FP-tree的项表头(倒序) do begin
       {
       (1)产生一个模式B=(ai并a),a为null,则将它视为空字符串
       (2)构造B的条件模式基,然后构造B的条件FP-tree TreeB
       (3)if TreeB != null 
              {
               FP-growth(TreeB,B)
              }
       }
       
    }
}

初看会觉得很复杂难懂,将下面例子看完后,再来看这个伪代码,会有意外收获!

1、第一次调用growth-FPtree的时候,后缀式a为空,于是找到项头表的最后一个元素d,找到它的条件模式基,所谓的条件模式基,就是沿着每个d结点,往上找,直到root结点,其中路径经过的结点组合在一起就为d的一个条件模式基(不含d),这个条件模式基(不含d)的support就是d的support,本例题d的条件模式基为:{(ba,1)(b,1)}

构建d的条件FPtree,这个过程相当于将条件模式基看成一个新的事务库,对其进行build-FPtree过程,先删除非频繁项目集a,只有一个元素b所以就不用排序,得到:

在这里插入图片描述

再对其进行growth-FPtree过程(此时的后缀式为元素d),由于它是单路径的,所以根据伪代码,将单路径仅有的元素b与后缀式组合得到一个频繁项目,(bd)

2、处理表头项的倒数第二个元素e,此时还是属于第一次调用growth-FPtree的时候,后缀式还是空;

找到e的条件模式基:{(ba,1)(bca,1)}

构造条件FPtree:

在这里插入图片描述

再对其进行growth-FPtree过程(此时的后缀式为元素d),由于它是单路径的,所以根据伪代码,将单路径上的所有结点进行组合(b、a、ba)并分别再与后缀式组合得到频繁项目集,{ be ae bae}

3、处理表头项的倒数第三个元素a,此时还是属于第一次调用growth-FPtree的时候,后缀式还是空;

找到a的条件模式基:{(b,2)(c,2)(bc,2)}

构造条件FPtree:

在这里插入图片描述

再对其进行growth-FPtree过程(此时的后缀式为元素a),它不是单路径并且后缀式不为空,根据伪代码,将后缀式a与项表头的每个元素进行组合(ba、ca),并将其加入到频繁项目集中;

此时这部分并没有结束,还得对上项表头中的元素进行build-FPtree过程,

1)找到c的条件模式基{(b,2) },构建FPtree:

在这里插入图片描述

对其进行growth-FPtree,(此时的后缀式为ca),它是单路径且后缀式不为空,根据伪代码,得到组合(bca,2),将其加入到频繁项目集中;

2)找到b的条件模式基{ },发现它为空,不做处理;

4、…

注意:后面的d、e与b、c类似。a才是重点,理解了a的处理过程才是对FPtree算法的真正理解!!!还有最重要的一点,表头项顺序不一样(支持度相等的元素的顺序),过程结果会有差异,但是最后的频繁项目集几乎都是一样的!!!!!!!!!!!!!!!!!!!!!

代码

本代码用java编写,输入方式采用的是读文件的形式,本菜鸟编码能力有限,可能繁化了一些操作,(最后我也不想再对代码进行优化了…个人觉得这个算法的编码还是有点难度的!!!肝了我五六个小时,这也足以证明我有多菜了吧哈哈)

package myDataMining;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.*;

public class myFPtree {
//    每个结点的信息,自定义的数据结构
    public static class TreeNode{
        // 节点类别名称
        private String name;
        // 计数数量
        private Integer count;
        // 父亲节点
        private TreeNode parentNode;
        // 孩子节点,可以为多个
        private ArrayList<TreeNode> childNodes=new ArrayList<>();




        private TreeNode left;

        public TreeNode(){

        }

        public TreeNode(String name,int count){
            this.name=name;
            this.count=count;
        }


    }



//定义的一些全局变量
//    记录所有的事务
    public static List<List<String>> record=new ArrayList<List<String>>();
    //    事物总条数
    public static Integer totalT;
//    最小支持数的阈值
    public static double MIN_SUPPORT;
//   记录每个item出现的次数,以便得到orderedRecord
   public static Map<String,Integer> ItemsCount =new HashMap<String,Integer>();
//    排序后的项目集
//  项表头
    public static List<TreeNode> form;
//    记录过程中产生的频繁集
    public static Map<String,Integer> frequentSet=new HashMap<>();
    //


   private static List<List<String>> orderedRecord;



    //从文件夹读数据,每次读一行存储
    public static List<List<String>> getRecord(String url){

        List<List<String>> record = new ArrayList<List<String>>();
        try {
            String encoding = "UTF-8";
            File file = new File(url);
            if (file.isFile() && file.exists()) {
                InputStreamReader read = new InputStreamReader(
                        new FileInputStream(file), encoding);
                BufferedReader bufferedReader = new BufferedReader(read);
                String lineTXT = null;
                int t=0;
                while ((lineTXT = bufferedReader.readLine()) != null) {//读一行文件
                    t++;
                    String[] lineString = lineTXT.split(" ");
                    List<String> lineList = new ArrayList<String>();
                    for (int i = 0; i < lineString.length; i++) {
                        lineList.add(lineString[i]);

                        if(ItemsCount.containsKey(lineString[i])){
                            Integer count = ItemsCount.get(lineString[i]);
                            ItemsCount.put(lineString[i],++count);
                        }
                        else {
                            ItemsCount.put(lineString[i],1);
                        }
                    }
                    record.add(lineList);
                }
                totalT=t;
                read.close();
            } else {
                System.out.println("找不到指定的文件!");
            }
        } catch (Exception e) {
            System.out.println("读取文件内容操作出错");
            e.printStackTrace();
        }
        return record;
    }


    public static void main(String[] args){
//        C:\Users\LUOJUN\Desktop\test
        System.out.println("请输入最小支持度:");
        Scanner scanner = new Scanner(System.in);
        MIN_SUPPORT=scanner.nextDouble();

        System.out.println("请输入待处理的文件地址:");
        String url = scanner.next();
        record = getRecord(url);
        if(record.isEmpty())
        {
            System.out.println("程序结束!!!");
            return;
        }else {
            System.out.println("读取数据成功!!!!!!!!!总共有"+totalT+"条数据");
            for (List<String> stringList : record) {
                for (String s : stringList) {
                    System.out.print(s+" ");
                }
                System.out.println();
            }


            System.out.println("当前最小支持度的阈值为"+MIN_SUPPORT+";现在对小于阈值的item进行处理,结果如下");
            Iterator<Map.Entry<String, Integer>> iterator = ItemsCount.entrySet().iterator();
            while (iterator.hasNext()){
                Map.Entry<String, Integer> next = iterator.next();
                if(next.getValue()<MIN_SUPPORT){
                    iterator.remove();
                }
            }
            for(Map.Entry<String,Integer> entry:ItemsCount.entrySet()){
                System.out.println(entry.getKey()+"===>"+entry.getValue());
            }

//            排序整理后的项目集


            TreeNode root=new TreeNode();
           form= buildFPtree(root,record,ItemsCount);
            System.out.println("下面对出现过的item进行排序:");
            for (TreeNode treeNode : form) {
                System.out.println(treeNode.name+"=====>"+treeNode.count);
            }
            System.out.println("排序整理后的项目集:");
            for (List<String> stringList : orderedRecord) {
                for (String s : stringList) {
                    System.out.print(s+" ");
                }
                System.out.println();
            }


           FPgrowth(root,null,form);
           System.out.print("频繁项目集为:{");
           for(Map.Entry<String,Integer> entry:frequentSet.entrySet()){
               System.out.print(" "+entry.getKey());
           }
           System.out.println("}");
           System.out.println("======================================");


        }
    }

    private static void FPgrowth(TreeNode root, String o,List<TreeNode> form) {
//        如果tree只有一条路径,则将form表头项中的所有元素与o组合加入到频繁项目集中
        if(TreeOnlyOnePath(root)){
            System.out.print("后缀式"+o+"的频繁项目集有:{");
            ArrayList<String> strings = new ArrayList<>();
            String s="";
            for(int i=0;i<form.size();i++){
                s=s+form.get(i).name;
            }
            String s1="";
            strings.add(s1);
            for(int i=0;i<s.length();i++){
                int size = strings.size();
                for(int j=0;j<size;j++){
                    String s2 = strings.get(j);
                    strings.add(s2+s.charAt(i));
                }
            }
           strings.remove(0);
            for (String string : strings) {
                int last = string.length() - 1;
                String c = String.valueOf(string.charAt(last));
                int count=0;
                for (TreeNode treeNode : form) {
                    if(treeNode.name.equals(c)){
                        count=treeNode.count;
                    }
                }
                System.out.print(" "+string+o);
                frequentSet.put(string+o,count);
            }

            System.out.println("}");

            System.out.println("======================================");
        }
//        如果tree不是单路径
        else {
            if(o==null){
//                第一次进入这个方法没有后缀式
                o="";
            }
            else {
//                有后缀式,并将其与form表头项中的元素组合加入到频繁集中
                System.out.print("后缀式"+o+"的频繁项目集有:{");
                for (TreeNode treeNode : form) {
                    frequentSet.put(treeNode.name+o,treeNode.count);
                    System.out.print(" "+treeNode.name+o);
                }
                System.out.println("}");
            }
//            tree有多路径,所以需要继续挖掘,条件模式相当于源数据,基于条件模式要继续建树,直到tree为单路径
                for(int i=form.size()-1;i>=0;i--){
                    TreeNode formNode = form.get(i);
                    TreeNode p=formNode.left;
                    Map<ArrayList<String>,Integer> conditionPatternSet = new HashMap<>();

                    while (p!=null){
                        ArrayList<String> list = new ArrayList<>();

                        TreeNode q=p.parentNode;
                        Stack<String> stack = new Stack<>();
                        while (q.parentNode!=null){
                            stack.push(q.name);
                            q=q.parentNode;
                        }
                        while (!stack.isEmpty()){
                            list.add(stack.pop());
                        }
                        if(list.size()!=0)
                        conditionPatternSet.put(list,p.count);
                        p=p.left;
                    }
                    HashMap<String, Integer> itemcount = new HashMap<String, Integer>();
                    System.out.println("=====================================================");
                    System.out.print("元素"+form.get(i).name+o+"的条件模式基为:{");
                    for(Map.Entry<ArrayList<String>,Integer> entry:conditionPatternSet.entrySet()){
                        ArrayList<String> key = entry.getKey();

                        System.out.print("(");
                        for (String s : key) {
                            if(itemcount.containsKey(s)){
                                Integer integer = itemcount.get(s);
                                itemcount.put(s,integer+entry.getValue());
                            }else {
                                itemcount.put(s, entry.getValue());
                            }
                            System.out.print(s);
                        }
                        System.out.print(","+entry.getValue()+")");
                    }
                    System.out.println("}");

//                    构造条件FPtree
//                    删除小于min——support的item
                    System.out.println("当前最小支持度的阈值为"+MIN_SUPPORT+";现在对小于阈值的item进行处理,结果如下");
                    Iterator<Map.Entry<String, Integer>> iterator = itemcount.entrySet().iterator();
                    while (iterator.hasNext()){
                        Map.Entry<String, Integer> next = iterator.next();
                        if(next.getValue()<MIN_SUPPORT){
                            iterator.remove();
                        }
                    }
                    for(Map.Entry<String,Integer> entry:itemcount.entrySet()){
                        System.out.println(entry.getKey()+"===>"+entry.getValue());
                    }
                    List<List<String>> orderedRecord= new ArrayList<List<String>>();

                    Iterator<Map.Entry<ArrayList<String>, Integer>> iterator1 = conditionPatternSet.entrySet().iterator();
                    while (iterator1.hasNext()){
                        Map.Entry<ArrayList<String>, Integer> next = iterator1.next();
                        Iterator<String> iterator2 = next.getKey().iterator();
                        while (iterator2.hasNext()){
                            String next1 = iterator2.next();
                            if(!itemcount.containsKey(next1)){
                                iterator2.remove();
                            }
                        }

                    }
                    for(Map.Entry<ArrayList<String>,Integer> entry:conditionPatternSet.entrySet()){
                        Integer j = entry.getValue();
                        for(int k=1;k<=j;k++){
                            orderedRecord.add(entry.getKey());
                        }
                    }

                    TreeNode root1=new TreeNode();

                    List<TreeNode> formNodes = buildFPtree(root1, orderedRecord, itemcount);

                    if (formNodes.size()==0){
                        System.out.println("项目"+form.get(i).name+o+"的条件FPtree为空!!");
//                        System.out.println("======================================");
                    }
                    else {
                        FPgrowth(root1,form.get(i).name+o,formNodes);
                    }


                }




        }
    }
//  判断是不是单路经树
    private static boolean TreeOnlyOnePath(TreeNode root) {
        TreeNode p=root;
        while (p!=null){
            if(p.childNodes.size()!=1){
                return false;
            }

            p=p.childNodes.get(0);
            if(p.childNodes.size()==0)
                break;
        }
        return true;
    }
//建FPtree
    private static List<TreeNode> buildFPtree(TreeNode root, List<List<String>> record, Map<String, Integer> itemsCount) {
        ArrayList<TreeNode> curForm = new ArrayList<>();
        for(Map.Entry<String,Integer> entry:itemsCount.entrySet()){
            TreeNode treeNode = new TreeNode();
            treeNode.name=entry.getKey();
            treeNode.count=entry.getValue();
            curForm.add(treeNode);
        }

        for(int i=0;i< curForm.size()-1;i++){
            for(int j=i+1;j<curForm.size();j++){
                if(curForm.get(j).count>=curForm.get(i).count){
                    TreeNode t=curForm.get(j);
                    curForm.set(j,curForm.get(i));
                    curForm.set(i,t);
                }
            }
        }

        List<List<String>> orderedRecord=  getOrderedRecord(record,curForm);
        myFPtree.orderedRecord=orderedRecord;

        for (List<String> strings : orderedRecord) {
                 TreeNode p=root;

                if(root.childNodes.size()==0){

                    for (String string : strings) {
                      TreeNode node=  new TreeNode(string,1);
                        TreeNode formNode= query(curForm,node);
                        formNode.left=node;
                        node.parentNode=p;
                        p.childNodes.add(node);
                        p=node;
                    }

                }

                else {
                    for (String string : strings) {
                       TreeNode a= childNodesHasString(p,string);
                        if(a==null){
                          TreeNode b=  new TreeNode(string,1);
                            TreeNode query = query(curForm, b);

                            if(query.left!=null){
                                while (query.left!=null){
                                    query=query.left;
                                }
                            }
                            query.left=b;
                            b.parentNode=p;
                            p.childNodes.add(b);
                            p=b;

                        }else {
                            Integer count = a.count;
                            a.count=count+1;
                            p=a;
                        }
                    }


                }
            }



        return curForm;

    }
//判断string是否在结点p中的孩子结点中
    private static TreeNode childNodesHasString(TreeNode p, String string) {

        ArrayList<TreeNode> childNodes = p.childNodes;
        for (TreeNode childNode : childNodes) {
            if(childNode.name.equals(string)){
                return childNode;
            }
        }
        return null;
    }
// 返回的是项头表中与node的name相同的结点
    private static TreeNode query(ArrayList<TreeNode> curForm, TreeNode node) {

        TreeNode p=node;
        for (TreeNode treeNode : curForm) {
            if(treeNode.name.equals(node.name)){
                p= treeNode;
            }
        }
        return p;
    }

//将事务根据item的频繁数进行排序
    private static List<List<String>> getOrderedRecord(List<List<String>> record, ArrayList<TreeNode> curForm) {

        List<List<String>> orderedRecord= new ArrayList<List<String>>();
        for (List<String> strings : record) {
            for(int i=0;i<strings.size()-1;i++){
                for(int j=0;j<strings.size()-1-i;j++){
                    if(getFormIndex(curForm,strings.get(j+1))<getFormIndex(curForm,strings.get(j))){
                       String s= strings.get(j);
                       strings.set(j, strings.get(j+1));
                       strings.set(j+1,s);
                    }
                }
            }
        }



        for (List<String> strings : record) {
            ArrayList<String> list = new ArrayList<>();
            for (String string : strings) {
                if(curFormHas(curForm,string)){
                    list.add(string);
                }
            }
            orderedRecord.add(list);
        }
        return orderedRecord;

    }

    private static boolean curFormHas(ArrayList<TreeNode> curForm, String string) {
        for (TreeNode treeNode : curForm) {
            if(treeNode.name.equals(string)){
                return true;
            }
        }
        return false;
    }

    private static int getFormIndex(ArrayList<TreeNode> curForm, String s) {

        for (TreeNode treeNode : curForm) {
            if(treeNode.name.equals(s)){
                return curForm.indexOf(treeNode);
            }
        }
        return 0;

    }


}
//    a b e
//        b d
//        b c
//        a b d
//        a c
//        b c
//        a c
//        a b c e
//        a b c
urForm, String string) {
        for (TreeNode treeNode : curForm) {
            if(treeNode.name.equals(string)){
                return true;
            }
        }
        return false;
    }

    private static int getFormIndex(ArrayList<TreeNode> curForm, String s) {

        for (TreeNode treeNode : curForm) {
            if(treeNode.name.equals(s)){
                return curForm.indexOf(treeNode);
            }
        }
        return 0;

    }


}
//    a b e
//        b d
//        b c
//        a b d
//        a c
//        b c
//        a c
//        a b c e
//        a b c