'''
KNN 算法流程:
1、计算已知类别数据集中的点与当前点之间的距离
2、按照距离从小到大顺序排序
3、选取与当前点距离最小的k个点
4、统计前k个点所在类别的出现次数
5、出现次数最高的类别作为当前点的类别
'''
from numpy import *
from os import listdir #列出给点目录的文件名
import operator
#创建数据集和标签
def creatDataSet():
group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels=['A','A','B','B']
return group,labels
'''
KNN分类算法
inX 用于分类的输入向量
dataSet 训练集
labels 标签向量
k 选择最近邻居的数目
'''
def classify0(inX,dataSet,labels,k):
#计算当前点与数据集中各个点的距离
dataSetSize=dataSet.shape[0] #shape[0]返回数据集矩阵行数, sharp[1]返回列数
diffMat=tile(inX,(dataSetSize,1))-dataSet #将inX扩展为 dataSetSize*1(1表示1倍inX列数) [(x1-x2),(y1-y2)]
sqDiffMat=diffMat**2 #数组中对应元素平方 [(x1-x2)^2,(y1-y2)^2]
sqDistances=sqDiffMat.sum(axis=1) # axis=0 按行求和,axis=1 按列求和
distances=sqDistances**0.5
#按距离从小到大排序
sortedDistIndicies=distances.argsort() #argsort为排序,元素从小到大的顺序返回下标。 eg. [3,1,2] 返回的是[2,1,0]
classCount={}
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]] #返回距离对应的类别
classCount[voteIlabel]=classCount.get(voteIlabel,0) +1 #get提取字典里的元素,计算离目标点最近的K个点的类别,这个点是那个类那个类就加1
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
'''
对文本操作,处理样本集输入格式
输入为文件名字符串,输出为训练样本矩阵和类标签向量
'''
def file2matrix(filename):
fr=open(filename) #打开文件
arrayOLines=fr.readlines() #读取整个文件并保存为列表,每一行为列表的一个元素
numberOfLines =len(arrayOLines) #数组的行数即为样本的个数(行数)
returnMat=zeros((numberOfLines,3)) #创建 numberOLines*3 的0矩阵
classLabelVector=[] #创建一个空列表存放训练集标签
index=0
for line in arrayOLines: #依次读取每行
line=line.strip() #去掉回车字符
listFromLine=line.split('\t') #每一行数据用tab分割
returnMat[index,:]=listFromLine[0:3] #把分隔好的数据存放到数据集中
classLabelVector.append(int(listFromLine[-1])) #把样本对应的标签放入标签集中,-1表示最后一列
index +=1
return returnMat,classLabelVector
#归一化样本集
def autoNorm(dataSet):
minVals=dataSet.min(0) #求每列最小值
maxVals=dataSet.max(0) #求每列最大值
ranges=maxVals-minVals
normDataSet=zeros(shape(dataSet)) #生成dataSet同维度的0矩阵,用来保存归一化后的数据集
m=dataSet.shape[0] #返回dataSet的列数
normDataSet=dataSet-tile(minVals,(m,1))
normDataSet=normDataSet/tile(ranges,(m,1))
return normDataSet,ranges,minVals
#测试代码,选择多少数据测试分类器
def datingClassTest():
hoRatio = 0.10 #从数据集中抽取100个样本作为测试集
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt') #调用函数处理数据集
normMat,ranges,minVals=autoNorm(datingDataMat) #归一化数据集normMat
m=normMat.shape[0] #统计数据集行数
numTestVecs=int(m*hoRatio) #测试集样本数
errorCount=0.0 #分类错误统计
for i in range(numTestVecs): #对测试集进行测试
#输入参数:normMat[i,:]为测试样本集,表示归一化后的第i行数据
# normMat[numTestVecs:m,:]为训练样本集,样本数量为(m-numTestVecs)个
# datingLabels[numTestVecs:m]为训练样本对应的标签
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 totle error rate is: %f' %(errorCount/float(numTestVecs)))
def classifyPerson():
resultList = ['Not at all','In small doses','In large doses']
percentTats = float(input('prencentage of time spent playing video games?'))
ffMiles = float(input('frequent flier miles earned per years?'))
iceCrean = float(input('liters of ice cream consumed per years?'))
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
inArr = array([ffMiles,percentTats,iceCrean])
classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
print('You will probably like this persion: ',resultList[classifierResult-1])
"""-----------------------------------------手写体测试程序-------------------------------------------"""
#将图片转换为向量
def img2vector(fliename):
returnVect = zeros((1,1024))
fr = open(fliename)
for i in range(32):
linstr = fr.readline()
for j in range(32):
returnVect[0,32*i+j] = int(linstr[j])
return returnVect
def handwritingClassTest():
hwLabels = [] #保存标签向量
trainingFileList = listdir('trainingDigits') #将文件夹trainingDigits下的文件名保存在列表trainingFileList中格式为 文件名.扩展名
m = len(trainingFileList) #训练集目录中文件的个数
trainingMat = zeros((m,1024)) #初始化一个2维矩阵,将训练集的所有数据放入其中
for i in range(m):
fileNameStr = trainingFileList[i] #返回训练集目录中第i个文件的文件名,是字符串,不是文件的内容 eg. 4_5.txt
filestr = fileNameStr.split('.')[0] #以'.'分割fileNameStr,并取出第0个。 eg. fileNameStr = '4_5.txt', filestr = '4_5'
classNumStr = int(filestr.split('_')[0]) #以'_'分割,取出第0个,并转换为int型,即为标签。 eg. classNumStr = 4
hwLabels.append(classNumStr) #保存标签向量
trainingMat[i,:]=img2vector('trainingDigits/%s' %fileNameStr)
testFileList = listdir('testDigits')
errorCount = 0.0
mTest = len(testFileList) #测试样本集文件数
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
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 (classNumStr != classifierResult):
errorCount += 1.0
print('\n the total number error is: %d' %errorCount)
print('\n the total error rate is: %f' %(errorCount/float(mTest)))
以下是通过调用 sklearn 实现对数据集 KNN 分类:
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 20 20:04:34 2018
@author: zhangsh
"""
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.cross_validation import train_test_split
from sklearn import preprocessing
# 1、导入数据集
'''
numpy.loadtxt(fname, dtype=, comments='#', delimiter=None, \
converters=None, skiprows=0, usecols=None, \
unpack=False, ndmin=0)
'''
data = np.loadtxt('datingTestSet2.txt')
m,n = data.shape
X = data[:,:3]
y = data[:,-1].reshape(m,1)
# 2、数据预处理:归一化 (0,1)
X = preprocessing.scale(X)
X_train,X_test,y_train,y_test = train_test_split(X, y, test_size = 0.3, random_state = 40)
# 3、建立模型
knn = KNeighborsClassifier(n_neighbors = 3) # 创建 KNN 模型
knn.fit(X_train, y_train) # 训练
classifiedData = knn.predict(X_test) # 测试分类器
errorCount = 0.0
for i in range(classifiedData.size):
if classifiedData[i] != y_test[i] :
errorCount += 1
errorClassifiedRate = errorCount/classifiedData.size
print('分类器分类结果',classifiedData)
print('分类出错个数:',errorCount)
print('出错率:',errorClassifiedRate)