數據挖掘入門系列教程(二)之分類問題OneR算法

數據挖掘入門系列教程(二)之分類問題OneR算法

數據挖掘入門系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.htmlhtml

項目地址:GitHubpython

在上一篇博客中,咱們經過分析親和性來尋找數據集中數據與數據之間的相關關係。這篇博客咱們會討論簡單的分類問題。git

分類簡介

分類問題,顧名思義我麼就是去關注類別(也就是目標)這個變量。分類應用的目的是根據已知類別的數據集獲得一個分類模型,而後經過這個分類模型去對類別未知的數據進行分類。這裏有一個很典型的應用,那就是垃圾郵件過濾器。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)。app

from sklearn.datasets import load_iris
dataset = load_iris()
print(dataset.DESCR)
# data 爲特徵值
data = dataset.data
# target爲分類類別
target = dataset.target

截一個數據集中的說明圖:dom


在數據集中,數據的特徵值通常是連續值,好比說花瓣的長度可能有無數個值,而當兩個值相近時,則表示類似度很大。(這個是由天然界決定的)svg

與之相反,數據的類別爲離散值。由於一種植物就確定是一種植物,一般使用數字表示類別,可是在這裏,數字的相近不可以表明這兩個類別類似(由於這個類別是人爲定義的)函數

數據集的特徵爲連續數據,而類別是離散值,所以咱們須要將連續值轉成類別值,這個稱之爲離散化

而將連續數據進行離散化有個很簡單的方法,就是設定一個閾值,高於這個閾值爲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數據挖掘入門與實踐

感謝蔣少華老師爲我解惑。

相關文章
相關標籤/搜索