KNN约会网站算法,
首先读取样本数据,得到样本矩阵和标签,然后把样本归一(用(A-min/)/(max-min)),利用矩阵把每个样本归一,返回归一后的矩阵,范围(max-min),以及最小值。然后是KNN算法,传入测试样本,传入训练样本,传入训练标签。然后利用求矩阵中的距离差,排序。把最相近的前K个挑出来,然后累计对应标签的个数,标签出现最多的就是最符合的。
读取样本数据,
首先读取,用open()函数,按行读取用readlines(),得出样本行数,然后用zeros()函数创建矩阵数值都为0的矩阵,然后按行把读取的数据放入returnmat数组中,因为设置最后一列为标签,所以把标签加入到classlabelvector中,返回矩阵和标签。
1 def file2matrix(filename): 2 """ 3 从文件中读入训练数据,并存储为矩阵 4 """ 5 fr = open(filename) 6 arrayOlines = fr.readlines() 7 numberOfLines = len(arrayOlines) #获取 n=样本的行数 8 returnMat = zeros((numberOfLines,3)) #创建一个2维矩阵用于存放训练样本数据,一共有n行,每一行存放3个数据
#该函数功能是创建numberOfLines*3类型的矩阵,并初始化为0
9 classLabelVector = [] #创建一个1维数组用于存放训练样本标签。 10 index = 0 11 for line in arrayOlines: 12 # 把回车符号给去掉 13 line = line.strip() 14 # 把每一行数据用\t分割 15 listFromLine = line.split('\t') 16 # 把分割好的数据放至数据集,其中index是该样本数据的下标,就是放到第几行 17 returnMat[index,:] = listFromLine[0:3] 18 # 把该样本对应的标签放至标签集,顺序与样本集对应。对于python数组,取0.1.2,就要写0-3,对于python二维数组,取法是,第一个为行数,逗号后为第几个,冒号代表取所有 19 classLabelVector.append(int(listFromLine[-1])) 20 index += 1 21 return returnMat,classLabelVector 22
数据归一
利用(样本数据-min)/(max-min),max和min是测试样本中最大的和最小的,最后返回归一后的矩阵,和范围以及最小值。用到shape()函数,shape函数为矩阵的函数,也可以用作创建一维函数。shape(0)表示矩阵行数,shape(1)表示矩阵列数,然后利用tile()函数,tile函数用法如下,主要是为了一次减完,得到减后的矩阵,这里面tile(传入min,以及m(有多少行)和最小的min(这是一行数据)
# tile属于numpy模块下边的函数 # tile(A, reps)返回一个shape=reps的矩阵,矩阵的每个元素是A # 比如 A=[0,1,2] 那么,tile(A, 2)= [0, 1, 2, 0, 1, 2] # tile(A,(2,2)) = [[0, 1, 2, 0, 1, 2], # [0, 1, 2, 0, 1, 2]] # tile(A,(2,1,2)) = [[[0, 1, 2, 0, 1, 2]], # [[0, 1, 2, 0, 1, 2]]] # 上边那个结果的分开理解就是: # 最外层是2个元素,即最外边的[]中包含2个元素,类似于[C,D],而此处的C=D,因为是复制出来的 # 然后C包含1个元素,即C=[E],同理D=[E] # 最后E包含2个元素,即E=[F,G],此处F=G,因为是复制出来的 # F就是A了,基础元素 # 综合起来就是(2,1,2)= [C, C] = [[E], [E]] = [[[F, F]], [[F, F]]] = [[[A, A]], [[A, A]]] # 这个地方就是为了把输入的测试样本扩展为和dataset的shape一样,然后就可以直接做矩阵减法了。 # 比如,dataset有4个样本,就是4*2的矩阵,输入测试样本肯定是一个了,就是1*2,为了计算输入样本与训练样本的距离 # 那么,需要对这个数据进行作差。这是一次比较,因为训练样本有n个,那么就要进行n次比较; # 为了方便计算,把输入样本复制n次,然后直接与训练样本作矩阵差运算,就可以一次性比较了n个样本。
def autoNorm(dataSet): """ 训练数据归一化 """ # 获取数据集中每一列的最小数值 # 以createDataSet()中的数据为例,group.min(0)=[0,0] minVals = dataSet.min(0) # 获取数据集中每一列的最大数值 # group.max(0)=[1, 1.1] maxVals = dataSet.max(0) # 最大值与最小的差值 ranges = maxVals - minVals # 创建一个与dataSet同shape的全0矩阵,用于存放归一化后的数据, normDataSet = zeros(shape(dataSet)) m = dataSet.shape[0] normDataSet = dataSet - tile(minVals, (m,1)) # 把最大最小差值扩充为dataSet同shape,然后作商,是指对应元素进行除法运算,而不是矩阵除法。 # 矩阵除法在numpy中要用linalg.solve(A,B) normDataSet = normDataSet/tile(ranges, (m,1)) return normDataSet, ranges, minVals
KNN核心算法
中间所用到函数的注释,
classCount.items(),把字典转换成二维数组,operator函数的用法如下。返回最好的选择。
a = [1,2,3] >>> b=operator.itemgetter(1) //定义函数b,获取对象的第1个域的值 >>> b(a) 2 >>> b=operator.itemgetter(1,0) //定义函数b,获取对象的第1个域和第0个的值 >>> b(a) (2, 1) 要注意,operator.itemgetter函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取值。 sorted函数 Python内置的排序函数sorted可以对list或者iterator进行排序,官网文档见:http://docs.python.org/2/library/functions.html?highlight=sorted#sorted,该函数原型为: sorted(iterable[, cmp[, key[, reverse]]]) 参数解释: (1)iterable指定要排序的list或者iterable,不用多说; (2)cmp为函数,指定排序时进行比较的函数,可以指定一个函数或者lambda函数,如: students为类对象的list,没个成员有三个域,用sorted进行比较时可以自己定cmp函数,例如这里要通过比较第三个数据成员来排序,代码可以这样写: students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] sorted(students, key=lambda student : student[2]) (3)key为函数,指定取待排序元素的哪一项进行排序,函数用上面的例子来说明,代码如下: sorted(students, key=lambda student : student[2]) key指定的lambda函数功能是去元素student的第三个域(即:student[2]),因此sorted排序时,会以students所有元素的第三个域来进行排序。 有了上面的operator.itemgetter函数,也可以用该函数来实现,例如要通过student的第三个域排序,可以这么写: sorted(students, key=operator.itemgetter(2)) sorted函数也可以进行多级排序,例如要根据第二个域和第三个域进行排序,可以这么写: sorted(students, key=operator.itemgetter(1,2)) 即先跟句第二个域排序,再根据第三个域排序。 (4)reverse参数就不用多说了,是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时将按降序排列。
dataSetSize = dataset.shape[0] """ 下面的求距离过程就是按照欧氏距离的公式计算的。 即 根号(x^2+y^2) """ # tile属于numpy模块下边的函数 # tile(A, reps)返回一个shape=reps的矩阵,矩阵的每个元素是A # 比如 A=[0,1,2] 那么,tile(A, 2)= [0, 1, 2, 0, 1, 2] # tile(A,(2,2)) = [[0, 1, 2, 0, 1, 2], # [0, 1, 2, 0, 1, 2]] # tile(A,(2,1,2)) = [[[0, 1, 2, 0, 1, 2]], # [[0, 1, 2, 0, 1, 2]]] # 上边那个结果的分开理解就是: # 最外层是2个元素,即最外边的[]中包含2个元素,类似于[C,D],而此处的C=D,因为是复制出来的 # 然后C包含1个元素,即C=[E],同理D=[E] # 最后E包含2个元素,即E=[F,G],此处F=G,因为是复制出来的 # F就是A了,基础元素 # 综合起来就是(2,1,2)= [C, C] = [[E], [E]] = [[[F, F]], [[F, F]]] = [[[A, A]], [[A, A]]] # 这个地方就是为了把输入的测试样本扩展为和dataset的shape一样,然后就可以直接做矩阵减法了。 # 比如,dataset有4个样本,就是4*2的矩阵,输入测试样本肯定是一个了,就是1*2,为了计算输入样本与训练样本的距离 # 那么,需要对这个数据进行作差。这是一次比较,因为训练样本有n个,那么就要进行n次比较; # 为了方便计算,把输入样本复制n次,然后直接与训练样本作矩阵差运算,就可以一次性比较了n个样本。 # 比如inX = [0,1],dataset就用函数返回的结果,那么 # tile(inX, (4,1))= [[ 0.0, 1.0], # [ 0.0, 1.0], # [ 0.0, 1.0], # [ 0.0, 1.0]] # 作差之后 # diffMat = [[-1.0,-0.1], # [-1.0, 0.0], # [ 0.0, 1.0], # [ 0.0, 0.9]] diffMat = tile(inX, (dataSetSize, 1)) - dataset # diffMat就是输入样本与每个训练样本的差值,然后对其每个x和y的差值进行平方运算。 # diffMat是一个矩阵,矩阵**2表示对矩阵中的每个元素进行**2操作,即平方。 # sqDiffMat = [[1.0, 0.01], # [1.0, 0.0 ], # [0.0, 1.0 ], # [0.0, 0.81]] sqDiffMat = diffMat ** 2 # axis=1表示按照横轴,sum表示累加,即按照行进行累加。 # sqDistance = [[1.01], # [1.0 ], # [1.0 ], # [0.81]] sqDistance = sqDiffMat.sum(axis=1) # 对平方和进行开根号 distance = sqDistance ** 0.5 # 按照升序进行快速排序,返回的是原数组的下标。 # 比如,x = [30, 10, 20, 40] # 升序排序后应该是[10,20,30,40],他们的原下标是[1,2,0,3] # 那么,numpy.argsort(x) = [1, 2, 0, 3] sortedDistIndicies = distance.argsort() # 存放最终的分类结果及相应的结果投票数 classCount = {} # 投票过程,就是统计前k个最近的样本所属类别包含的样本个数 for i in range(k): # index = sortedDistIndicies[i]是第i个最相近的样本下标 # voteIlabel = labels[index]是样本index对应的分类结果('A' or 'B') voteIlabel = labels[sortedDistIndicies[i]] # classCount.get(voteIlabel, 0)返回voteIlabel的值,如果不存在,则返回0 # 然后将票数增1 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 # 把分类结果进行排序,然后返回得票数最多的分类结果 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
主函数
def datingClassTest(): # 将数据集中10%的数据留作测试用,其余的90%用于训练 hoRatio = 0.10 datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #load data setfrom file 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, result is :%s" % (classifierResult, datingLabels[i],classifierResult==datingLabels[i])) if (classifierResult != datingLabels[i]): errorCount += 1.0 print("the total error rate is: %f" % (errorCount/float(numTestVecs))) print(errorCount)
....这都是还没有实现的算法,只是作者理解的思路..和基本知识点,用来做笔记的。。。各位看官砍砍价就好,欢迎指导。