西瓜书 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;
}
}
浙公网安备 33010602011771号