基于KNN的相亲分类问题

相亲数据集

部分数据及特征如下:
每年飞行公里数,一天吃甜品数(克),一天玩游戏时间占比,分类标记
40920 8.326976 0.953952 3

14488 7.153469 1.673904 2

26052 1.441871 0.805124 1

1, 表示无兴趣
2, 表示有兴趣
3, 表示很喜欢

python 实现相亲分类

根据KNN的算法步骤使用python实现

# coding:utf-8

import numpy as np
#import operator
# matplotlib 绘图模块
import matplotlib.pyplot as plt


# from array import array
# from matplotlib.font_manager import FontProperties


def classify(x, X, y, k):
    """
        x:待分类的样本,list or 1dim array 
        X:训练集,2dim array
        y:训练集的标记,1dim array
        k:近邻数
        
        return 预测的类别,1/2/3
    """
    # 计算行数
    m = X.shape[0]
    
    #判断x的数据格式
    if isinstance(x,(list,tuple)):
        x = np.array(x)
    elif hasattr(x,"shape") and x.ndim == 1 :
        pass
    else:
        raise ValueError("当前x样本数据格式不支持")
    
    #当前点到所有点的距离
    x_X_distance = np.sqrt(np.sum((x - X)**2,axis=1))
   
    #距离排序
    sorted_index = x_X_distance.argsort()
    

    #class_count投票决定类别
    class_count = {
    
    }
    for i in range(k):
        # 获取第i个近邻对应类别
        vote_label = y[sorted_index[i]]
        
        # 给相同的类别计数
        class_count[vote_label] = class_count.get(vote_label, 0) + 1
    
    #对投票结果排序
    sorted_class_count = sorted(class_count.items(), key=lambda x:x[1], reverse=True)
    return sorted_class_count[0][0]



#高效np.loadtxt
def load_data(filename):
    """
        加载数据集
        return 
            X:2dim array,样本集
            y:1dim array,样本标记
    """
    fr = open(filename, "rb")
    # readlines:是一次性将这个文本的内容全部加载到内存中(列表)
    lines = fr.readlines()
#    print("All lines:\n",lines[:10])
    
    rows = len(lines)
    
    # numpy.zeros 创建给定类型的数组
    X = np.zeros((rows, 3))
    
    # 标记列表
    y = []
    
    index = 0
    for line in lines:
        # 去掉一行的头尾空格
        line = line.decode("utf-8").strip()
#        print([line])
        line_sep_list = line.split('\t')
        
        #自动转为float
        X[index, :] = line_sep_list[0:3]
        
        #3\r\r\n  转为int,忽略\r\r\n
        y.append(int(line_sep_list[-1]))
        index += 1
    return X, np.array(y)



# 将数据归一化
def min_max_scale(X):
    '''
        将训练集中的数据进行归一化
        归一化的目的:
            训练集中飞行公里数这一维度中的值是非常大,那么这个纬度值对于最终的计算结果(两点的距离)影响是非常大,
            远远超过其他的两个维度对于最终结果的影响
        实际约会姑娘认为这三个特征是同等重要的
        下面使用最大最小值归一化的方式将训练集中的数据进行归一化
        X:2dim array,训练集
    '''
    #x.min(axis=0) 
    #代表的是统计这个矩阵中每一列的最小值 
    #返回值是一个矩阵1*3矩阵
    # 例如: numpyarray = array([[1,4,6],
    #                        [2,5,3]])
    #    numpyarray.min(axis=0) = array([1,4,3])
    #    numpyarray.min(axis=1) = array([1,2])
    #    numpyarray.max(axis=0) = array([2,5,6])
    #    numpyarray.max(axis=1) = array([6,5])
    
    #所有特征的最小值
    min_values = X.min(axis=0)
    
    #所有特征的最大值
    max_values = X.max(axis=0)
    
    #最大最小range
    ranges = max_values - min_values
    
    # x.shape[0] 计算行数
    # shape[1] 计算列数
    #m = x.shape[0]

    
    #手动广播
    #np.tile(arr,times) 重复一个数组times次               
    #normDataSet = X - np.tile(min_values, (m, 1))
    #normDataSet = normDataSet / np.tile(ranges, (m, 1))
    
    X_scale = (X-min_values)/ranges
    
    return X_scale, ranges, min_values




def train_model():
    """
        训练模型
        return acc: float,模型的准确率
    """
    
    #验证数据集占比
    rate = 0.1
    #加载数据集
    X, y = load_data('./datingTestSet.txt')
    # 将数据归一化
    X_scale, ranges, min_values = min_max_scale(X)
    
    # m 样本数
    m = X_scale.shape[0]
    print("Total smaples:\n",m)
    
    # 划分训练集,验证集
    num_of_validate = int(m * rate)
    
    #预测错误样本数
    error_count = 0.0
    #取验证集的每个样本
    for i in range(num_of_validate):
        # X_scale[i,:] 
        #取出数据的第i行,进行预测
        #X_scale[num_of_validate:m,:]取出数据中的100行到1000行 作为训练集,
        # y[num_of_validate:m] 取出数据中100行到1000行的类别
        #K=4
        #
        pred_result = classify(X_scale[i, :], X_scale[num_of_validate:m, :], y[num_of_validate:m], 4)
        print('模型预测值: %d ,真实值 : %d' % (pred_result, y[i]))
        if (pred_result != y[i]):
            error_count += 1.0
            
    error_rate = error_count / float(num_of_validate)
    print('正确率 : %f' % (1 - error_rate))
    return 1 - error_rate


def show_scatter():
    '''
        拿样本的飞行里程数和玩游戏所消耗的时间百分比这两个维度的值,
        画出散点图
    '''
    X,y = load_data('datingTestSet.txt')
    
    # 生成一个新的图像
    fig = plt.figure()
    # matplotlib下, 一个 Figure 对象可以包含多个子图(Axes), 
    #可以使用 subplot() 快速绘制
    # subplot(numRows, numCols, plotNum)图表的整个绘图区域被分成 numRows 行和 numCols 列,
    #按照从左到右,从上到下的顺序对每个子区域进行编号,左上的子区域的编号为1
    # plt.subplot(111)等价于plt.subplot(1,1,1)
    axes = plt.subplot(111)
    # 设置字体 黑体  ,用来正常显示中文标签
    plt.rcParams['font.sans-serif'] = ['SimHei']

    # 绘制散点图 ,前两个参数表示相同长度的数组序列 ,
    #s 表示点的大小, c表示颜色
    type1 = axes.scatter(X[y==1][:,0],X[y==1][:,2],s=20,c="r",edgecolors=None,marker="s")
    type2 = axes.scatter(X[y==2][:,0],X[y==2][:,2],s=40,c='g',edgecolors=None,marker="^")
    type3 = axes.scatter(X[y==3][:,0],X[y==3][:,2],s=50,c='b',edgecolors=None,marker="o")
    plt.title('相亲样本点')
    plt.xlabel(u'每年飞行里程数')
    plt.ylabel(u'玩游戏所消耗的时间百分比')
    # loc 设置图例的位置 2是upper left
    axes.legend((type1, type2, type3), (u'没兴趣', u'有兴趣', u'很喜欢'), loc="upper left")
    #plt.scatter(datingDataMat[:,0],datingDataMat[:,1],c = datingLabels)
    plt.show()


def predict_person(input_man):
    """
        预测未知样本
        input_man:list or 1dim array
        return label:'没兴趣'/'有兴趣'/'很喜欢'
    """
    labels = ['没兴趣', '有兴趣', '很喜欢']
    
    #使用所有的训练样本来预测
    X,y = load_data('datingTestSet.txt')
    
    #归一化
    X_scale, ranges, min_values = min_max_scale(X)
    
    #预测未知样本
    result = classify((input_man - min_values) / ranges, X_scale, y, 3)
    
    print('你对即将约会的人:%s' % labels[result - 1])


if __name__ == '__main__':
    #     show_scatter观察数据的分布情况
    #     show_scatter()
    
    #acc 为准确率
    acc = train_model()
    if acc > 0.9:
        # input_sample 很喜欢
        input_man = [18983, 10.448091,0.267652]
        predict_person(input_man)
    


sklearn库实现相亲分类

# coding:utf-8
import operator

from sklearn.neighbors import NearestNeighbors

from KNN_python import load_data, min_max_scale

if __name__ == '__main__':
    X,y = load_data('datingTestSet.txt')
    X_scale, ranges, min_values = min_max_scale(X)
    
    # n_neighbors=4 表示查找的近邻数,默认是5
    # fit:用X_scale作为训练集拟合模型  
    #n_neighbors:几个最近邻 
    # NearestNeighbors 默认使用的就是欧式距离测度
    #
    nbrs = NearestNeighbors(n_neighbors=4).fit(X_scale)
    
    
    # input_man
    input_man = [18983, 10.448091,0.267652]
    # 数据归一化
    S = (input_man - min_values) / ranges
    
    # 找到当前点的K个临近点
    # indices 最近邻的索引 2dim array
    # distance 最近邻的距离 2dim array
    distances, indices = nbrs.kneighbors([S])
    print("distances is %s" % distances)
    print("indices is %s" % indices)
    
    # class_count   
    #key:类别
    #value:出现次数
    class_count = {
    
    }
    for i in range(4):
        # 获取第i个近邻的标记
        vote_label = y[indices[0][i]]
        class_count[vote_label] = class_count.get(vote_label, 0) + 1
    sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
    labels = ['没兴趣', '有兴趣', '很喜欢']
    print(labels[sorted_class_count[0][0] - 1])

总体代码

链接:https://pan.baidu.com/s/1Ue8CB6K9uNbGz1dSbriEMQ
提取码:nvhd

猜你喜欢

转载自blog.csdn.net/weixin_45228198/article/details/113873986