西瓜书 4.3 决策树C4.5实现

1.定义输入输出,树的样子

输入为二维Object数组objects,object[i]代表第i个西瓜

 Object[][]objects={ {"青绿", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.697, 0.460, "好瓜"},
                {"乌黑", "蜷缩", "沉闷", "清晰", "凹陷", "硬滑", 0.774, 0.376, "好瓜"},
                {"乌黑", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.634, 0.264, "好瓜"},
                {"青绿", "蜷缩", "沉闷", "清晰", "凹陷", "硬滑", 0.608, 0.318, "好瓜"},
                {"浅白", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.556, 0.215, "好瓜"},
                {"青绿", "稍蜷", "浊响", "清晰", "稍凹", "软粘", 0.403, 0.237, "好瓜"},
                {"乌黑", "稍蜷", "浊响", "稍糊", "稍凹", "软粘", 0.481, 0.149, "好瓜"},
                {"乌黑", "稍蜷", "浊响", "清晰", "稍凹", "硬滑", 0.437, 0.211, "好瓜"},
                {"乌黑", "稍蜷", "沉闷", "稍糊", "稍凹", "硬滑", 0.666, 0.091, "坏瓜"},
                {"青绿", "硬挺", "清脆", "清晰", "平坦", "软粘", 0.243, 0.267, "坏瓜"},
                {"浅白", "硬挺", "清脆", "模糊", "平坦", "硬滑", 0.245, 0.057, "坏瓜"},
                {"浅白", "蜷缩", "浊响", "模糊", "平坦", "软粘", 0.343, 0.099, "坏瓜"},
                {"青绿", "稍蜷", "浊响", "稍糊", "凹陷", "硬滑", 0.639, 0.161, "坏瓜"},
                {"浅白", "稍蜷", "沉闷", "稍糊", "凹陷", "硬滑", 0.657, 0.198, "坏瓜"},
                {"乌黑", "稍蜷", "浊响", "清晰", "稍凹", "软粘", 0.360, 0.370, "坏瓜"},
                {"浅白", "蜷缩", "浊响", "模糊", "平坦", "硬滑", 0.593, 0.042, "坏瓜"},
                {"青绿", "蜷缩", "沉闷", "稍糊", "稍凹", "硬滑", 0.719, 0.103, "坏瓜"}
        };

树长下面这个样子

public class DecisionTree {
    List<String>label;//标签
    List<List<Object>> trainSet;//训练集
    Map<String,Integer>indexes=new HashMap<>();//标签对应的训练集的索引
    Set<String>continuousIndex=new HashSet<>();//连续值的索引
    Node root;
}

class Node{
    String name;
    List<String>edges=new ArrayList<>();//边
    List<Node>childs=new ArrayList<>();//子节点
}

2.代码实现

Main

package com.fly.tree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        Object[][]objects={ {"青绿", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.697, 0.460, "好瓜"},
                {"乌黑", "蜷缩", "沉闷", "清晰", "凹陷", "硬滑", 0.774, 0.376, "好瓜"},
                {"乌黑", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.634, 0.264, "好瓜"},
                {"青绿", "蜷缩", "沉闷", "清晰", "凹陷", "硬滑", 0.608, 0.318, "好瓜"},
                {"浅白", "蜷缩", "浊响", "清晰", "凹陷", "硬滑", 0.556, 0.215, "好瓜"},
                {"青绿", "稍蜷", "浊响", "清晰", "稍凹", "软粘", 0.403, 0.237, "好瓜"},
                {"乌黑", "稍蜷", "浊响", "稍糊", "稍凹", "软粘", 0.481, 0.149, "好瓜"},
                {"乌黑", "稍蜷", "浊响", "清晰", "稍凹", "硬滑", 0.437, 0.211, "好瓜"},
                {"乌黑", "稍蜷", "沉闷", "稍糊", "稍凹", "硬滑", 0.666, 0.091, "坏瓜"},
                {"青绿", "硬挺", "清脆", "清晰", "平坦", "软粘", 0.243, 0.267, "坏瓜"},
                {"浅白", "硬挺", "清脆", "模糊", "平坦", "硬滑", 0.245, 0.057, "坏瓜"},
                {"浅白", "蜷缩", "浊响", "模糊", "平坦", "软粘", 0.343, 0.099, "坏瓜"},
                {"青绿", "稍蜷", "浊响", "稍糊", "凹陷", "硬滑", 0.639, 0.161, "坏瓜"},
                {"浅白", "稍蜷", "沉闷", "稍糊", "凹陷", "硬滑", 0.657, 0.198, "坏瓜"},
                {"乌黑", "稍蜷", "浊响", "清晰", "稍凹", "软粘", 0.360, 0.370, "坏瓜"},
                {"浅白", "蜷缩", "浊响", "模糊", "平坦", "硬滑", 0.593, 0.042, "坏瓜"},
                {"青绿", "蜷缩", "沉闷", "稍糊", "稍凹", "硬滑", 0.719, 0.103, "坏瓜"}
        };
        List<List<Object>>trainSet=new ArrayList<>();
        List<String>label=new ArrayList<>();
        for(Object[] objs :objects){
            List<Object> list = Arrays.asList(objs);
            trainSet.add(list);
        }
        String[]strings={"色泽","根蒂","敲声","纹理","脐部","触感","密度","含糖率","好瓜"};
        label=Arrays.asList(strings);
        DecisionTree decisionTree=new DecisionTree(label,trainSet,"密度","含糖率");
        decisionTree.treeGenerate();
        decisionTree.show();

    }
}

Tree

package com.fly.tree;

import java.util.*;

public class DecisionTree {
    List<String>label;//标签
    List<List<Object>> trainSet;//训练集
    Map<String,Integer>indexes=new HashMap<>();//标签对应的训练集的索引
    Set<String>continuousIndex=new HashSet<>();//连续值的索引
    Node root;
    public DecisionTree(List<String> label, List<List<Object>> trainSet,String ...a){
        this(label,trainSet);
        for(String i:a){
            continuousIndex.add(i);
        }
    }
    public DecisionTree(List<String> label, List<List<Object>> trainSet) {
        this.label = label;
        this.trainSet = trainSet;
        for(int i=0;i<label.size();i++){
            indexes.put(label.get(i),i);
        }

    }
    public void treeGenerate() throws Exception {
        root=treeGenerate(trainSet,label);
    }

    public Node treeGenerate(List<List<Object>> set,List<String>label) throws Exception {
        //如果只有一个类
        if(isOneClass(set)){
            List<Object> list = set.get(0);
            return new Node(list.get(list.size()-1).toString());
        }
        //如果属性集合为空,在该属性上只有一个值
        if(label.size()==0||isOneValue(set,label)){
            return new Node(getMostValue(set));
        }
        String split=getBestSplit( set,label);
        Node node=new Node(split);
        String[] splits = split.split("<=");
        //如果是连续属性
        if(continuousIndex.contains(splits[0])){
            List<List<Object>>[] setByContinuousValue = getSetByContinuousValue(set, splits[0], Double.parseDouble(splits[1]));
            node.addChild(treeGenerate(setByContinuousValue[0],label),"是");
            node.addChild(treeGenerate(setByContinuousValue[1],label),"否");
        }else{
            Set<Object> values = getValues(set, split);
            for(Object value:values){
                List<List<Object>> setByValue = getSteByValue(set, split, value);
                if(setByValue.size()==0){
                    return  new Node(getMostValue(set));
                }
                List<String>childLabel=new ArrayList<>(label);
                childLabel.remove(split);
                node.addChild(treeGenerate(setByValue,childLabel),value.toString());

            }
        }

        return node;
    }



    //打印树
    public void show(){
        if(root==null){
            System.out.println("tree is empty");
        }
        show(root);
    }

    public void show(Node node){


        if(node.childs.size()>0){
            System.out.println(node);
            System.out.println("\n\n----------------------------------------------");
            for(Node child:node.childs){
                show(child);
            }
        }
    }

    //获取最好的划分属性
    public String getBestSplit(List<List<Object>> set, List<String> labels) {
        double t=gain(set,labels.get(0));
        String value=labels.get(0);
        double[] splitValue=new double[1];
        for(int i=0;i<labels.size()-1;i++){
            String label = labels.get(i);
            double gain;
            if(continuousIndex.contains(label)){
                 gain=continuousGain(set,label,splitValue);
            }else{
                gain = gain(set, label);
            }

            if(t<gain){
                t=gain;
                value=label;
            }
        }
        if(continuousIndex.contains(value)){
            continuousGain(set,value,splitValue);//重新给splitValue赋值
            return value+"<= "+splitValue[0];
        }
        return value;
    }

    //信息增益
    public double gain(List<List<Object>>set,String label){
        if(continuousIndex.contains(label)){
            double[]a=new double[1];
            return continuousGain(set,label,a);
        }
        double gain = ent(set);
        Set<Object> values = getValues(set, label);
        for(Object o:values){
            List<List<Object>> setByValue = getSteByValue(set, label, o);
            double ent = ent(setByValue);
            gain-=setByValue.size()*1.0/set.size()*ent;
        }
        return gain;
    }

    //获取连续值的信息增益
    public double continuousGain(List<List<Object>> set, String label,double[] splitValue) {
        Double[] candidate =getCandidate(set, label);
        Double a=candidate[0];
        double gain=gain(set, label,a);
        for (Double aDouble : candidate) {
            double t = gain(set, label, aDouble);
            if (t > gain) {
                gain = t;
                a = aDouble;
            }
        }
        splitValue[0]=a;

        return  gain;
    }

    //以a为划分连续值的信息增益
    public double gain(List<List<Object>> set,String label,Double a){
        List<List<Object>> set1=new ArrayList<>();//小于a的子集
        List<List<Object>> set2=new ArrayList<>();//大于a的子集
        for(List<Object>entity:set){
            Integer index = indexes.get(label);
            Double value = (Double)entity.get(index);
            if(value<=a){
                set1.add(entity);
            }else{
                set2.add(entity);
            }
        }

        return  ent(set)-set1.size()*1.0/set.size()*ent(set1)-set2.size()*1.0/set.size()*ent(set2);
    }

    //获取连续属性的候选值
    public Double[] getCandidate(List<List<Object>> set, String label){
        Set<Object> values = getValues(set, label);
        Object[] objects = values.toArray();
        Arrays.sort(objects);
        Double[] res=new Double[objects.length-1];
        for(int i=0;i<res.length;i++){
            res[i]=((Double)objects[i]+(Double) objects[i+1])/2.0;
        }
        return res;
    }

    //获取集合中某个属性为某个值的子集合
    public List<List<Object>> getSteByValue(List<List<Object>>set,String label,Object value){
        Integer index = indexes.get(label);
        List<List<Object>> childSet=new ArrayList<>();
        for(List<Object>entity:set){
            Object o = entity.get(index);
            if(o.equals(value)){
                childSet.add(entity);
            }
        }
        return childSet;

    }

    //获取集合以连续值为划分的子集合
    public  List<List<Object>>[] getSetByContinuousValue(List<List<Object>>set,String label,double a){
        List<List<Object>> set1=new ArrayList<>();//小于a的子集
        List<List<Object>> set2=new ArrayList<>();//大于a的子集
        List<List<Object>>[] childSet=new ArrayList[2];
        childSet[0]=set1;
        childSet[1]=set2;
        for(List<Object>entity:set){
            Integer index = indexes.get(label);
            Double value = (Double)entity.get(index);
            if(value<=a){
                set1.add(entity);
            }else{
                set2.add(entity);
            }
        }
        return childSet;

    }

    //获取某个属性的值的集合
    public Set<Object> getValues(List<List<Object>>set1,String label){
        Integer index = indexes.get(label);
        Set<Object>set=new HashSet<>();
        for(List<Object>list:set1){
            Object o = list.get(index);
            set.add(o);
        }
        return set;
    }

    //获取信息熵
    public double ent(List<List<Object>>set){
        Map<Object,Integer>map=new HashMap<>();
        int num=set.size();
        double ans=0;
        for(List<Object> list:set){
            Object clazz = list.get(list.size() - 1);
            if(map.containsKey(clazz))map.put(clazz,map.get(clazz)+1);
            else map.put(clazz,1);
        }
        Collection<Integer> values = map.values();
        for(Integer value:values){
            ans+=value*1.0/num*(Math.log(value*1.0/num)/Math.log(2));
        }
        return -ans;
    }

    //获取集合中最多的类
    public String getMostValue(List<List<Object>> set) throws Exception {
        Map<Object,Integer>map=new HashMap<>();
        for(List<Object>list:set){
            Object clazz = list.get(list.size() - 1);
            if(map.containsKey(clazz))map.put(clazz,map.get(clazz)+1);
            else map.put(clazz,1);
        }
        Integer max = Collections.max(map.values());
        for(Map.Entry<Object,Integer> entry:map.entrySet()){
            if(max.equals(entry.getValue())){
                return entry.getKey().toString();
            }
        }
        throw new Exception("getMostValue error");

    }

    //判断集合是否全属于同一类别
    public boolean isOneClass(List<List<Object>> set) {
        Set<Object> set1=new HashSet<Object>();
        for(List<Object>list:set){
            set1.add(list.get(list.size()-1));
        }
        return set1.size() == 1;
    }

    //判断集合在label标签上的取值是否相同
    public boolean isOneValue(List<List<Object>> set, List<String> label) {
        Set<String>set1=new HashSet<>();
        for (List<Object> list:set){
            StringBuilder sb=new StringBuilder("");
            for (String s:label){
                Integer integer = indexes.get(s);
                Object o=list.get(integer);
                sb.append("#").append(o.toString());
            }
            set1.add(sb.toString());
        }
        return set1.size()==1;
    }
}

class Node{
    String name;
    List<String>edges=new ArrayList<>();//边
    List<Node>childs=new ArrayList<>();//子节点
    public Node(String name){
        this.name=name;
    }
    public Node(){};
    public void addChild(Node node,String edge){
        childs.add(node);
        edges.add(edge);
    }
    @Override
    public String toString(){
        if(childs.size()==0)return  name;
        String s=name+"\nedges: "+edges+"\nchilds";
        for(Node node:childs){
            s=s+node.name+" ";
        }
        return  s;
    }

}
posted on 2020-12-17 10:46  计网好难  阅读(469)  评论(0)    收藏  举报