import numpy as np
from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
import time
import pandas as pd
import operator#引入运算符模块
start_time = time.time()
"""
Parameters:
filename - 文件名
Returns:
returnMat - 特征矩阵
datingLabels - 分类Label向量
"""
# 函数说明:打开并解析文件,对数据进行分类:1代表不喜欢,2代表魅力一般,3代表极具魅力
def file2matrix(filename):
arrayOfLines=pd.read_table(filename,sep="\t",header=None)#打开文件
numberOflines=len(arrayOfLines)#得到文件的行数
datingDataMat=np.zeros((numberOflines,3))#创建返回的NumPy矩阵
datingLabels=[]#返回分类标签向量
#循环处理每一行的文件
#print(type(arrayOfLines))
for index, row in arrayOfLines.iterrows():
datingDataMat[index,:]=row[0:3]#选取前三列元素,每次循环是一行
label=row[3]#输出标签的文本
####根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力
if label == 'didntLike':
datingLabels.append(1)
elif label == 'smallDoses':
datingLabels.append(2)
elif label == 'largeDoses':
datingLabels.append(3)
return datingDataMat,datingLabels
#归一化数据
def autoNorm(dataSet):
minVals=dataSet.min(0)#参数0:从列中选取最小值
maxVals=dataSet.max(0)
ranges=maxVals-minVals#取值范围
normDataSet=np.zeros(np.shape(dataSet))#创建新的返回矩阵
m=dataSet.shape[0]#返回dataSet的行数
#计算归一化特征值
normDataSet=dataSet-np.tile(minVals,(m,1))#原值-最小值,tile将minVals的行复制m次,列复制一次(即列数不变)
normDataSet=normDataSet/np.tile(ranges,(m,1))#计算归一化特征值
#return normDataSet,ranges,minVals#返回
return normDataSet,minVals,ranges
####为每组数据值分类,函数的功能是使用k-近邻算法将 每组数据划分到某个类中
def classify0(inX,dataSet,labels,K):
#numpy函数shape[0]返回dataSet的行数
dataSetSize=dataSet.shape[0]#获取数组的形状信息,0返回行数,1返回列数
# 在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
diffMat=np.tile(inX,(dataSetSize,1))-dataSet#测试集和训练集之前的x,y上的差值,新(x,y)=(x_测-x_训练,y_测-y_训)
"""
c = np.array([[1, 2], [3, 4]])
d = np.tile(c, (2, 3))
print(d)
# 输出:
# [[1 2 1 2 1 2]
# [3 4 3 4 3 4]
# [1 2 1 2 1 2]
# [3 4 3 4 3 4]]
"""
##计算欧式距离
sqDiffMat= diffMat ** 2#计算距离的平方
sqDistances=sqDiffMat.sum(axis=1)#(x_测-x_训练)^2+y_测-y_训)^2
distances=pow(sqDistances,1/3)#开根号
##返回distances中元素从小到大排序后的索引值
sortedDistIndicies=distances.argsort()#argsort,返回数组排序后的索引,先从小到大排序然后再按照各个顺序返回数值没有排序前的索引
#argsort()返回最小的的数值的索引,之后返回第二小的索引
"""distances=[129.0736224 , 127.28314892, 117.13667231, 10. ,15.13274595,18.24828759])
sortedDistIndicies=[3, 4, 5, 2, 1, 0]
"""
classCount={}#创建空字典
for i in range(K):
# 取出前k个元素的类别
voteIlabel=labels[sortedDistIndicies[i]]
#计算类别次数
#classCount.get(voteIlabel, 0) 表示获取字典 classCount 中键为 voteIlabel 的值,如果不存在则返回0
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
#对键值进行排序
#classCount.items() 返回字典 classCount 中所有键值对的视图
# python3中用items()替换python2中的iteritems()
# key=operator.itemgetter(1)根据字典的值进行排序
# key=operator.itemgetter(0)根据字典的键进行排序
#reverse = True
#是排序函数的关键字参数,表示按照降序进行排序
sortedClassCount=sorted(classCount.items(),#sorted排序,并返回一个列表
key=operator.itemgetter(1),reverse=True)
#print(sortedClassCount[0][0])
return sortedClassCount[0][0]
#return distances,sortedDistIndicies
#分类器,计算错误次数比例
def datingClassTest(normDataSet, datingLabels):
hoRatio=0.10
m=normDataSet.shape[0]#输出normDataSet的行数
numTestVecs=int(m*hoRatio)#计算测试集的数量
errorCount=0.0#错误率
for i in range(numTestVecs):
#每行数据的分类结果
classifierResult=classify0(normDataSet[i,:],normDataSet[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
#print("分类器返回值:{},真实标签:{}".format(classifierResult,datingLabels[i]))
#错误次数计算
if classifierResult != datingLabels[i]:
errorCount+=1.0
print("错误率为{:.2f}%".format((errorCount/m)*100))#输出错误率
"""
minVals--------训练集里面的最小值
ranges---------训练集范围
normDataSet--------归一化后的训练集
datingLabels---训练集标签
"""
#函数说明:通过输入一个人的三维特征,进行分类输出
def classifyPerson(minVals,ranges,normDataSet,datingLabels):
#输出结果
resultList=["讨厌","有些喜欢","非常喜欢"]
#三维特征用户输入
precentTats = float(input("玩视频游戏所耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰激淋公升数:"))
inArr = np.array([precentTats, ffMiles, iceCream])#将输入特征转为测试集格式
norminArr = (inArr - minVals) / ranges#测试集归一化
#返回分类结果
classifierResult = classify0(norminArr, normDataSet, datingLabels, 3)
#打印结果,-1是因为分类结果是1,2,3,但是对应索引是0,1,2
print("你可能{}这个人" .format(resultList[classifierResult-1]))
#函数说明:可视化数据
def showdata(datingDataMat, datingLabels):
font=FontProperties(fname=r"/kaggle/input/use-font/Excalidraw.ttf", size=14)#前缀为r的字符串表示原始字符串(raw string)。使用原始字符串时,反斜杠\不会被解释为转义字符,而是作为普通字符处理
#将fig画布分布nrows行ncols列,不共享x、y,axs[0][0]表示第一行第一个区域
fig,axs=plt.subplots(nrows=2,ncols=2,sharex=False,sharey=False,figsize=(13,8))
numberOflabels=len(datingLabels)
labelsColors=[]#定义空列表
#定义标签对应的颜色
for i in datingLabels:
if i ==1:
labelsColors.append("black")
if i ==2:
labelsColors.append("orange")
if i ==3:
labelsColors.append("red")
##画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第二列(玩游戏)数据画散点数据,散点大小为15,透明度为0.5
axs[0][0].scatter(x=datingDataMat[:,0],y=datingDataMat[:,1],color=labelsColors,s=15,alpha=0.5)
#设置标题,x轴label,y轴label
axs0_title_text = axs[0][0].set_title(u'每年获得的飞行常客里程数与玩视频游戏所消耗时间占比', fontproperties=font)#fontproperties参数不能大写首字母为FontProertise
axs0_xlabel_text = axs[0][0].set_xlabel(u'每年获得的飞行常客里程数', fontproperties=font)
axs0_ylabel_text = axs[0][0].set_ylabel(u'玩视频游戏所消耗时间占', fontproperties=font)
plt.setp(axs0_title_text,size=9,weight='bold',color='red')#setp是matplotlib.pyplot模块的一种方法,批量设置对象属性,weight(字体粗细)
plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')
plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')
# 画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=labelsColors, s=15, alpha=.5)
# 设置标题,x轴label,y轴label
axs1_title_text = axs[0][1].set_title(u'每年获得的飞行常客里程数与每周消费的冰激淋公升数', fontproperties=font)
axs1_xlabel_text = axs[0][1].set_xlabel(u'每年获得的飞行常客里程数', fontproperties=font)
axs1_ylabel_text = axs[0][1].set_ylabel(u'每周消费的冰激淋公升数', fontproperties=font)
plt.setp(axs1_title_text, size=9, weight='bold', color='red')
plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')
plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')
# 画出散点图,以datingDataMat矩阵的第二(玩游戏)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=labelsColors, s=15, alpha=.5)
# 设置标题,x轴label,y轴label
axs2_title_text = axs[1][0].set_title(u'玩视频游戏所消耗时间占比与每周消费的冰激淋公升数', fontproperties=font)
axs2_xlabel_text = axs[1][0].set_xlabel(u'玩视频游戏所消耗时间占比', fontproperties=font)
axs2_ylabel_text = axs[1][0].set_ylabel(u'每周消费的冰激淋公升数', fontproperties=font)
plt.setp(axs2_title_text, size=9, weight='bold', color='red')
plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')
plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')
#设置图例
#mlines.line2D给图表设置属性
didntLike=mlines.Line2D([],[],color="black",marker=".",markersize=6,label='didntLike')
smallDoes=mlines.Line2D([],[],color="orange",marker=".",markersize=6,label="smallDoes")
largeDoses = mlines.Line2D([], [], color='red', marker='.',
markersize=6, label='largeDoses')
#添加图例
#legend在图形中添加图例的方法,handles 参数是一个包含要显示在图例中的线条或其他图形对象的列表
axs[0][0].legend(handles=[didntLike, smallDoes, largeDoses])
axs[0][1].legend(handles=[didntLike, smallDoes, largeDoses])
axs[1][0].legend(handles=[didntLike, smallDoes, largeDoses])
#显示图片
plt.show()
if __name__=="__main__":
#打开的文件名
filename="/kaggle/input/datingtestset/datingTestSet.txt"
#打开并处理数据
datingDataMat, datingLabels=file2matrix(filename)
#数据归一化
normDataSet,minVals,ranges=autoNorm(datingDataMat)
# kNN,并计算数据分类正确率
datingClassTest(normDataSet, datingLabels)
#数据可视化
showdata(normDataSet, datingLabels)
#计算运行时间
end_time = time.time()
execution_time = end_time - start_time
print(f"代码执行时间:{execution_time} 秒")
#自己输入数据来获得输出结果
classifyPerson(minVals,ranges,normDataSet,datingLabels)
kNN 约会网站总代码 在kaggle上写的
最新推荐文章于 2024-08-22 07:51:42 发布