k临近算法(kNN)采用测量不同特征值之间的距离方法进行分类,也是一种非常直观的方法。本文主要记录了使用kNN算法改进约会网站的例子。
任务一:分类算法classify0
就是使用距离公式计算特征值之间的距离,选择最邻近的k个点,通过统计这k个点的结果来得出样本的预测值。
def
classify0
(inX,dataset,labels,k)
:
#shape 返回行列数,shape[0]是行数,有多少元组
datasetsize = dataset.shape[
0
]
#tile 复制inX,使其与dataset一样大小
diffmat = tile(inX,(datasetsize,
1
)) - dataset
#**表示乘方
sqdiffmat = diffmat **
2
#按行将计算结果求和
sqdistances = sqdiffmat.sum(axis=
1
)
distances = sqdistances **
0.5
#使用argsort排序,返回索引值
sortedDistIndicies = distances.argsort()
#用于计数,计算结果
classcount = {}
for
i
in
range(k) :
voteIlabel = labels[sortedDistIndicies[i]]
classcount[voteIlabel] = classcount.get(voteIlabel,
0
)+
1
#按照第二个元素降序排列
sortedClasscount = sorted(classcount.iteritems(),key=operator.itemgetter(
1
),reverse=
True
)
#返回出现次数最多的那一个label的值
return
sortedClasscount[
0
][
0
]
1、get()方法语法:
dict
.
get
(
key
,
default
=
None
)
key -- 字典中要查找的键。
default -- 如果指定键的值不存在时,返回该默认值值。
2、
sorted 语法:
sorted
(
iterable
[,
cmp
[,
key
[,
reverse
]]])
参数说明:
iterable -- 可迭代对象。
cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
3、
python字典的iteritems方法作用
与items方法相比作用大致相同,只是它的返回值不是列表,而是一个迭代器。
dict iteritems()操作方法
:
>>> f = x.iteritems()
>>> f
<dictionary-itemiterator object at 0xb74d5e3c>
>>> type(f)
<type 'dictionary-itemiterator'> #字典项的迭代器
>>> list(f)
字典.iteritems()方法在需要迭代结果的时候使用最适合,而且它的工作效率非常的高。
4、
tile函数用法
>>> numpy.tile([
0
,
0
],
5
)
#在列方向上重复[0,0]5次,默认行1次
array([
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
])
>>> numpy.tile([
0
,
0
],(
1
,
1
))
#在列方向上重复[0,0]1次,行1次
array([[
0
,
0
]])
>>> numpy.tile([
0
,
0
],(
2
,
1
))
#在列方向上重复[0,0]1次,行2次
array([[
0
,
0
],
[
0
,
0
]])
>>> numpy.tile([
0
,
0
],(
3
,
1
))
array([[
0
,
0
],
[
0
,
0
],
[
0
,
0
]])
5、
argsort函数
任务二:读入数据
注意这里书上写错了,应该读入的是datingTestSet2.txt而不是datingTestSet.txt
def
file2matrix
(filename)
:
fr = open(filename)
#打开文件,按行读入
arrayOLines = fr.readlines()
#获得文件行数
numberOfLines = len(arrayOLines)
#创建m行n列的零矩阵
returnMat = zeros((numberOfLines,
3
))
classLabelVector = []
index =
0
for
line
in
arrayOLines:
line = line.strip()
#删除行前面的空格
listFromLine = line.split(
'\t'
)
#根据分隔符划分
returnMat[index,:] = listFromLine[
0
:
3
]
#取得每一行的内容存起来
classLabelVector.append(int(listFromLine[-
1
]))
index +=
1
return
returnMat,classLabelVector
任务三:使用Matplotlib画图
安装Matplotlib时还需要numpy, dateutil, pytz, pyparsing, six, setuptools这几个包。可以在
这里
下载到,挺全的。加入到python27\Lib\site-packages目录下。
在powershell中cd到datingTestSet2.txt所在文件夹
输入python命令并且输入以下命令
粘贴:
import numpy
import matplotlib
import matplotlib.pyplot as plt
import k
NN
reload
(k
NN
)
datingDataMat
,
datingLabels
=
kNN
.
file2matrix
('dating
TestSet2
.txt')
fig
=
plt
.
figure
()
ax
=
fig
.
add_subplot
(
111
)
ax
.
scatter
(dating
DataMat
[:,
1
],dating
DataMat
[:,
2
],
15.0
*numpy.
array
(dating
Labels
),
15.0
*numpy.
array
(dating
Labels
))
plt
.
show
()
下面使用后两个特征的图片
将scatter函数修改为:
ax
.scatter
(datingDataMat[:,
0
],datingDataMat[:,
1
],
15.0
*numpy
.array
(datingLabels),
15.0
*numpy
.array
(datingLabels))
1
使用前两个特征的图片
任务四:归一化
免除较大数值的数据给分类带来的影响,将每一项数据归一化为0~1之间的数字。
def
autoNorm
(dataSet)
:
#找出样本集中的最小值
minVals = dataSet.min(
0
)
#找出样本集中的最大值
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
任务五:
分类并检验书中所给的数据
def
datingClassTest
()
:
#选取多少数据测试分类器
hoRatio =
0.10
#从datingTestSet2.txt中获取数据
datingDataMat,datingLabels = file2matrix(
'datingTestSet2.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))
print
errorCount
出错率为5%
任务六:将图像转换为测试向量
任务七:测试识别手写数字
def img2vector(filename): returnVect = zeros((1,1024)) fr = open(filename) for i in range(32): lineStr = fr.readline() for j in range(32): returnVect[0,32*i+j] = int(lineStr[j]) return returnVect def handwritingClassTest(): hwLabels = [] trainingFileList = listdir('trainingDigits') #load the training set m = len(trainingFileList) trainingMat = zeros((m,1024)) for i in range(m): fileNameStr = trainingFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) hwLabels.append(classNumStr) trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) testFileList = listdir('testDigits') #iterate through the test set errorCount = 0.0 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) vectorUnderTest = img2vector('testDigits/%s' % fileNameStr) classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr) if (classifierResult != classNumStr): errorCount += 1.0 print "\nthe total number of errors is: %d" % errorCount print "\nthe total error rate is: %f" % (errorCount/float(mTest))