1.k-近邻算法
# k近邻算法:classify0(用于分类的输入向量inX,输入的训练样本dataset,标签向量labels,选择最近邻居的数目k)
# first step:计算待分类向量和所有样本点的距离;
# second step:对该距离数据从小到大排序,确定前k个距离最小示例的标签;
# third step:返回发生频率最高的元素标签.
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0] # dataSetSize = 训练样本行数
diffMat = tile(inX, (dataSetSize,1)) - dataSet # 将输入inX扩展成训练样本行数*3(和训练样本大小一样)
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1) # axis = 1,对每一行求和(对列操作)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort() # 对序列排序,并返回对应原序列索引
classCout = {} # 统计标签个数
for i in range (k):
voteIlabel = labels[sortedDistIndicies[i]] # 索引为i的示例的标签
classCout[voteIlabel] = classCout.get(voteIlabel,0) + 1 # 该标签个数+1
sortedClaassCout = sorted(classCout.items(), # 按照第2个域(即标签类别)降序排列
key=operator.itemgetter(1), reverse=True)
return sortedClaassCout[0][0]
Python语法
np.argsort()
np.argsort(x, axis)
对x进行升序排列,将排序后的序列对应原序列索引赋给x。axis=0,按列排序;axis=1,按行排序。
# 一维数组
>>> x = array([0.3, 0.5, 0.8, 0.4])
>>> y = x
>>> np.argsort(x)
array([0, 3, 1, 2], dtype=int64)
>>> np.argsort(-y)
array([2, 1, 3, 0], dtype=int64)
# 二维数组
>>> x = [[0, 3], [1, 2]]
>>> y = x
>>> np.argsort(x, axis = 0) # 升序/按列排列
array([[0, 1],
[1, 0]], dtype=int64)
>>> np.argsort(y, axis = 1) # 升序/按行排列
array([[0, 1],
[0, 1]], dtype=int64)
dict.get()
dict.get(key, default=None)
返回字典中指定key对应的值,如果值不在字典中则返回default。
>>> dict = {'Name': 'Zara', 'Age': 27}
>>> print ("Value : %s" % dict.get('Age'))
Value : 27
>>> print ("Value : %s" % dict.get('Sex', "Never"))
Value : Never
dict.items()
dict.items()
以列表返回可遍历的(键, 值) 对/字典。
>>> dict = {'Google': 'www.google.com', 'Runoob': 'www.runoob.com', 'taobao': 'www.taobao.com'}
>>> print "字典值 : %s" % dict.items()
字典值 : [('Google', 'www.google.com'), ('taobao', 'www.taobao.com'), ('Runoob', 'www.runoob.com')]
>>> for key,values in dict.items():
print (key,values)
Google www.google.com
taobao www.taobao.com
Runoob www.runoob.com
sorted()
sorted(iterable, key=None, reverse=False)
对可迭代对象iterable进行排序操作。key可以指定迭代对象中的某个元素进行排序。reverse=False,升序(默认);reverse=True,降序。
sorted()和sort()区别
# 1.sort()对已有list进行排序,会改变list的值,sorted()则返回一个新的list
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
>>> a=[5,2,3,1,4]
>>> a.sort()
>>> a
[1,2,3,4,5]
# 2.sort()只能用于list,sorted()适用于所有可迭代对象
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
利用key进行特殊排序
# 1.利用key进行倒序排序
>>> example_list = [5, 0, 6, 1, 2, 7, 3, 4]
>>> result_list = sorted(example_list, key=lambda x: x*-1) # x的相反数
>>> print(result_list)
[7, 6, 5, 4, 3, 2, 1, 0]
# 2.利用key选择排序的域/维度
>>> import operator
>>> dict = {'Lena':18, 'John':20, 'Mark':15, 'Jackson':23}
>>> sorted(dict, key=operator.itemgetter(1),reverse=True) # 按第二个域,即数字进行降序排列
[('Jackson', 23), ('John', 20), ('Lena', 18), ('Mark', 15)]
2.准备数据:从文本文件中解析数据
# 将文本记录转换为分类器可以接受的格式
# first step:获取文本记录行数
# second step:创建一个二维数组(行数*3)
# third step:将文本每一行末尾末尾元素换成\t
# PS:注释行,源程序直接用int转换标签,出现错误,这里用一个字典进行转换
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines,3))
classLabelVector = []
index = 0
labels = {'didntLike':1,'smallDoses':2,'largeDoses':3} # 新增 这行代码可以放到for的开始,不需要每次都重新赋值
for line in arrayOLines:
line = line.strip() # 移除line末尾元素
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
# classLabelVector.append(int(listFromLine[-1])) #classLabelVector储存的应为int
classLabelVector.append(labels[listFromLine[-1]]) # 去掉了int
index +=1
return returnMat,classLabelVector
Python语法
file.readlines()
file.readline(size) # 不指定size,则默认读取首行;指定size,则读取指定size字节(包括\n字符)
file.readlines() # 读取file所有行,以list形式返回
str.strip()
str.strip([chars])
移除字符串str开头和结尾的字符或字符串chars(默认为空格),无法删除字符串中间的字符。
>>> str = "*****this is **string** example....wow!!!*****"
>>> print (str.strip( '*' )) # 指定字符串 *
this is **string** example....wow!!!
>>> str = "123abcrunoob321"
>>> print (str.strip( '12' )) # 字符序列为 12
3abcrunoob3
str.split()
str.split(char="", num=string.count(str))
用指定分隔符char对字符串进行切片,分割为num+1的子字符串,num默认为-1,即切割所有。
>>> str = "this is string example....wow!!!"
>>> print (str.split( )) # 以空格为分隔符
['this', 'is', 'string', 'example....wow!!!']
>>> print (str.split('i',1)) # 以 i 为分隔符
['th', 's is string example....wow!!!']
>>> print (str.split('w')) # 以 w 为分隔符
['this is string example....', 'o', '!!!']
list.append()
list.append(obj)
在list末尾添加新的对象。该方法无返回值,会直接改变list的值。
list.append()和list.extend()区别
>>> mylist = [10,20,30]
>>> mylist.extend([40,50,60])
>>> mylist
[10, 20, 30, 40, 50, 60]
>>> mylist.append([70,80,90])
>>> mylist
[10, 20, 30, 40, 50, 60, [70, 80, 90]]
append()向list尾部添加一个元素,而extend()只接受一个列表作为参数,并将该参数的每个元素都添加到原有的列表中。该例也说明一个list可包含不同类型。
3.准备数据:归一化数值
# 归一化数值:消除由于某一项数值过大带来的误差影响
# 将取值范围处理为0——1区间内的值
# newvalue = (oldvalue-min)/(max-min)
def autoNorm(dataSet):
minVals = dataSet.min(0) # 找出每列最小值,minVal为1*3矩阵
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet)) # normDataSet和dataSet为1000*3矩阵
m = dataSet.shape[0] # m=1000,即dataSet的行数
normDataSet = dataSet - tile(minVals, (m,1)) #用tile,将minVal行数重复m次
normDataSet = normDataSet/tile(ranges, (m,1)) #每个元素相除,不是矩阵除法
return normDataSet, ranges, minVals
Python语法
np.tile()
tile(A, reps)
A和reps均为数组,将数组A重复reps次
>>> from numpy import *
>>> A = [1,2]
>>> tile(A,(3,2)) #将A的行重复3次,列重复两次
array([[1, 2, 1, 2],
[1, 2, 1, 2],
[1, 2, 1, 2]])
4.测试算法:针对约会网站的测试
# 分类器针对约会网站的测试代码
# first step:读取训练样本(file2matrix函数)并归一化(autoNorm函数)
# sencond step:计算测试数据的数量,即计算哪些是测试样本(0.1),哪些是训练样本(0.9)
# third step:将测试样本和训练样本输入到classify0函数
# forth step:计算错误率(错误样本数/总样本数)并输出
def datingClassTest():
hoRatio = 0.1
datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
normMat, ranges, minVals = autoNorm(datingDataMat) # 调用定义的归一化函数
m = normMat.shape[0] # m = 1000(样本总行数)
numTestVecs = int(m*hoRatio) # 测试集数量占总量的0.1
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:], datingLabels[numTestVecs:m],3) #调用定义的kNN分类函数
print ("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i])) # %d:整数;%s:字符串
if(classifierResult != datingLabels[i]):
errorCount += 1.0
print ("the total error rate is: %f" %(errorCount/float(numTestVecs))) # %f:错误率为浮点数
5.使用算法:约会网站预测
# 约会网站预测函数(和测试代码kNN.datingClassTest()基本相同)
# first step:获取测试用户数据(1*3)
# second step:读取训练样本(file2matrix函数)并归一化(autoNorm函数)
# third step:将测试用户数据归一化,并将其和训练样本数据输入到classify0函数
def classifyPerson():
resultList = ['not at all', 'in small doses', 'in large doses']
percentTats = float(input("percentage of time spent playing video games?")) # 输入用户数据(py3.*用input, py2.*用raw_input)
ffMiles = float(input("frequent flier miles earned per year?"))
iceCream = float(input("liters of ice cream consumed per year?"))
inArr = array([ffMiles, percentTats, iceCream])
datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
classifierResult = classify0((inArr-minVals)/ranges, datingDataMat, datingLabels, 3)
print("You will probably like this person: ",resultList[classifierResult - 1])
Python语句
input()
input([prompt]) # prompt为提示信息
在 Python3.x 中 raw_input() 和 input() 进行了整合,去除了 raw_input( ),仅保留了input( )函数,其接收任意任性输入,将所有输入默认为字符串处理,并返回字符串类型。
>>>a = input("input:")
input:123 # 输入整数
>>> type(a)
<class 'str'> # 字符串
>>> a = input("input:")
input:runoob # 正确,字符串表达式
>>> type(a)
<class 'str'> # 字符串
6.示例:手写识别系统
准备数据
# 将手写数字图像转换为向量
def img2vector(filename):
returnVect = zeros((1,1024)) # 32*32=1024,构建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]) #这里必须加int,lineStr[j]是字符串
return returnVect
测试算法
# 手写数字识别测试程序
# first step:获取trainingDigits目录中的文件内容,存入trainingFileList
# second step:构建m*1024矩阵,该矩阵每行储存一个图像(32*32)
# third step:从文件名中解析出分类数字,存入hwLabels
# forth step:相同的方法获取测试样本,将其送入classifier分类
def handwritingClassTest():
hwLabels = []
# trainingFileList = open('traningDigits')
trainingFileList = listdir('trainingDigits')
m = len(trainingFileList)
trainingMat = zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] # 相当于剔除后缀,e.g. 0_13.txt,fileStr=0_13
classNumStr = int(fileStr.split('_')[0]) # 获取数字类别,e.g. 0_13.txt,fileStr=0_13,classNumStr=0
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
# testFileList = open('testDigits')
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])
classNumStr = int(fileNameStr.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 error is: %d" %errorCount)
print("\nthe total error rate is: %f" %(errorCount/float(mTest)))
测试结果
>>> import kNN
>>> kNN.handwritingClassTest()
...
...
the classifier came back with: 9, the real answer is: 9
the classifier came back with: 9, the real answer is: 9
the classifier came back with: 9, the real answer is: 9
the total number of error is: 10
the total error rate is: 0.010571
总结
k-近邻算法采用测量不同特征值之间的距离方法进行分类。
优点: 精度高,对异常值不敏感,无数据输入假定。
缺点: 计算复杂度高,空间复杂度高。如在手写数字识别示例中,2000个训练样本,那么算法要为每个测试向量做2000次距离运算,每次距离运算又包括1024维度浮点运算,也就需要为测试向量准备2MB的存储空间。
适用数据范围: 数值型和标称型。数值型数据变量 可在无尽范围内取值,如概率,主要用于回归;标称型数据变量 则在有限范围内取值,如真/假,适用于分类。