`

数据挖掘中 决策树算法实现——Bash

阅读更多

 

一、决策树简介:

 

关于决策树,几乎是数据挖掘分类算法中最先介绍到的。

决策树,顾名思义就是用来做决定的树,一个分支就是一个决策过程。

 

每个决策过程中涉及一个数据的属性,而且只涉及一个。然后递归地,贪心地直到满足决策条件(即可以得到明确的决策结果)。

 

决策树的实现首先要有一些先验(已经知道结果的历史)数据做训练,通过分析训练数据得到每个属性对结果的影响的大小,这里我们通过一种叫做信息增益的理论去描述它,期间也涉及到的概念。也可参考文章信息增益与熵.

 

下面我们结合实例说一下决策树实现过程中的上述关键概念:

 

假设我们有如下数据:

 

age job house credit class
1 0 0 1 0
1 0 0 2 0
1 1 0 2 1
1 1 1 1 1
1 0 0 1 0
2 0 0 1 0
2 0 0 2 0
2 1 1 2 1
2 0 1 3 1
2 0 1 3 1
3 0 1 3 1
3 0 1 2 1
3 1 0 2 1
3 1 0 3 1
3 0 0 1 0

(一)

我们首先要通过计算找到哪个属性的所有属性值能更好地表达class字段的不同。通过计算,我们发现house的属性值最能表现class字段的不同。这个衡量标准其实就是信息增益。计算方法是:首先计算全部数据的,然后除class之外的其他属性逐个遍历,找到最小的那个属性(house),然后将全部数据的减去按照house属性划分数据之后的数据的

 

这个值如果满足条件假如(>0.1),我们认为数据应该按照这个节点进行分裂,也就是说这个属性(house)构成了我们的一次决策过程。

 

(二)

然后

在按照house分裂的每个数据集上,针对其他属性(house除外)进行与(一)相同的过程,直到信息增益不足以满足数据分裂的条件。

 

这样,我们就得到了一个关于属性数据划分的一棵树。可以作为class字段未知的数据的决策依据。

 

 

二、决策树代码实现:

 

具体计算代码如下:---假设上述数据我们保存为descision.dat文件,以及需要bash4.0及以上支持运行。

 

#!/home/admin/bin/bash_bin/bash_4

input=$1;

if [ -z $input ]; then
    echo "please input the traning file";
    exit 1;
fi 

## pre calculate the log2 value for the later calculate operation
declare -a log2;
logi=0;
records=$(cat $input | wc -l);
for i in `awk -v n=$records 'BEGIN{for(i=1;i<n;i++) print log(i)/log(2);}'`
do
    ((logi+=1));
    log2[$logi]=$i;
done


## function for calculating the entropy for the given distribution of the class
function getEntropy {
    local input=`echo $1`;
    if [[ $input == *" "* ]]; then
        local current_entropy=0;
        local sum=0;
        local i;
        for i in $input
        do
            ((sum+=$i));
            current_entropy=$(awk -v n=$i -v l=${log2[$i]} -v o=$current_entropy 'BEGIN{print n*l+o}');
        done
        current_entropy=$(awk -v n=$current_entropy -v b=$sum -v l=${log2[$sum]} 'BEGIN{print n/b*-1+l;}')
        eval $2=$current_entropy;
    else
        eval $2=0;
    fi
}


### the header title of the input data
declare -A header_info;
header=$(head -1 $input);
headers=(${header//,/ })
length=${#headers[@]};
for((i=0;i<length;i++))
do
    attr=${headers[$i]};
    header_info[$attr]=$i;
done



### the data content of the input data
data=${input}_dat;
sed -n '2,$p' $input > $data



## use an array to store the information of a descision tree
## the node structure is {child,slibling,parent,attr,attr_value,leaf,class}
## the root is a virtual node with none used attribute
## only the leaf node has class flag and the "leaf,class" is meaningfull
## the descision_tree
declare -a descision_tree;

## the root node with no child\slibing and anythings else
descision_tree[0]="0:0:0:N:N:0:0";


## use recursive algrithm to build the tree 
## so we need a trace_stack to record the call level infomation
declare -a trace_stack;

## push the root node into the stack
trace_stack[0]=0;
stack_deep=1;

## begin to build the tree until the trace_stack is empty
while [ $stack_deep -ne 0 ]
do
    ((stack_deep-=1));
    current_node_index=${trace_stack[$stack_deep]};
    current_node=${descision_tree[$current_node_index]};
    current_node_struct=(${current_node//:/ });

    ## select the current data set 
    ## get used attr and their values
    attrs=${current_node_struct[3]};
    attrv=${current_node_struct[4]};

    declare -a grepstra=();

    if [ $attrs != "N" ];then
        attr=(${attrs//,/ });
        attrvs=(${attrv//,/ });
        attrc=${#attr[@]};
        for((i=0;i<attrc;i++))
        do
            a=${attr[$i]};
            index=${header_info[$a]};
            grepstra[$index]=${attrvs[$i]};
        done
    fi

    for((i=0;i<length;i++))
    do
        if [ -z ${grepstra[$i]} ]; then
            grepstra[$i]=".*";
        fi
    done
    grepstrt=${grepstra[*]};
    grepstr=${grepstrt// /,};
    grep $grepstr $data > current_node_data

    ## calculate the entropy before split the records
    entropy=0;
    input=`cat current_node_data | cut -d "," -f 5 | sort | uniq -c | sed 's/^ \+//g' | cut -d " " -f 1`
    getEntropy "$input" entropy;

    ## calculate the entropy for each of the rest attrs
    ## and select the min one
    min_attr_entropy=1; 
    min_attr_name="";
    min_attr_index=0;
    for((i=0;i<length-1;i++))
    do
        ## just use the rest attrs
        if [[ "$attrs" != *"${headers[$i]}"* ]]; then
            ## calculate the entropy for the current attr
            ### get the different values for the headers[$i]
            j=$((i+1));
            cut -d "," -f $j,$length current_node_data > tmp_attr_ds
            dist_values=`cut -d , -f 1 tmp_attr_ds | sort | uniq -c | sed 's/^ \+//g' | sed 's/ /,/g'`;
            totle=0;
            totle_entropy_attr=0;
            for k in $dist_values
            do
                info=(${k//,/ });
                ((totle+=${info[0]}));
                cur_class_input=`grep "^${info[1]}," tmp_attr_ds | cut -d "," -f 2 | sort | uniq -c | sed 's/^ \+//g' | cut -d " " -f 1`
                cur_attr_value_entropy=0;
                getEntropy "$cur_class_input" cur_attr_value_entropy;
                totle_entropy_attr=$(awk -v c=${info[0]} -v e=$cur_attr_value_entropy -v o=$totle_entropy_attr 'BEGIN{print c*e+o;}');
            done
            attr_entropy=$(awk -v e=$totle_entropy_attr -v c=$totle 'BEGIN{print e/c;}');
            if [ $(echo "$attr_entropy < $min_attr_entropy" | bc) = 1 ]; then
                min_attr_entropy=$attr_entropy;
                min_attr_name="${headers[$i]}";
                min_attr_index=$j;
            fi
        fi
    done

    ## calculate the gain between the original entropy of the current node 
    ## and the entropy after split by the attribute which has the min_entropy
    gain=$(awk -v b=$entropy -v a=$min_attr_entropy 'BEGIN{print b-a;}');

    ## when the gain is large than 0.1 and  then put it as a branch
    ##      and add the child nodes to the current node and push the index to the trace_stack
    ## otherwise make it as a leaf node and get the class flag
    ##      and do not push trace_stack
    if [ $(echo "$gain > 0.1" | bc)  = 1 ]; then
        ### get the attribute values
        attr_values_str=`cut -d , -f $min_attr_index current_node_data | sort | uniq`;
        attr_values=($attr_values_str);

        ### generate the node and add to the tree and add their index to the trace_stack
        tree_store_length=${#descision_tree[@]};
        current_node_struct[0]=$tree_store_length;
        parent_node_index=$current_node_index;
       
        attr_value_c=${#attr_values[@]};
        for((i=0;i<attr_value_c;i++))
        do
            tree_store_length=${#descision_tree[@]};
            slibling=0;
            if [ $i -lt $((attr_value_c-1)) ]; then
                slibling=$((tree_store_length+1));
            fi

            new_attr="";
            new_attrvalue="";
            if [ $attrs != "N" ]; then
                new_attr="$attrs,$min_attr_name";
                new_attrvalue="$attrv,${attr_values[$i]}";
            else
                new_attr="$min_attr_name";
                new_attrvalue="${attr_values[$i]}";
            fi
            new_node="0:$slibling:$parent_node_index:$new_attr:$new_attr_value:0:0";
            descision_tree[$tree_store_length]="$new_node";
            trace_stack[$stack_deep]=$tree_store_length;
            ((stack_deep+=1));
        done
        current_node_update=${current_node_struct[*]};
        descision_tree[$current_node_index]=${current_node_update// /:};
    else   ## current node is a leaf node 
        current_node_struct[5]=1;
        current_node_struct[6]=`cut -d , -f $length current_node_data | sort | uniq -c | sort -n -r | head -1 | sed 's/^ \+[^ ]* //g'`;
        current_node_update=${current_node_struct[*]};
        descision_tree[$current_node_index]=${current_node_update// /:};
    fi 
    
    ## output the descision tree after every step for split or leaf node generater
    echo ${descision_tree[@]};
done

 

执行代码:

 

./descision.sh descision.dat

 执行结果为:

 

1:0:0:N:N:0:0 0:2:0:house:0:0:0 0:0:0:house:1:0:0
1:0:0:N:N:0:0 0:2:0:house:0:0:0 0:0:0:house:1:1:1
1:0:0:N:N:0:0 3:2:0:house:0:0:0 0:0:0:house:1:1:1 0:4:1:house,job:0,0:0:0 0:0:1:house,job:0,1:0:0
1:0:0:N:N:0:0 3:2:0:house:0:0:0 0:0:0:house:1:1:1 0:4:1:house,job:0,0:0:0 0:0:1:house,job:0,1:1:1
1:0:0:N:N:0:0 3:2:0:house:0:0:0 0:0:0:house:1:1:1 0:4:1:house,job:0,0:1:0 0:0:1:house,job:0,1:1:1

输出结果中展示了决策树结构生成过程的细节,以及生成过程中树的变化过程

 

本代码中使用了一维数组结构来存储整棵决策树,输出的先后顺序按照数组下标输出。

 

输出结果中最后一行表示最终的决策树。它表示的树形结构其实是:

 

决策树结果

这样看着是不是就爽多了。

 

说明:

关于上述决策树结果中其实有部分存在误导:

默认根节点是放在数组的第一个位置的,即索引值为0,而子节点中的child与sibling为0时并不表示指向跟节点,而是表示无意义,即没有子节点或兄弟节点。

 

该决策树所代表的分类规则:

根据该决策树输出,我们挖掘的规则是这样的:

首先根据house属性判断,当house属性为1时,走到索引为2的节点,此时该节点是叶子节点,预测值class为1.

当house属性为0时,接着按照job属性来判断,当job属性为0时走到索引为3的节点,预测值class为0。如果job属性为1时走到索引值为4的节点,此时预测值class为1.

 

 

关于决策树的其他相关具体信息可参考:决策树决策树学习

1
5
分享到:
评论

相关推荐

    test.bash——bash的语法例程

    主要是bash语法的例程,在记录学习笔记的时候做练习用的。学习记录请参考:https://blog.csdn.net/xiaodouhao123456/article/details/109473083,及其所在专栏中的其他笔记。

    决策树联系数据源

    根据给定文件的信息,我们可以构建一...这种实践可以帮助我们在实际工作中更好地应用决策树算法解决具体问题。 以上就是关于如何使用Python实现决策树,并利用给定数据集进行训练和评估的详细介绍。希望对您有所帮助!

    Bash参考手册.pdf

    Bash是Unix shell的免费实现,兼容 Unix shell的所有功能,并且添加了一些新的功能。 2. Shell是什么? Shell是一个命令行接口,用户可以通过命令行与操作系统进行交互。Shell提供了一个交互式的命令行环境,用户...

    实现一个简单的bash

    本教程将深入讲解如何实现一个简单的Bash脚本,以及在编写过程中需要注意的关键知识点。 首先,创建一个新的文本文件并用你喜欢的编辑器打开,例如`nano`或`vim`。文件扩展名通常为`.sh`,以表明这是一个Shell脚本...

    bash.acp&bash.stx

    在这个场景中,我们关注的是与Bash shell相关的配置文件——"bash.acp"和"bash.stx",这些文件是专门为EditPlus定制的,目的是增强在编辑Bash脚本时的用户体验。 `bash.acp` 文件是EditPlus的语法规则配置文件,...

    Bash漏洞——Shellshock浅析.pdf

    【Bash漏洞——Shellshock浅析】 Bash漏洞,又称Shellshock,是指在2014年被发现的一个严重安全漏洞,它影响了广泛使用的Bash shell,这是一个在Unix-like操作系统,包括Linux和Mac OS X中的命令行解释器。这个漏洞...

    Apriori算法python实现含数据集

    总之,Apriori算法是数据挖掘中的基础工具,它通过Python实现能快速处理大量数据,找出有意义的关联规则。结合实际的数据集,我们可以有效地探索数据背后的信息,为商业策略提供有力的支持。在实际应用中,根据具体...

    bash v203- bash的windows本地实现

    综上所述,bash v203的Windows本地实现为Windows用户提供了一种在本地环境中直接使用Unix/Linux命令行工具的方式,结合UnxUtils,可以极大地增强Windows系统的命令行功能,尤其对于开发者和系统管理员而言,这是一个...

    关于linux bash致命漏洞的情况以及预防措施

    近期,一个被称为比“心脏出血”(Heartbleed)更为严重的Linux安全漏洞——Bash漏洞被公开披露。这一漏洞存在于广泛使用的Bash shell中,允许攻击者通过注入恶意环境变量的方式,远程执行代码,从而完全控制受影响...

    BASH中文帮助文档

    它可以执行从标准输入或者文件中读取的命令,整合了 Korn 和 C Shell 的优秀特性,目标是成为遵循 IEEE POSIX Shell and Tools specification 的实现。 选项 BASH 在启动时可以接受多种选项,以下是常用的选项: ...

    vuex-table+SpringBoot前端实现树形结构(csdn)————程序.pdf

    在本文档中,我们探讨了如何使用`Vuex`和`vxe-table`库与`SpringBoot`后端配合,在前端实现树形结构的数据展示。`vxe-table`是一个功能强大的表格组件,提供了丰富的功能,如表格编辑、排序、筛选等,同时也支持树形...

    Linux_bash_vs_dash

    本文档主要探讨了在 Linux 系统中两种常见的 shell —— bash 和 dash 的对比。通过对比这两种 shell 的特性、语法相似性及差异性,帮助读者更好地理解它们各自的优缺点以及适用场景。 **标签解释:** - **Linux...

    linux learning the bash shell

    - **核心概念**:本标题明确指出了学习的目标——Bash Shell。Bash(Bourne Again SHell)是Unix/Linux操作系统中最常用的命令解释器之一,也是大多数Linux发行版的默认Shell。 #### 描述:Learn Bash Shell - **...

    BASH 中文文档

    - **核心概念**:BASH(Bourne Again SHell)是一种Unix shell,广泛应用于Linux操作系统中作为默认shell。本中文文档旨在为用户提供全面深入的学习资料。 #### 二、描述:bash4.0中文参考 你懂的 方便各位下载,...

    python : knn算法——批量识别验证码.rar

    在Python中,可以使用scikit-learn库实现KNN算法。首先安装库: ```bash pip install scikit-learn ``` 然后,编写代码进行训练和预测: ```python from sklearn.neighbors import KNeighborsClassifier from ...

    man bash 中文PDF 版

    - **描述**: BASH 是一个与 sh 兼容的命令解释程序,能够执行来自标准输入或文件中的命令。它还融合了 Korn Shell (ksh) 和 C Shell (csh) 中的一些优秀特性。 - **目标**: 成为遵循 IEEE POSIX Shell and Tools ...

    基于Java的数据结构与算法实现.zip

    本项目是一个基于Java的数据结构与算法实现集合,涵盖了多种经典的排序算法、查找算法以及树结构的操作。通过这些实现,用户可以深入理解各种算法的原理和应用场景,同时也可以作为学习和实践数据结构与算法的参考。...

Global site tag (gtag.js) - Google Analytics