KNN以及K-D树的基本实现

kd树的搜索方式

至下而上的搜索

kd树的基本逻辑是,建立k-d树:

def findtree(树,当前维度 = 1,点)if 当前树只有一个节点:
		return 该节点,搜索路径
	if[当前维度]<树的当前节点[当前维度]:
		findtree(树,当前维度>>1,点)
		更新搜索路径
	else:
		findtree(树,当前维度>>1,点)
		更新搜索路径


def down_tree(树,输入点,最优点=0,原递归节点表=0,判断条件=0):
#其实可以通过树是否存在,来判断是否计算
	if== None or 判断条件 == 0:
		当前最近点,原递归节点表 = findtree(树,树的根节点的维度,输入点)
		递归节点表1 =  递归节点表.copy()
	#判断条件用于判断是否执行搜索最近点
	递归节点 = 原递归节点表.最后一个点
	#递归节点表备份,用于存储操作
	
	#最近点仅第一次更新即可
	最近点距离 = 欧氏距离(输入点-当前最近点)
	#每次迭代都要更新,递归节点距离
	递归节点的距离 = 欧氏距离(输入点-原递归节点表.最后一个点)
	#递归节点算法
	///#判断链表是否到头,到头则返回最近点和递归链表
	if 原递归节点表 is None
		return 当前最近点,递归节点表1
		
		
	if 递归节点的距离 < 最近点的距离:
		当前最近点  = 递归节点
		最近点的距离 = 递归节点的距离
		#实际上,如果递归点是当前最小点,只有当切分维度距离超平面距离刚刚相等时
		#才不用在递归节点的另一个里寻找,这种情况在统计学上概率为0.所以我们还是要判断。
	if 最近点的距离 > 超平面的距离:
		#说明了与另一个区域相交,则在该节点,此时节点为递归节点(父节点),在另一个子树里面寻找最优点。
		子树 = 当前最近点.另一个子树
		# 考虑没有子树的情况,则当前点为最优点
		if 子树 = None
			#则说明当前点为最优点,递归向上
			原递归节点表 -> 1
			#使用输入条件,来使递归跳过第一步
			down_tree(None,输入点,当前最近点,原递归节点表,判断条件=1):
			#跳转到(2)
		else
			最优点,递归节点表 = down_tree(子树,输入点,当前最近点,原递归节点表,判断条件=0)
			最优距离 = 欧式距离(最优点-输入点)
			if 最优距离 < 最近点的距离
			#说明该区域内没有最优点,最优点依然为当前点,不操作,递归
			原递归节点表 -> 1
			down_tree(None,输入点,最优点,原递归节点表,判断条件=1):
			#跳转到(2)
			else
			#说明有最优点,更新最优点,更新递归结点链表
			当前最近点 = 最优点
			最近点的距离 = 最优距离
			原递归节点表 = 原递归节点表 + 递归节点表
			递归节点表备份更新 = 递归节点表1 + 递归节点表
			#更新了链表和最近点,进行从新递归
			down_tree(None,输入点,当前最近点,递归节点表备份更新,判断条件=1):
	else 最近点的距离 < 超平面距离
	#说明了没有与另一个区域相交,这种概率理论上为0
	原递归节点表 -> 1
	down_tree(None,输入点,最优点,原递归节点表,判断条件=1):
	#跳转到(2)
#递归节点的距离 < 最近点的距离,不用更新节点,依然判断超平面,方法与上面一样,
#说明上面的情况独立于是否跟新点。
#观察跳转到(2)的情况,一般返回,最优点,最优距离通过最优点计算,以及链表。添加判断是否执行第一步即可

///#如果做Knn则新建一个长度为k的链表,如果有点比之前点小,则插入其中,最后返回K个点	
???输入点可以优化为 constant ,或者放到最后定义为常量,不清楚该程序的震荡性质如何。可能有部分地方可以优化	

至上而下的搜索方式

该搜索方式如图所示
在这里插入图片描述
从上到下的搜索方式算法相同:

1)初始化根节点当成当前最近点(有输入则不初始化)
		计算距离,判断输入点在节点的哪一侧,
		左侧:
				if 左子树为空:
					if 右子树不为空,且与超平面有交集:
						搜索(右子树节点为最近点)
						跳转->(1)
					else:
						returnelse
					搜索(左子树节点为最近点)
					跳转->(1)
		右侧:
				if 右子树为空:
					if 左子树不为空,且与超平面有交集
						搜索(左子树节点为最近点)
						跳转->(1)
					else:
						returnelse:
					搜索(右子树节点为最近点)
					跳转->(1)

从上往下往往会执行跟多的步骤,后向前回溯更好。

KNN一般算法

采用python书写的代码,用pandas数据操作,matplot绘图。
实在没啥时间的我就不写成中文了。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#打开文件
def field_open(list='D:\suanfa\Knn\datingTestSet.txt'):
    folines = pd.read_csv(list, header=None, sep='\t')
    print(folines.shape)
    return folines

#绘制图像
def plot_fig(a):
    Colors = []
    for m in a.iloc[:, -1]:
        if m == 'didntLike':
            Colors.append('black')
        if m == 'smallDoses':
            Colors.append('orange')
        if m == 'largeDoses':
            Colors.append('red')
# 绘制图片,
    fig = plt.figure()
    ax = fig.add_subplot(221)
    ax.scatter(a.iloc[:, 0], a.iloc[:, 1], c=Colors)

    ax1 = fig.add_subplot(222)
    ax1.scatter(a.iloc[:, 0], a.iloc[:, 2], c=Colors)

    ax2 = fig.add_subplot(223)
    ax2.scatter(a.iloc[:, 1], a.iloc[:, 2], c=Colors)
    plt.show()

#数据的归一化
def normal(a):
    amax = a.iloc[:, 0:3].max()
    amin = a.iloc[:, 0:3].min()
    anor = amax - amin
    agui = (a.iloc[:, 0:3] - amin)/anor
    agui1 = pd.concat([agui.iloc[:, :3], a.iloc[:, 3]], axis=1)
    return agui1

#欧氏空间距离计算
def cal_dis(Xtrain, Xtest,k):
    dista  = list((((Xtrain.iloc[:, :-1] - Xtest)**2).sum())**0.5)
    distal = pd.DataFrame({'dist': dista, 'labels': (Xtrain.iloc[:, 3])})
    dr = distal.sort_values(by='dist')[: k]
    re = dr.value_counts('labels')
    return re.index[0]

#随机生成函数
def sui_ji(Xdata,rata=0.9):
    a = int(Xdata.shape[0]*rata)
    Xtrain = Xdata.iloc[:a, :]
    Xtest  = Xdata.iloc[a:, :]
    #重置序号
    Xtest.index = range(Xtest.shape[0])
    return Xtrain, Xtest

#KNN计算
def data_chat(k):
    oridata = field_open()
    nordata = normal(oridata)
    train, test = sui_ji(nordata)
    print(train)
    n = train.shape[1] - 1
    m = test.shape[0]
    result = []
    #计算所有值并取前K个
    for i in range(m):
        dist = list((((train.iloc[:, :n] - test.iloc[i, :n]) ** 2).sum(1)) ** 5)
        dist_l = pd.DataFrame({'dist': dist, 'labels': (train.iloc[:, n])})
        dr = dist_l.sort_values(by='dist')[: k]
        re = dr.loc[:, 'labels'].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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值