相亲数据集
部分数据及特征如下:
每年飞行公里数,一天吃甜品数(克),一天玩游戏时间占比,分类标记
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