原理:
存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
KNN的一般流程:
(1)收集数据集,特征和类别标签
(2)准备数据,数据的归一化,去除坏数据等
(3)构造输入数据集,运用KNN判定数据的分类类别
(4)计算错误率,获得比较合适的K值
(5)输入需要预测分类的数据集
乳腺癌判断实例:
(1)获取数据:
import pandas as pd import numpy as np name_c=['sample code number','clump thickness', 'uniformity of cell size', 'uniformity of cell shape','marginal adhesion','single epithelial cell', 'bare nuclei','bland chromatin','normal nucleoli','mitoses','class' ] data=pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data',names=name_c) data=data.replace(to_replace='?',value=np.nan) data=data.dropna(how='any') l=[] ###第7列数据是字符形式,这里为了简便就不要那一列了 for i in name_c[1:10]: l.append(data[i ].values) if i!=name_c[6] else 0
(2)数据处理:
#数据归一化函数
def autoNorm(dataSet): #获得数据的最大最小值 print ("**********************") minVals = dataSet.min(0) maxVals = dataSet.max(0) print ("minValues:",minVals) print ("maxValuse:",maxVals) #计算最大最小值的差 ranges = maxVals - minVals #shape(dataSet)返回dataSet的矩阵行列数 normDataSet=np.zeros(np.shape(dataSet)) #返回dataSet的行数 m = dataSet.shape[0] #原始值减去最小值 normDataSet=dataSet-np.tile(minVals,(m,1)) #除以最大值和最小值的差,得到的归一化的数据 normDataSet = normDataSet/np.tile(ranges,(m,1)) print (normDataSet) return normDataSet,ranges,minVals
(3)kNN算法,计算欧式距离,选择距离最小的前k个点,并返回分类结果。
def classify(intX,dataSet,labels,k): ''' KNN算法 ''' #numpy中shape[0]返回数组的行数,shape[1]返回列数 dataSetSize = dataSet.shape[0] #将intX在横向重复dataSetSize次,纵向重复1次 #例如intX=([1,2])--->([[1,2],[1,2],[1,2],[1,2]])便于后面计算 diffMat = np.tile(intX,(dataSetSize,1))-dataSet #二维特征相减后乘方 sqdifMax = diffMat**2 #计算距离 seqDistances = sqdifMax.sum(axis=1) distances = seqDistances**0.5 #print ("distances:",distances) #返回distance中元素从小到大排序后的索引 sortDistance = distances.argsort() #print ("sortDistance:",sortDistance) classCount = {} for i in range(k): #取出前k个元素的类别 voteLabel = labels[sortDistance[i]] classCount[voteLabel] = classCount.get(voteLabel,0)+1 #dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。 #计算类别次数 #key=operator.itemgetter(1)根据字典的值进行排序 #key=operator.itemgetter(0)根据字典的键进行排序 #reverse降序排序字典 sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True) #print(intX) print ("sortedClassCount:",sortedClassCount) return sortedClassCount[0][0]
(4)数据的分类测试
def datingClassTest(datingDataMat,datingLabels,k=10): #取所有数据的10% hoRatio = 0.1 #数据归一化,返回归一化后的矩阵,数据范围,数据最小值 normMat,ranges,minVals = autoNorm(datingDataMat) #获得nornMat的行数 m = normMat.shape[0] #百分之十的测试数据的个数 numTestVecs = int(m*hoRatio) #分类错误计数 errorCount = 0.0 for i in range(numTestVecs): #前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集 classifierResult = classify(normMat[i,:],normMat[numTestVecs:m,:], datingLabels[numTestVecs:m],k) print ("分类结果:%d \t真实类别:%d"%(classifierResult,datingLabels[i])) if classifierResult != datingLabels[i]: errorCount += 1.0 print ("错误率:%f"%(errorCount/float(numTestVecs)*100))
datingClassTest(np.array(l).T,data[name_c[10]].values,10)
缺点:如果数据大,需要耗费大量内存和运算时间。无法给出分类的典型特征。