1. 程式人生 > >【機器學習實戰-kNN:約會網站約友分類】python3實現-書本知識【2】

【機器學習實戰-kNN:約會網站約友分類】python3實現-書本知識【2】

# coding=utf-8
# kNN-約會網站約友分類
from numpy import *
import matplotlib.pyplot as plt
import matplotlib.font_manager as font
import operator


# 【1】獲取資料
def init_data():
    # 開啟訓練集檔案
    f = open(r"F:\Python\data\kNN\datingTestSet2.txt", "r")
    rows = f.readlines()
    lines_number = len(rows)
    return_mat = zeros((lines_number, 3))  # lines_number行 3列
    class_label_vec = []
    index = 0
    for row in [value.split("\t") for value in rows]:
        return_mat[index, :] = row[0:3]  # 取row前三列
        class_label_vec.append(int(row[-1]))  # row[-1]取列表最後一列資料
        index += 1
    # 關閉開啟的檔案
    f.close()
    return return_mat, class_label_vec


# 【2】特徵縮放 X:=[X-mean(X)]/std(X) || X:=[X-min(X)]/max(X)-min(X) ;
def feature_scaling(data_set):
    # 特徵縮放參數
    max_value = data_set.max(0)
    min_value = data_set.min(0)
    # avg_value = (min_value + max_value)/2
    diff_value = max_value - min_value
    norm_data_set = zeros(shape(data_set))  # 初始化與data_set結構一樣的零array
    # print(norm_data_set)
    m = data_set.shape[0]
    norm_data_set = data_set - tile(min_value, (m, 1))  # avg_value
    norm_data_set = norm_data_set/tile(diff_value, (m, 1))
    return norm_data_set, diff_value, min_value


# 【3】kNN實現 input_set:輸入集 data_set:訓練集
def classify0(input_set, data_set, labels, k):
    data_set_size = data_set.shape[0]
    # 計算距離tile 重複以input_set生成跟data_set一樣行數的mat
    diff_mat = tile(input_set, (data_set_size, 1)) - data_set
    sq_diff_mat = diff_mat ** 2
    sq_distances = sq_diff_mat.sum(axis=1)
    distances = sq_distances ** 0.5
    # 按照距離遞增排序
    sorted_dist_indicies = distances.argsort()  # argsort返回從小到大排序的索引值
    class_count = {}  # 初始化一個空字典
    # 選取距離最小的k個點
    for i in range(k):
        vote_ilabel = labels[sorted_dist_indicies[i]]
        # 確認前k個點所在類別的出現概率,統計幾個類別出現次數
        class_count[vote_ilabel] = class_count.get(vote_ilabel, 0) + 1
    # 返回前k個點出現頻率最高的類別作為預測分類
    sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
    return sorted_class_count[0][0]


# 【4】測試kNN
def dating_class_test():
    # 測試樣本比例
    ho_ratio = 0.1
    dating_data_mat, dating_labels = init_data()
    norm_mat, diff_dt, min_value = feature_scaling(dating_data_mat)
    m = norm_mat.shape[0]
    num_test_vecs = int(m * ho_ratio)  # 測試樣本的數量
    error_count = 0.0
    for i in range(num_test_vecs):
        # 測試樣本和訓練樣本
        classifier_result = classify0(norm_mat[i, :], norm_mat[num_test_vecs:m, :],
                                      dating_labels[num_test_vecs:m], 4)
        print("the classifier came back with:%d , the real answer is:%d" % (classifier_result, dating_labels[i]))
        if classifier_result != dating_labels[i]:
            error_count += 1.0
    right_ratio = 1-error_count/float(num_test_vecs)
    print("the total right rate is :%f %%" % (right_ratio*100))


# 【5】樣本資料繪圖
def make_plot():
    # 獲取資料
    x, y = init_data()
    # 特徵縮放
    norm_mat, diff_dt, min_value = feature_scaling(x)

    fig = plt.figure()
    ax = fig.add_subplot(111)  # 畫布分割一行一列資料在第一塊
    # 設定字型
    simsun = font.FontProperties(fname='C:\Windows\Fonts\simsun.ttc')
    # ax.scatter(x[:, 1], x[:, 2], 15.0*array(y), 15.0*array(y))  # 取2 3列繪圖
    # plt.xlabel("玩視訊耗時百分比", fontproperties=simsun)
    # plt.ylabel("周消耗冰激凌公升數", fontproperties=simsun)

    ax.scatter(norm_mat[:, 0], norm_mat[:, 1], 15.0*array(y), 15.0*array(y))  # 取1 2列繪圖
    plt.xlabel("飛行常客里程數", fontproperties=simsun)
    plt.ylabel("玩視訊耗時百分比", fontproperties=simsun)
    plt.show()


# 預測函式
def classify_main():
    result_list = ['not at all', 'in small doses', 'in large doses']
    # 輸入
    ff_miles = float(input("frequent flier miles earned per year?"))
    percent_tats = float(input("percentage of time spent playing video games?"))
    ice_cream = float(input("liters of ice cream consumed per year?"))
    # 獲取資料
    dating_data_mat, dating_labels = init_data()
    # 特徵縮放
    norm_mat, diff_dt, min_value = feature_scaling(dating_data_mat)
    in_arr = array([ff_miles, percent_tats, ice_cream])
    # 計算距離
    classifier_result = classify0((in_arr-min_value)/diff_dt, norm_mat, dating_labels, 3)
    print("You will probably like this person:", result_list[classifier_result-1])

# 主方法
if __name__ == "__main__":

    # 繪圖
    make_plot()
    # 測試kNN指令碼
    # dating_class_test()
    # 預測函式
    classify_main()