How to find all neighbors of a given point in a delaunay triangulation using scipy.spatial.Delaunay?

前端 未结 7 1969
一个人的身影
一个人的身影 2020-12-28 19:40

I have been searching for an answer to this question but cannot find anything useful.

I am working with the python scientific computing stack (scipy,numpy,matplotlib

相关标签:
7条回答
  • 2020-12-28 19:43

    I know it's been a while since this question was posed. However, I just had the same problem and figured out how to solve it. Just use the (somewhat poorly documented) method 'vertex_neighbor_vertices' of your Delaunay triangulation object (let us call it 'tri'). It will return two arrays:

    def get_neighbor_vertex_ids_from_vertex_id(vertex_id,tri):
        #use a less awful function name
        helper = tri.vertex_neighbor_vertices
        index_pointers = helper[0]
        indices = helper[1]
        result_ids = indices[index_pointers[vertex_id]:index_pointers[vertex_id+1]]
    
        return result_ids
    

    The neighbor vertices to the point with the index vertex_id are stored somewhere in the second array that I named 'indices'. But where? This is where the first array (which I called 'index_pointers') comes in. The starting position (for the second array 'indices') is index_pointers[vertex_id], the first position past the relevant sub-array is index_pointers[vertex_id+1]. So the solution is indices[index_pointers[vertex_id]:index_pointers[vertex_id+1]]

    0 讨论(0)
  • 2020-12-28 19:43

    All the answers here are focused on getting the neighbors for one point (except astrofrog, but that is in 2D and this is 6x faster), however, it's equally expensive to get a mapping for all of the points → all neighbors.

    You can do this with

    from collections import defaultdict
    from itertools import permutations
    tri = Delaunay(...)
    _neighbors = defaultdict(set)
    for simplex in tri.vertices:
        for i, j in permutations(simplex, 2):
            _neighbors[i].add(j)
    
    points = [tuple(p) for p in tri.points]
    neighbors = {}
    for k, v in _neighbors.items():
        neighbors[points[k]] = [points[i] for i in v]
    

    This works in any dimension and this solution, finding all neighbors of all points, is faster than finding only the neighbors of one point (the excepted answer of James Porter).

    0 讨论(0)
  • 2020-12-28 19:44

    The methods described above cycle through all the simplices, which could take very long, in case there's a large number of points. A better way might be to use Delaunay.vertex_neighbor_vertices, which already contains all the information about the neighbors. Unfortunately, extracting the information

    def find_neighbors(pindex, triang):
    
        return triang.vertex_neighbor_vertices[1][triang.vertex_neighbor_vertices[0][pindex]:triang.vertex_neighbor_vertices[0][pindex+1]]
    

    The following code demonstrates how to get the indices of some vertex (number 17, in this example):

    import scipy.spatial
    import numpy
    import pylab
    
    x_list = numpy.random.random(200)
    y_list = numpy.random.random(200)
    
    tri = scipy.spatial.Delaunay(numpy.array([[x,y] for x,y in zip(x_list, y_list)]))
    
    pindex = 17
    
    neighbor_indices = find_neighbors(pindex,tri)
    
    pylab.plot(x_list, y_list, 'b.')
    pylab.plot(x_list[pindex], y_list[pindex], 'dg')
    pylab.plot([x_list[i] for i in neighbor_indices],
               [y_list[i] for i in neighbor_indices], 'ro')    
    
    pylab.show()
    
    0 讨论(0)
  • 2020-12-28 19:45

    Here is an ellaboration on @astrofrog answer. This works also in more than 2D.

    It took about 300 ms on set of 2430 points in 3D (about 16000 simplices).

    from collections import defaultdict
    
    def find_neighbors(tess):
        neighbors = defaultdict(set)
    
        for simplex in tess.simplices:
            for idx in simplex:
                other = set(simplex)
                other.remove(idx)
                neighbors[idx] = neighbors[idx].union(other)
        return neighbors
    
    0 讨论(0)
  • 2020-12-28 19:51

    Here is also a simple one line version of James Porter's own answer using list comprehension:

    find_neighbors = lambda x,triang: list(set(indx for simplex in triang.simplices if x in simplex for indx in simplex if indx !=x))
    
    0 讨论(0)
  • 2020-12-28 19:53

    I needed this too and came across the following answer. It turns out that if you need the neighbors for all initial points, it's much more efficient to produce a dictionary of neighbors in one go (the following example is for 2D):

    def find_neighbors(tess, points):
    
        neighbors = {}
        for point in range(points.shape[0]):
            neighbors[point] = []
    
        for simplex in tess.simplices:
            neighbors[simplex[0]] += [simplex[1],simplex[2]]
            neighbors[simplex[1]] += [simplex[2],simplex[0]]
            neighbors[simplex[2]] += [simplex[0],simplex[1]]
    
        return neighbors
    

    The neighbors of point v are then neighbors[v]. For 10,000 points in this takes 370ms to run on my laptop. Maybe others have ideas on optimizing this further?

    0 讨论(0)
提交回复
热议问题