机器学习算法一——k-近邻算法(2)
使用k-近邻算法改进约会网站的配对效果
1、准备数据:从文本文件中解析数据
数据样本存放在文本文件datingTestSet.txt中,主要包含3种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所耗时间百分比
- 每周消费的冰淇淋公升数
将文本记录到转换NumPy的解析程序
###从文本文件中解析数据
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines) #文件的行数
returnMat = zeros((numberOfLines,3))
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip() #截取掉所有的回车字符
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3] #取前三个元素存储到特征矩阵中
if listFromLine[-1] == 'didntLike':
classLabelVector.append(1)
elif listFromLine[-1] == 'smallDoses':
classLabelVector.append(2)
elif listFromLine[-1] == 'largeDoses':
classLabelVector.append(3)
#classLabelVector.append(int(listFromLine[-1]))#索引值-1表示列表中的最后一列元素
index += 1
return returnMat,classLabelVector
注:
classLabelVector.append(int(listFromLine[-1]))
必须明确告知解释器存储的元素值为整型
数据处理结果
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
print(datingDataMat)
>>>[[ 4.09200000e+04 8.32697600e+00 9.53952000e-01]
[ 1.44880000e+04 7.15346900e+00 1.67390400e+00]
[ 2.60520000e+04 1.44187100e+00 8.05124000e-01]
...,
[ 2.65750000e+04 1.06501020e+01 8.66627000e-01]
[ 4.81110000e+04 9.13452800e+00 7.28045000e-01]
[ 4.37570000e+04 7.88260100e+00 1.33244600e+00]]
print(datingLabels[0:20])
>>>[3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3]
(1)fr.readlines()
将文件中的数据全部读出来
例如,文件内容如下:
fr = open(filename)
array = fr.readlines()
numberOf = len(array)
print(array)
print(numberOf)
>>>['Hello,world!\n', 'Hello,Wednesday!\n', 'Good luck!']
3
(2)len()函数
其表示返回容器中的项目数,上例中返回值为3
(3)strip()方法
语法:str.strip([chars])
参数:chars——移除字符串头尾指定的字符
返回值:返回移除字符串头尾指定的字符生成的新字符串
实例:
for line in array:
line = line.strip()
print(line)
>>>Hello,world!
Hello,Wednesday!
Good luck!
(4)split()方法
语法:str.split(str="", num=string.count(str))
参数:str —— 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等
返回值:返回分割后的字符串列表
实例:
listline = line.split('\t')
print(listline)
>>>['Hello,world!']
['Hello,Wednesday!']
['Good luck!']
(5)数组
区间左闭右开
#列表
b=([[2 ,4 ,6 ,7],
[9 ,8 ,3 ,2],
[0 ,3 ,5 ,1]])
print(b[0:2])
>>>[[2, 4, 6, 7], [9, 8, 3, 2]]
print(b[0:2,2])
>>>TypeError: list indices must be integers or slices, not tuple
#数组
import numpy as np
b=np.array([[2 ,4 ,6 ,7],
[9 ,8 ,3 ,2],
[0 ,3 ,5 ,1]])
print(b[0:2,2])
>>>[6 3]
print(b[0:2,:])
>>>[[2 4 6 7]
[9 8 3 2]]
print(b[:,3])
>>>[7 2 1]
2、分析数据:使用matplotlib创建散点图
fig = plt.figure()
ax1 = fig.add_subplot(211)
plt.xlabel("%time")
plt.ylabel("L")
ax1.scatter(datingDataMat[:,1],datingDataMat[:,2],
15.0*array(datingLabels),15.0*array(datingLabels))
ax2 = fig.add_subplot(212)
plt.xlabel("miles")
plt.ylabel("%time")
ax2.scatter(datingDataMat[:,0],datingDataMat[:,1],
15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()
第一张图为玩视频游戏所耗时间百分比和每周消费的冰淇淋公升数的数据散点图。虽然能够比较容易地区分数据点从属类别,但依然很难根据这张图得出结论性信息。
第二张图为每年赢得的飞行常客里程数与玩视频游戏所占百分比的约会数据散点图。图中清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同。
(1)add_subplot()函数
add_subplot(nlm)表示图像是nxl的,选中第m个
比如,add_subplot(222)表示图像是2x2的,选中第2个;
fig = plt.figure()
ax = fig.add_subplot(222)
add_subplot(223)表示图像是2x2的,选中第3个
fig = plt.figure()
ax = fig.add_subplot(223)
(2)scatter()函数
语法:matplotlib.pyplot.scatter(x,y,s=20,c=‘b’,marker=‘o’,cmap=None,norm=None,vmin=None,vmax=None,alpha=None,linewidths=None,verts=None,hold=None,**kwargs)
参数:x,y——输入数据
s——标量或形如shape(n,)数组,可选,默认20,size in points^2
c——色彩或颜色序列,可选,默认(注意c不应是一个单一的RGB数字或RGBA序列,因为不便区分。c可以是一个RGB或RGBA二维行数组)
b----blue c----cyan g----green k----black m----magenta r----red w----white
y----yellow
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
plt.show()
原图
可以对比一下s为2.0时
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],
2.0*array(datingLabels),15.0*array(datingLabels))
3、准备数据:归一化数值
提取四组样本数据:
样本3和4之间的距离计算如下:
发现每年获取的飞行常客里程数由于数字大而对计算结果的影响也远远大于其他两个特征。
然而,三种特征是同等重要的,因此作为三个等权重的特征之一,飞行常客里程数不应该如此严重地影响到计算结果。
方法:归一化(将取值范围处理为 0-1或-1~1之间)
newValue = (oldValue-min)/(max-min)将任意取值范围的特征值转化为0到1间的值
###归一化特征值
def autoNorm(dataSet):
minVals = dataSet.min(0) #对dataset数据集的列求最小值
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0] #第一维的长度,矩阵行数
normDataSet = dataSet - tile(minVals,(m,1))
normDataSet = normDataSet/tile(ranges,(m,1))
return normDataSet, ranges, minVals
注意:特征值矩阵有1000x3个值,而minVals和range的值都为1x3,因此使用NumPy库中tile()函数将变量内容复制成输入矩阵同样大小的矩阵。这里是具体特征值相除。
在NumPy中,若是矩阵除法,需要使用函数linalg.solve(matA,matB)
normMat,ranges,minVals = autoNorm(datingDataMat)
print(normMat)
print(ranges)
print(minVals)
运行结果
[[ 0.44832535 0.39805139 0.56233353]
[ 0.15873259 0.34195467 0.98724416]
[ 0.28542943 0.06892523 0.47449629]
...,
[ 0.29115949 0.50910294 0.51079493]
[ 0.52711097 0.43665451 0.4290048 ]
[ 0.47940793 0.3768091 0.78571804]]
[ 9.12730000e+04 2.09193490e+01 1.69436100e+00]
[ 0. 0. 0.001156]
4、测试算法:作为完整程序验证分类器
机器学习算法一个很重要的工作就是评估算法的正确率,通常只提供已有数据的90%作为训练样本来训练分类器,剩余10%去测试分类器,检测分类器的正确率。
###分类器针对约会网站的测试代码
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet.txt') #处理读取文件数据
normMat,ranges,minVals = autoNorm(datingDataMat) #归一化特征值
m = normMat.shape[0]
numTestVecs = int(m*hoRatio) #测试集个数
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
#分类器执行处理
print("the classifier came back with: %d, the real answer is: %d" %(classifierResult,datingLabels[i]))
if(classifierResult != datingLabels[i]):
errorCount += 1.0
print("the total error rate is: %f" %(errorCount/float(numTestVecs)))
>>>the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
……
the classifier came back with: 3, the real answer is: 1
the total error rate is: 0.050000
将k值改为6:
>>>the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the total error rate is: 0.060000
我们可以改变函数datingClassTest内变量hoRatio和变量k的值,检测错误率是否随着变量值的变化而增加。依赖于分类算法、数据集和程序设置,分类器的输出结果可能有很大的不同。
5、使用算法:构建完整可用系统
###约会网站预测函数
def classifyPerson():
resultList = ['not at all','in small doses','in large doses']
percentTats = float(input("percentage of time spent playing video games?"))
ffMiles = float(input("frequent flier miles earned per year?"))
iceCream = float(input("liters of ice cream consumed per year?"))
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
inArr = array([ffMiles,percentTats,iceCream])
classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
print("You will probably like this person:",resultList[classifierResult - 1])
python2中input()函数,数字格式直接输入,字符格式必须加上单引号或者双引号;
python2中raw_input()函数,无论输入什么,都默认为字符格式
python3中用input()函数整合了2中的input()和raw_input()函数,删除了2中input,功能同2中的raw_input()函数。
运行效果如下:
k-近邻算法实现了对约会网站配对效果的改进!