《機器學習實戰》中的程式清單2-1 k近鄰演算法(kNN)classify0都做了什麼

王明輝發表於2018-01-26

 

 

from numpy import *
import operator
import matplotlib
import matplotlib.pyplot as plt
from imp import *
#from os import *
import os

reload(operator)

def start():
    group,labels = createDataSet()
    testSample = [5,7]
    print("測試樣本:" ,end="")
    print(testSample)

    return classify0(testSample, group, labels, 4)

def createDataSet():

    group = array([[1,2],[2,3],[1,1],[4,5]]) #此處隨意定義,表示一個已知的已分類的資料集
    labels = ['A','A','B','B']

    #例如
    #group = array([[1,2],[2,3],[1,1],[4,5],[5,7],[6,6]]) #此處隨意定義,表示一個已知的已分類的資料集
    #labels = ['A','A','B','B','C','C']

    return group, labels 

def classify0(inX, dataSet, labels, k):
    """
      inX 是輸入的測試樣本,是一個[x, y]樣式的
      dataset 是訓練樣本集
      labels 是訓練樣本標籤
      k 是top k最相近的
    """

    # 矩陣的shape是個tuple,如果直接呼叫dataSet.shape,會返回(4,2),即
    # 返回矩陣的(行數,列數),
    # 那麼shape[0]獲取資料集的行數,
    # 行數就是樣本的數量
    # shape[1]返回資料集的列數
    dataSetSize = dataSet.shape[0]

    ###################說明程式碼########################
    #print("dataSet.shape[0]返回矩陣的行數:")
    #print(dataSetSize)
    #print("dataSet.shape[1]返回矩陣的列數:")
    #cols = dataSet.shape[1]
    #print(cols)
    #print(dataSet.shape)
    #print("dataSet.shape型別:")
    #print(type(dataSet.shape))
    ###################################################

    #此處Mat是Maxtrix的縮寫,diffMat,即矩陣的差,結果也是矩陣
    #關於tile函式的說明,見http://www.cnblogs.com/Sabre/p/7976702.html
    #簡單來說就是把inX(本例是[1,1])在“行”這個維度上,複製了dataSetSize次(本例dataSetSize==4),在“列”這個維度上,複製了1次
    #形成[[1,1],[1,1],[1,1],[1,1]]這樣一個矩陣,以便與dataSet進行運算
    #之所以進行這樣的運算,是因為要使用歐式距離公式求輸入點與已存在各點的距離

    #這是第1步,求給出點[1,1]與已知4點的差,輸出為矩陣
    diffMat = tile(inX,(dataSetSize,1)) - dataSet
    #print(tile(inX,(dataSetSize,1)))

    ###################說明程式碼########################
    #print("diffMat:" + str(diffMat))
    ###################################################
    
    #第2步,對矩陣進行平方,即,求差的平方
    sqDiffMat = diffMat ** 2

    ###################說明程式碼########################
    #print("sqDiffMat:" + str(sqDiffMat))
    #print("sqDiffMat",end="")
    #print(sqDiffMat[324])
    ###################################################

    #sum(axis=1)是將矩陣中每一行中的數值相加,如[[0 0] [1 1] [0 1] [9 9]]將得到[0,2,1,18],得到平方和
    #sum(axis=0)是將矩陣中每一列中的數值相加
    #第3步,求和
    sqDistances = sqDiffMat.sum(axis=1)
    #print("sqDistances:", end="")
    #print(sqDistances[875])

    ###################說明程式碼########################
    #print("sqDistances:" + str(sqDistances))
    ###################################################
    
    #第4步,將平方和進行開方,得到距離,輸出為陣列
    distances = sqDistances ** 0.5


    ###################說明程式碼########################
    #print("未知點到各個已知點的距離:",distances)
    ###################################################

    #argsort(),將陣列中的元素的索引放在由小到大的位置上由小到大排序
    #如陣列a = array([ 0 4 3 18]),b = a.argsort()之後,得到b是[0 2 1 3]這是a的索引陣列,最小的在最前面,位置0,第二小的是索引為2的元素,即3,3在陣列中的位置是2
    #第三小的是索引為1的,即4,4在陣列中的索引位置是2,第四小的是索引為3的,即18
    #這樣保證了原陣列元素的位置不變,以便進行標籤的匹配
    #print(distances[875])
    #print(distances[324])
    #print(distances[392])
    sortedDistIndicies = distances.argsort()

    ###################說明程式碼########################
    #print("索引位置:", sortedDistIndicies) #可得到前k個索引
    ###################################################
    
    #建立空字典
    classCount = {} 
    
    #k值是取前k個樣本進行比較
    for i in range(k):
        #返回distances中索引為sortedDistIndicies[i]的值
        #此例中分別為:
        #sortedDistIndicies[0]==0,則labels[0]=='A',voteIlabel=='A'
        #sortedDistIndicies[1]==2,則labels[2]=='B',voteIlabel=='B'
        #sortedDistIndicies[2]==1,則labels[0]=='A',voteIlabel=='A'
        #sortedDistIndicies[3]==18,則labels[0]=='B',voteIlabel=='B'

        voteIlabel = labels[sortedDistIndicies[i]] 
        #print("中華人民共和國")
        ###################說明程式碼########################

        # print(voteIlabel)
        # print("標籤" + str(i) + ":" + str(voteIlabel))
        ###################################################

        #dict.get(key, default=None),對於鍵 key 返回其對應的值,或者若 dict 中不含 key 則返回 default(注意, default的預設值為 None,此處設定為0)
        #第一次呼叫classCount.get時,classCount內還沒有值
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
        
        ###################說明程式碼########################
        # print("第"+str(i+1)+"次訪問,classCount[" + str(voteIlabel) + "]值為:" + str(classCount[voteIlabel]))
        # print("classCount的內容為:")
        # print(classCount)
        ###################################################
    
    # sorted(iterable[,cmp,[,key[,reverse=True]]])
    # 作用:Return a new sorted list from the items in iterable.
    #           第一個引數是一個iterable,返回值是一個對iterable中元素進行排序後的列表(list)。
    # 可選的引數有三個,cmp、key和reverse。
    # 1)cmp指定一個定製的比較函式,這個函式接收兩個引數(iterable的元素),如果第一個引數小於第二個引數,返回一個負數;如果第一個引數等於第二個引數,返回零;如果第一個引數大於第二個引數,返回一個正數。預設值為None。
    # 2)key指定一個接收一個引數的函式,這個函式用於從每個元素中提取一個用於比較的關鍵字。預設值為None。
    #   從python2.4開始,list.sort()和sorted()函式增加了key引數來指定一個函式,此函式將在每個元素比較前被呼叫
    #   key引數的值為一個函式,此函式只有一個引數且返回一個值用來進行比較。這個技術是快速的,因為key指定的函式將準確地對每個元素呼叫。
    #   key=operator.itemgetter(0)或key=operator.itemgetter(1),決定以字典的鍵排序還是以字典的值排序
    #   0以鍵排序,1以值排序
    # 3)reverse是一個布林值。如果設定為True,列表元素將被倒序排列。
    # operator.itemgetter(1)這個很難解釋,用以下的例子一看就懂
    # a=[11,22,33]
    # b = operator.itemgetter(2)
    # b(a)
    # 輸出:33
    # b = operator.itemgetter(2,0,1)
    # b(a)
    # 輸出:(33,11,22)
    # operator.itemgetter函式返回的不是值,而是一個函式,通過該函式作用到物件上才能獲取值
   # 在這裡
itemgetter(1)的作用是按照第二個元素的順序對元組進行排序,也就是value的順序,如果改成itemgetter(0),則根據Key值排序
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True)

    #print(sortedClassCount)

    #返回正序排序後最小的值,即“k個最小相鄰”的值決定測試樣本的類別
    print("最終結果,測試樣本類別:" , end="")
    print(sortedClassCount)
    return sortedClassCount[0][0] 

 

 

以下為輸出結果,未必完全一致,請自行除錯。

輸出結果:

dataSet.shape[0]返回矩陣的行數:
4
dataSet.shape[1]返回矩陣的列數:
2
(4, 2)
dataSet.shape型別:
<class 'tuple'>
diffMat:[[ 2 1]
[ 1 0]
[ 2 2]
[-1 -2]]
sqDiffMat:[[4 1]
[1 0]
[4 4]
[1 4]]
sqDistances:[5 1 8 5]
未知點到各個已知點的距離: [ 2.23606798 1. 2.82842712 2.23606798]
索引位置: [1 0 3 2]
標籤0:A
第1次訪問,classCount[A]值為:1
classCount的內容為:
{'A': 1}
標籤1:A
第2次訪問,classCount[A]值為:2
classCount的內容為:
{'A': 2}
標籤2:B
第3次訪問,classCount[B]值為:1
classCount的內容為:
{'A': 2, 'B': 1}
標籤3:B
第4次訪問,classCount[B]值為:2
classCount的內容為:
{'A': 2, 'B': 2}
[('A', 2), ('B', 2)]
最終結果,測試樣本類別:A
[Finished in 5.3s]

相關文章