【问题标题】:Nearest Neighbor Search in Python without k-d tree没有k-d树的Python中的最近邻搜索
【发布时间】:2011-12-15 23:16:41
【问题描述】:

我开始学习来自 C++ 背景的 Python。我正在寻找的是一种快速简便的方法,可以在多维点的二维(numpy)数组(也是 numpy 数组)中找到一些多维查询点的最近(最近邻)。我知道 scipy 有一个 k-d 树,但我认为这不是我想要的。首先,我将更改二维数组中多维点的值。其次,二维数组中每个点的位置(坐标)很重要,因为我还将更改它们的邻居。

我可以编写一个函数,通过二维数组测量查询点和数组中的点之间的距离,同时跟踪最小的点(使用 scipy 空间距离函数来测量距离)。是否有内置功能可以做到这一点?我试图尽可能避免在 python 中迭代数组。我还将有许多查询点,因此至少会有两个“for 循环”——一个循环遍历查询点,对于每个查询,循环遍历 2D 数组并找到最小距离。

感谢您的建议。

【问题讨论】:

    标签: python numpy scipy nearest-neighbor


    【解决方案1】:

    为了更快地搜索和支持动态项目插入,您可以对二维项目使用二叉树,其中大于和小于运算符由到参考点 (0,0) 的距离定义。

    def dist(x1,x2):
        return np.sqrt( (float(x1[0])-float(x2[0]))**2 +(float(x1[1])-float(x2[1]))**2 )
    
    class Node(object):
    
        def __init__(self, item=None,):
            self.item = item
            self.left = None
            self.right = None
    
        def __repr__(self):
            return '{}'.format(self.item)
    
        def _add(self, value, center):
            new_node = Node(value)
            if not self.item:
                self.item = new_node        
            else:
            vdist = dist(value,center)
            idist = dist(self.item,center)
                if vdist > idist:
                    self.right = self.right and self.right._add(value, center) or new_node
                elif vdist < idist:
                    self.left = self.left and self.left._add(value, center) or new_node
                else:
                    print("BSTs do not support repeated items.")
    
            return self # this is necessary!!!
    
        def _isLeaf(self):
            return not self.right and not self.left
    
    class BSTC(object):
    
        def __init__(self, center=[0.0,0.0]):
            self.root = None
        self.count = 0
        self.center = center
    
        def add(self, value):
            if not self.root:
                self.root = Node(value)
            else:
                self.root._add(value,self.center)
        self.count += 1
    
        def __len__(self): return self.count
    
        def closest(self, target):
                gap = float("inf")
                closest = float("inf")
                curr = self.root
                while curr:
                    if dist(curr.item,target) < gap:
                        gap = dist(curr.item, target)
                        closest = curr
                    if target == curr.item:
                        break
                    elif dist(target,self.center) < dist(curr.item,self.center):
                        curr = curr.left
                    else:
                        curr = curr.right
                return closest.item, gap
    
    
    import util
    
    bst = util.BSTC()
    print len(bst)
    
    arr = [(23.2323,34.34535),(23.23,36.34535),(53.23,34.34535),(66.6666,11.11111)]
    for i in range(len(arr)): bst.add(arr[i])
    
    f = (11.111,22.2222)
    print bst.closest(f)
    print map(lambda x: util.dist(f,x), arr)
    

    【讨论】:

    • 嗨@BBSysDyn。感谢您发布解决方案。是否可以修改代码,因为某些行的缩进似乎已关闭。我试图修复它,但后来我得到了错误的结果。你能检查一下你的代码的缩进吗?谢谢你。
    【解决方案2】:

    如果简洁是您的目标,您可以这样做:

    In [14]: X = scipy.randn(10,2)
    
    In [15]: X
    Out[15]: 
    array([[ 0.85831163,  1.45039761],
           [ 0.91590236, -0.64937523],
           [-1.19610431, -1.07731673],
           [-0.48454195,  1.64276509],
           [ 0.90944798, -0.42998205],
           [-1.17765553,  0.20858178],
           [-0.29433563, -0.8737285 ],
           [ 0.5115424 , -0.50863231],
           [-0.73882547, -0.52016481],
           [-0.14366935, -0.96248649]])
    
    In [16]: q = scipy.array([0.91, -0.43])
    
    In [17]: scipy.argmin([scipy.inner(q-x,q-x) for x in X])
    Out[17]: 4
    

    如果您有多个查询点:

    In [18]: Q = scipy.array([[0.91, -0.43], [-0.14, -0.96]])
    
    In [19]: [scipy.argmin([scipy.inner(q-x,q-x) for x in X]) for q in Q]
    Out[19]: [4, 9]
    

    【讨论】:

      【解决方案3】:

      广播对于这种事情非常有用。我不确定这是否是您需要的,但在这里我使用广播来查找 p(3 空间中的一个点)和 X(3 空间中的一组 10 个点)之间的位移。

      import numpy as np
      
      def closest(X, p):
          disp = X - p
          return np.argmin((disp*disp).sum(1))
      
      X = np.random.random((10, 3))
      p = np.random.random(3)
      
      print X
      #array([[ 0.68395953,  0.97882991,  0.68826511],
      #       [ 0.57938059,  0.24713904,  0.32822283],
      #       [ 0.06070267,  0.06561339,  0.62241713],
      #       [ 0.93734468,  0.73026772,  0.33755815],
      #       [ 0.29370809,  0.76298588,  0.68728743],
      #       [ 0.66248449,  0.6023311 ,  0.76704199],
      #       [ 0.53490144,  0.96555923,  0.43994738],
      #       [ 0.23780428,  0.75525843,  0.46067472],
      #       [ 0.84240565,  0.82573202,  0.56029917],
      #       [ 0.66751884,  0.31561133,  0.19244683]])
      print p
      #array([ 0.587416 ,  0.4181857,  0.2539029])
      print closest(X, p)
      #9
      

      【讨论】:

        【解决方案4】:

        您可以计算所有距离scipy.spatial.distance.cdist( X, Y ) 或使用 RTree 获取动态数据:http://gispython.org/rtree/docs/class.html

        【讨论】:

        • 我喜欢第一个建议,但我一次执行一个查询并更新数组中的值(类似于 SOM)。我可以使用 cdist(X,Y) 其中 X 只是一个查询并更新数组并继续下一个查询。 Rtree 似乎可以,但我有点不确定如何在我的情况下使用它。我想知道是否有任何图形包允许使用外点进行最近邻搜索?我可以使用图形包制作一个格子,其中每个节点都是一个多维点。图形包的其他一些功能将在我的程序中派上用场
        猜你喜欢
        • 2012-11-10
        • 2011-10-23
        • 2019-12-20
        • 1970-01-01
        • 2013-08-16
        • 2013-03-16
        • 2012-06-11
        • 2010-12-10
        • 2013-03-21
        相关资源
        最近更新 更多