k近鄰法的kd Tree搜索

最近在讀李航老師的《統(tǒng)計學(xué)習(xí)方法》拗小,讀到第三章的k近鄰算法時重罪,在N>>k時遍歷搜索比較費時,為了更高效的搜索可以采用kd Tree的方式組織Training數(shù)據(jù)哀九,我看到一篇博客剿配,前面的圖示理解部分說的比較到位,不過博主的代碼有些問題阅束,包括:

  • 代碼的完整邏輯是不對的
  • 并沒有對已搜索的節(jié)點進行標(biāo)注呼胚,導(dǎo)致重復(fù)計算(走回頭路),這樣整個kd Tree的初衷就完全沒意義了

于是在該代碼基礎(chǔ)上改了一版正確的息裸,帶了一些調(diào)試信息蝇更。

  • 更正代碼邏輯
  • 代碼clean up
  • 更新為python 3兼容
  • 增加up_traced標(biāo)志避免走回頭路沪编,支持多次搜索,每次搜索前做好該標(biāo)志的清理
# -*- coding: utf-8 -*-

import numpy as np


class Node:
    def __init__(self, data, parent, dim):
        self.data = data
        self.parent = parent
        self.lChild = None
        self.rChild = None
        self.dim = dim
        # only track search_Up process
        self.up_traced = False

    def setLChild(self, lChild):
        self.lChild = lChild

    def setRChild(self, rChild):
        self.rChild = rChild


class KdTree:
    def __init__(self, train):
        self.root = self.__build(train, 1, None)

    def __build(self, train, depth, parent):  # 遞歸建樹
        (m, k) = train.shape

        if m == 0:
            return None

        train = train[train[:, depth % k].argsort()]

        root = Node(train[m//2], parent, depth % k)
        root.setLChild(self.__build(train[:m//2, :], depth+1, root))
        root.setRChild(self.__build(train[m//2+1:, :], depth+1, root))
        return root

    def findNearestPointAndDistance(self, point):  # 查找與point距離最近的點
        point = np.array(point)
        node = self.__findSmallestSubSpace(point, self.root)
        print("Start node:", node.data)
        return self.__searchUp(point, node, node, np.linalg.norm(point - node.data))

    def __searchUp(self, point, node, nearestPoint, nearestDistance):
        if node.parent is None:
            return [nearestPoint, nearestDistance]

        print("UP:", node.parent.data)
        node.parent.up_traced = True
        distance = np.linalg.norm(node.parent.data - point)
        if distance < nearestDistance:
            nearestDistance = distance
            nearestPoint = node.parent

        distance = np.abs(node.parent.data[node.dim] - point[node.parent.dim])
        if distance < nearestDistance:
            [p, d] = self.__searchDown(point, node.parent)
            if d < nearestDistance:
                nearestDistance = d
                nearestPoint = p

        [p, d] = self.__searchUp(point, node.parent, nearestPoint, nearestDistance)
        if d < nearestDistance:
            nearestDistance = d
            nearestPoint = p

        return [nearestPoint, nearestDistance]

    def __searchDown(self, point, node):

        nearestDistance = np.linalg.norm(node.data - point)
        nearestPoint = node

        print("DOWN:", node.data)
        if node.lChild is not None and node.lChild.up_traced is False:
            [p, d] = self.__searchDown(point, node.lChild)
            if d < nearestDistance:
                nearestDistance = d
                nearestPoint = p

        if node.rChild is not None and node.rChild.up_traced is False:
            [p, d] = self.__searchDown(point, node.rChild)
            if d < nearestDistance:
                nearestDistance = d
                nearestPoint = p

        print("---- ", nearestPoint.data, nearestDistance)
        return [nearestPoint, nearestDistance]

    def __findSmallestSubSpace(self, point, node):  # 找到這個點所在的最小的子空間
        """
        從根節(jié)點出發(fā)年扩,遞歸地向下訪問kd樹蚁廓。如果point當(dāng)前維的坐標(biāo)小于切分點的坐標(biāo),則
        移動到左子節(jié)點厨幻,否則移動到右子節(jié)點相嵌。直到子節(jié)點為葉節(jié)點為止。
        """
        # New search: clean up up_traced flag for all up path nodes
        node.up_traced = False
        if point[node.dim] < node.data[node.dim]:
            if node.lChild is None:
                return node
            else:
                return self.__findSmallestSubSpace(point, node.lChild)
        else:
            if node.rChild is None:
                return node
            else:
                return self.__findSmallestSubSpace(point, node.rChild)


train = np.array([[2, 3], [5, 4], [9, 6], [4, 7], [8, 1], [7, 2]])
train = np.array([[2, 5], [3, 2], [3, 7], [8, 3], [6, 6], [1, 1], [1, 8]])
kdTree = KdTree(train)


target = np.array([6, 4])
print('##### target :', target)
[p, d] = kdTree.findNearestPointAndDistance(target)

print(p.data, d)
print('---------------------')

(m, k) = train.shape
for i in range(m):
    print(train[i], np.linalg.norm(train[i]-target))


target = np.array([2, 2])
print('')
print('##### target :', target)
[p, d] = kdTree.findNearestPointAndDistance(target)

print(p.data, d)
print('---------------------')

for i in range(m):
    print(train[i], np.linalg.norm(train[i]-target))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末况脆,一起剝皮案震驚了整個濱河市饭宾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌格了,老刑警劉巖看铆,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盛末,居然都是意外死亡弹惦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門满败,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肤频,“玉大人,你說我怎么就攤上這事算墨∠模” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵净嘀,是天一觀的道長报咳。 經(jīng)常有香客問我,道長挖藏,這世上最難降的妖魔是什么暑刃? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮膜眠,結(jié)果婚禮上惫恼,老公的妹妹穿的比我還像新娘鸟蜡。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布癞松。 她就那樣靜靜地躺著函卒,像睡著了一般适瓦。 火紅的嫁衣襯著肌膚如雪驯遇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天捎琐,我揣著相機與錄音会涎,去河邊找鬼裹匙。 笑死,一個胖子當(dāng)著我的面吹牛末秃,可吹牛的內(nèi)容都是我干的概页。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蛔溃,長吁一口氣:“原來是場噩夢啊……” “哼绰沥!你這毒婦竟也來了篱蝇?” 一聲冷哼從身側(cè)響起贺待,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎零截,沒想到半個月后麸塞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡涧衙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年哪工,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弧哎。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡雁比,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撤嫩,到底是詐尸還是另有隱情偎捎,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布序攘,位于F島的核電站茴她,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏程奠。R本人自食惡果不足惜丈牢,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞄沙。 院中可真熱鬧己沛,春花似錦、人聲如沸距境。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肮疗。三九已至晶姊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伪货,已是汗流浹背们衙。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工钾怔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒙挑。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓宗侦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忆蚀。 傳聞我的和親對象是個殘疾皇子矾利,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容