机器学习之k-近邻算法案例实战
本博客讲解knn算法的实际案例运用,采用jupyter note逐步讲解,及后面用python进行封装,偏于读者对knn算法的整体了解。
本博客涉及两个小案例。
1、电影类别的分类
2、约会网站的分析
1、电影类别的分类
1.1 电影数据如图1
由图1知:
判断一个电影类型,我们用两个指标,打斗镜头和接吻镜头,通过这两个指标所占的数量来判断电影类型是动作片还是爱情片。
新电影的数据已知,我们采用knn近邻算法能够解决这个问题。
1.2 实现knn近邻算法(jupyter notebook)
#导入包
import pandas as pd
#1、导入数据
rowdata = {'电影名称':['无问西东','后来的我们','前任3','红海行动','唐人街探案','战狼2'],
'打斗镜头':[1,5,12,108,112,115],
'接吻镜头':[101,89,97,5,9,8],
'电影类型':['爱情片','爱情片','爱情片','动作片','动作片','动作片']}
#将数据转换成二维表格形式
movie_data = pd.DataFrame(rowdata)
movie_data
如图2:
#2、计算距离
#新电影数据
new_data = [24,67]
#计算距离
dist = list((((movie_data.iloc[:6,1:3] - new_data)**2).sum(1))**0.5)
dist
#3、将距离升序排序,然后选取距离最小的k个点
#将距离与标签对应
dist_label = pd.DataFrame({'dist':dist,'label':(movie_data.iloc[:6,1:3])})
dist_label
#升序
dist_label.sort_values(by = 'dist')
#4、确定前k个点所在类别的出现频率
k=4
dr = dist_label.sort_values(by = 'dist')[:k]
dr
如图3:
#5、确定前k个点所在类别的出现频率
re = dr.loc[:,'label'].value_counts()
re
#识别结果
result = []
result.append(re.index[0])
result
1.3 封装成函数
import pandas as pd
class Moviesort():
#1、构建数据
rowdata = {'电影名称':['无问西东','后来的我们','前任3','红海行动','唐人街探案','战狼2'],
'打斗镜头':[1,5,12,108,112,115],
'接吻镜头':[101,89,97,5,9,8],
'电影类型':['爱情片','爱情片','爱情片','动作片','动作片','动作片']}
movie_data = pd.DataFrame(rowdata)
#1、计算距离
def distance(self,new_data):
dist = list((((movie_data.iloc[:,1:3] - new_data)**2).sum(1))**0.5)
return dist
#2、找出前k个距离最近的值
def dist_label(self,k):
result = []
#将距离与标签对应
d_l = pd.DataFrame({'dist':dist,'label':(movie_data.iloc[:,3])})
#对距离值做升序
d_v = d_l.sort_values(by='dist')[:k]
#对前k个标签进行统计
label_num = d_v.loc[:,'label'].value_counts()
re = label_num.index[0]
result.append(re)
return result
def main():
m = Moviesort()
#new_data=[24,67]
m.distance([24,67])
print(dist)
#k=4
m.dist_label(4)
print(result)
if __name__ == '__main__':
main()
2、约会网站数据分析
2.1 加载约会网站数据
datingTest = pd.read_table('datingTestSet.txt',header=None)
datingTest.head()
2.2 分析数据
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
#将不同标签用颜色区分
colors = []
for i in range(datingTest.shape[0]):
m = datingTest.iloc[i,-1]
if m == 'didntLike':
colors.append('black')
elif m == 'smallDoses':
colors.append('orange')
elif m == 'largeDoses':
colors.append('red')
#绘制两两特征之间的散点图
plt.rcParams['font.sans-serif'] = ['Simhei'] #图中字体设置为黑体
pl = plt.figure(figsize=(12,8))
fig1 = pl.add_subplot(221) #x,y,标签1
plt.scatter(datingTest.iloc[:,1],datingTest.iloc[:,2],marker='.',c=colors)
plt.xlabel('玩游戏视频所占时间比')
plt.ylabel('每周消费冰激凌公升数')
fig2 = pl.add_subplot(222) #x,y,标签2
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,1],marker='.',c=colors)
plt.xlabel('每年飞行常客里程')
plt.ylabel('玩游戏视频所占时间比')
fig3 = pl.add_subplot(223) #x,y,标签3
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,2],marker='.',c=colors)
plt.xlabel('每年飞行常客里程')
plt.ylabel('每周消费冰激凌公升数')
plt.show()
3、数据归一化
'''
函数功能:归一化
参数说明:dataset:原始数据集
返回:0-1标准化之后的数据集
'''
def minmax(dataset):
minDf = dataset.min()
maxDf = dataset.max()
normset = (dataset - minDf) / (maxDf - minDf)
return normset
#将数据代入函数,进行归一化处理
datingT = pd.concat([minmax(datingTest.iloc[:,:3]),datingTest.iloc[:,3]],axis=1)
datingT
4、划分训练集与测试集
#90%的训练集,10%的测试集
'''
函数功能:切分训练集与测试集
参数说明:dataset:原始数据集
rate:训练集所占比例
返回:切分好的训练集和测试集
'''
def randsplit(dataset,rate=0.9):
n = dataset.shape[0] #行数
m = int(n*rate) #训练集比例
train = dataset.iloc[:m,:]
test = dataset.iloc[m:,:]
test.index = range(test.shape[0])
return train,test
train,test = randsplit(datingT)
train
test
5、测试代码
'''
函数功能:k-近邻算法分类器
参数说明:
train:训练集、
test:测试集
k:k-近邻参数,即选择距离最小的k个点
返回:预测好分类的测试集
'''
def datingclass(train,test,k):
n = train.shape[1] - 1
m = test.shape[0]
result = []
#k-近邻算法
for i in range(m):
#1、计算距离
dist = list((((train.iloc[:,:n] - test.iloc[i,:n])**2).sum(1))**0.5)
#2、取前k个距离最近值
#test加标签
d_l = pd.DataFrame({'dist':dist,'label':(train.iloc[:,n])})
#对距离排序取前k个值
dist_value = d_l.sort_values(by='dist')[:k]
#统计类别数量
label_num = dist_value.loc[:,'label'].value_counts()
result.append(label_num.index[0]) #概率最大的标签在最前面
result = pd.Series(result)
test['predict'] = result
acc = (test.iloc[:,-1] == test.iloc[:,-2]).mean()
print(f'模型预测准确率为{acc}')
print(dist_value)
return test
2.3 用python函数封装
import pandas as pd
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
class Data_knn:
#1、数据准备
dataloader = pd.read_table('datingTestSet.txt',header = None)
datingTest = dataloader
#2、数据归一化
def data_norm(self,dataset):
data = dataset.iloc[:,:3]
minDf = data.min()
maxDf = data.max()
normset = (data - minDf) / (maxDf - minDf)
return normset
#2、分析数据
def analyze_data(self,dataset):
#把不同标签用颜色区分
colors = []
for i in range(datingTest.shape[0]):
m = datingTest.iloc[i,-1]
if m == 'didntLike':
colors.append('black')
elif m == 'smallDoses':
colors.append('orange')
elif m == 'largeDoses':
colors.append('red')
plt.rcParams['font.sans-serif'] = ['simhei'] #图中字体为黑体
pl = plt.figure(figsize=(12,8))
fig1 = pl.add_subplot(221)
plt.scatter(datingTest.iloc[:,1],datingTest.iloc[:,2],marker='.',c=colors)
plt.xlabel('玩游戏视频所占时间比')
plt.ylabel('每周消费冰淇淋公升数')
fig2 = pl.add_subplot(222)
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,1],marker='.',c=colors)
plt.xlabel('每年飞行常客里程')
plt.ylabel('玩游戏视频所占时间比')
fig3 = pl.add_subplot(223)
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,2],marker='.',c=colors)
plt.xlabel('每年飞行常客里程')
plt.ylabel('每周消费冰淇淋公升数')
plt.show()
#训练
def test_knn(self,dataset,k,rate=0.9):
#划分数据集
n = dataset.shape[0]
m = int(n*rate)
train = dataset.iloc[:m,:]
test = dataset.iloc[m:,:]
#将test数据集索引从0开始
test.index = range(test.shape[0])
#k-近邻
#计算距离
a = train.shape[1] - 1
b = test.shape[0]
result = []
for i in range(b):
dist = list((((train.iloc[:,:a] - test.iloc[i,:a])**2).sum(1))**0.5)
#给距离加标签
d_l = pd.DataFrame({'dist':dist,'label':(train.iloc[:,-1])})
#提取前面k个最小的距离
d_v = d_l.sort_values(by='dist')[:k]
#统计前k个标签的概率
re = d_v.loc[:,'label'].value_counts()
result.append(re.index[0])
result = pd.Series(result)
test['predict'] = result
acc = (test.iloc[:,-1] == test.iloc[:,-2]).mean()
print(f'模型预测准确率为{acc}')
return test
d = Data_knn()
test = d.test_knn(datingTest,k=5)
test