zoukankan      html  css  js  c++  java
  • 数据挖掘入门系列教程(二)之分类问题OneR算法

    数据挖掘入门系列教程(二)之分类问题OneR算法

    数据挖掘入门系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html

    项目地址:GitHub

    在上一篇博客中,我们通过分析亲和性来寻找数据集中数据与数据之间的相关关系。这篇博客我们会讨论简单的分类问题。

    分类简介

    分类问题,顾名思义我么就是去关注类别(也就是目标)这个变量。分类应用的目的是根据已知类别的数据集得到一个分类模型,然后通过这个分类模型去对类别未知的数据进行分类。这里有一个很典型的应用,那就是垃圾邮件过滤器。

    在这片博客中,我们使用著名Iris(鸢尾属)植物作为数据集。这个数据集共有150条植物数据,每条数据都给出了四个特征:sepal length、sepal width、petal length、petal width(分别表示萼片和花瓣的长与宽),单位均为cm。一共有三种类别:Iris Setosa(山鸢尾)、Iris Versicolour(变色鸢尾)和Iris Virginica(维吉尼亚鸢尾)

    数据集准备

    在scikit-learn库中内置了该数据集,我们首先pip安装scikit-learn库


    下面的代码表示从sklearn中的数据集中加载iris数据集,并打印数据集中的说明(Description)。

    from sklearn.datasets import load_iris
    dataset = load_iris()
    print(dataset.DESCR)
    # data 为特征值
    data = dataset.data
    # target为分类类别
    target = dataset.target

    截一个数据集中的说明图:


    在数据集中,数据的特征值一般是连续值,比如说花瓣的长度可能有无数个值,而当两个值相近时,则表示相似度很大。(这个是由自然界决定的)

    与之相反,数据的类别为离散值。因为一种植物就肯定是一种植物,通常使用数字表示类别,但是在这里,数字的相近不能够代表这两个类别相似(因为这个类别是人为定义的)

    数据集的特征为连续数据,而类别是离散值,因此我们需要将连续值转成类别值,这个称之为离散化

    而将连续数据进行离散化有个很简单的方法,就是设定一个阈值,高于这个阈值为1,低于这个阈值为0。具体怎么实现我们在下面再说。

    OneR算法

    OneR(one rule)算法很简单,当时挺有效的。on rule,一条规则,以上面的iris植物为例,就是我们选择四个特种中分类效果最好的一个作为分类依据。这里值得注意的是,选择一个选择一个选择一个

    下面是算法的具体步骤,为《Python数据挖掘入门与实践》的原文。

    算法首先遍历每个特征的每一个取值,对于每一个特征值,统计它在各个类别中的出现次数,找到它出现次数最多的类别,并统计它在其他类别中的出现次数。

    举例来说,假如数据集的某一个特征可以取0或1两个值。数据集共有三个类别。特征值为0的情况下,A类有20个这样的个体,B类有60个,C类也有20个。那么特征值为0的个体最可能属于B类,当然还有40个个体确实是特征值为0,但是它们不属于B类。将特征值为0的个体分到B类的错误率就是40%,因为有40个这样的个体分别属于A类和C类。特征值为1时,计算方法类似,不再赘述;其他各特征值最可能属于的类别及错误率的计算方法也一样。

    统计完所有的特征值及其在每个类别的出现次数后,我们再来计算每个特征的错误率。计算方法为把它的各个取值的错误率相加,选取错误率最低的特征作为唯一的分类准则(OneR),用于接下来的分类。

    如果大家对OneR算法为什么能够对花卉进行分类感到迷惑的话,可以继续往下看,后面有说明。

    在前面的前面我们介绍了特征值的离散化,通过设定一个阈值,我们可以将特征值变成简单的0和1。具体怎么做呢?可以看下面的图片:


    假如一共有三个类别,120条数据形成一个(120 × 3的矩阵),然后我们进行压缩行,计算每一列的平均值,然后得到阈值矩阵(1 × 3的矩阵)。这个时候,我们就可以原先的数据进行离散化,变成0和1了。(这一步可以看示意图)

    在python的numpy中有一个方法,numpy.mean,里面经常操作的参数为axis,以m*n的矩阵为例:

    • axis = None,也就是不加这个参数,则是对m*n 个求平均值,返回一个实数
    • axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
    • axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
    average_num = data.mean(axis = 0)
    
    import numpy as np
    data = np.array(data > average_num,dtype = "int")
    print(data)

    通过np.array去构建一个新的数组,当data 大于average_num的时候(为矩阵比较),就为True,否则为False,然后指定类型为int,则True变成了1,False变成了0。结果如下图:


    算法实现

    既然是去构建一个分类模型,那么我们既需要去构建这个模型,也需要去测试这个模型。so,我们既需要训练集,也需要测试集。根据二八法则,一共150条数据,那么就有120个训练集,30个测试集。

    幸运的是sklearn提供了这个划分训练集的库给我们,train_test_split中0.2 代表的是测试集所占的比例(在我上传到GitHub的源代码中,没有设置这个值,默认是0.25),14代表的是随机种子。

    
    from sklearn.model_selection import train_test_split
    # 随机获得训练和测试集
    def get_train_and_predict_set():
        data_train,data_predict,target_train,target_predict = train_test_split(data,target,test_size=0.2, random_state=14)
        return data_train,data_predict,target_train,target_predict
    
    data_train,data_predict,target_train,target_predict = get_train_and_predict_set()
    

    这里有一点需要注意,同时也是困扰了我一段时间的问题。那就是在OneR算法中,只凭借1个特征,2种特征值,凭什么能够对3种花卉进行识别??实际上,不能,除非有3个特征值。在《Python数据挖掘入门与实践》,用花卉这个例子举OneR算法不是很恰当,因为当算法实现的时候,只能够识别出两种花卉。如下图:


    如果想看一个合适的例子,大家可以去看:https://www.saedsayad.com/oner.htm,在里面最后识别的结果只有yes和no。

    具体的训练步骤是怎么样的呢?

    首先我们假设有x,y,z三个特征,每个特征的特征值为0和1,同时有A,B两个类。因此我们可以得到下面的统计。

    对于每一个特征值,统计它在各个类别中的出现次数:


    既然我们得到了统计,这时候,我们就开始来计算错误率。首先我们找到某个特征值(如 $X = 0$)出现次数最多的类别。在下图中,被框框圈住的部分就是出现次数最多的特征值(如果有三个类别,任然是选择次数最多的类别)。


    再然后我们就是计算出每一个特征的错误率了,下面以$X$为例


    同理,我们可以得到$Y$,$Z$的错误错误率,然后选择最小的错误率作为分类标准即可。

    说了这么多,现在来写代码了。

    下面是train_feature函数,目的是得到指定特征特征值得到错误率最小的类别。也就是上面图中的$b_{x0},a_{x1}$等等。

    
    from collections import defaultdict
    from operator import itemgetter
    
    def train_feature(data_train,target_train,index,value):
        """
            data_train:训练集特征
            target_train:训练集类别
            index:特征值的索引
            value :特征值
        """
        count = defaultdict(int)
        for sample,class_name in zip(data_train,target_train):
            if(sample[index] ==value):
                count[class_name] += 1
                
       	# 进行排序
        sort_class = sorted(count.items(),key=itemgetter(1),reverse = True)
        # 拥有该特征最多的类别
        max_class = sort_class[0][0]
        max_num = sort_class[0][1]
        all_num = 0
        
        for class_name,class_num in sort_class:
            all_num += class_num
    #     print("{}特征,值为{},错误数量为{}".format(index,value,all_num-max_num))
        # 错误率
        error = 1 - (max_num / all_num)
        return max_class,error

    在train函数中,我们对所有的特征和特征值进行计算,得到最小的特征错误率。

    
    def train():
        errors = defaultdict(int)
        class_names = defaultdict(list)
        # 遍历特征
        for i in range(data_train.shape[1]):
           # 遍历特征值 
            for j in range(0,2):
                class_name,error = train_feature(data_train,target_train,i,j)
                errors[i] += error
                class_names[i].append(class_name)            
        return errors,class_names
    
    errors,class_names = train()
    # 进行排序
    sort_errors = sorted(errors.items(),key=itemgetter(1))
    best_error = sort_errors[0]
    # 得到最小错误率对应的特征
    best_feature = best_error[0]
    # 当特征值取 0 ,1对应的类别。
    best_class = class_names[best_feature]
    print("最好的特征是{}".format(best_error[0]))
    print(best_class)
    

    训练完成后,我们就可以进行predict了。predict就是那测试集中数据进行测试,使用自己的模型进行预测,在与正确的作比较得到准确度。看下图predict的流程:


    下面是预测代码以及准确度检测代码:

    # 进行预测
    def predict(data_test,feature,best_class):
        return np.array([best_class[int(data[feature])] for data in data_test])
    
    result_predict = predict(data_predict,best_feature,best_class)
    
    print("预测准确度{}".format(np.mean(result_predict == target_predict) * 100))
    print("预测结果{}".format(result_predict))

    结果

    在以下条件下:

    • 分割测试集和训练集的随机种子为14
    • 默认的切割比例

    最后的结果如下如所示:


    在以下条件下:

    • 切换比例为2:8
    • 随机种子为14

    结果如下图所示


    OneR算法很简单,但是在某些情况下却很有效,没有完美的算法,只有最适用的算法。

    结尾

    GitHub地址:GitHub

    参考书籍:Python数据挖掘入门与实践

    感谢蒋少华老师为我解惑。

  • 相关阅读:
    面向对象下
    面向对象上
    将博客搬至CSDN
    矩阵的常用术语和操作
    python2.7 Unable to find vcvarsall.bat
    intellij创建maven web项目
    intellij 编译 springmvc+hibernate+spring+maven 找不到hbm.xml映射文件
    spring Thymeleaf 中文乱码
    visualstudiocode 调试electron
    android反编译工具总结
  • 原文地址:https://www.cnblogs.com/xiaohuiduan/p/12446058.html
Copyright © 2011-2022 走看看