pythonknnnearest-neighboreuclidean-distance

What is the Most Efficient Way to Compute the (euclidean) Distance of the Nearest Neighbor in a List of (x,y,z) points?


What is the most efficient way compute (euclidean) distance of the nearest neighbor for each point in an array?

I have a list of 100k (X,Y,Z) points and I would like to compute a list of nearest neighbor distances. The index of the distance would correspond to the index of the point.

I've looked into PYOD and sklearn neighbors, but those seem to require "teaching". I think my problem is simpler than that. For each point: find nearest neighbor, compute distance.

Example data:

points = [
     (0             0   1322.1695
      0.006711111   0   1322.1696
      0.026844444   0   1322.1697
      0.0604        0   1322.1649
      0.107377778   0   1322.1651
      0.167777778   0   1322.1634
      0.2416        0   1322.1629
      0.328844444   0   1322.1631
      0.429511111   0   1322.1627...)]

compute k = 1 nearest neighbor distances

result format:

results = [nearest neighbor distance]

example results:

results = [
0.005939372
0.005939372
0.017815632
0.030118587
0.041569616
0.053475883
0.065324964
0.077200014
0.089077602)
]

UPDATE:

I've implemented two of the approaches suggested.

  1. Use the scipy.spatial.cdist to compute the full distances matrices
  2. Use a nearest X neighbors in radius R to find subset of neighbor distances for every point and return the smallest.

Results are that Method 2 is faster than Method 1 but took a lot more effort to implement (makes sense).

It seems the limiting factor for Method 1 is the memory needed to run the full computation, especially when my data set is approaching 10^5 (x, y, z) points. For my data set of 23k points, it takes ~ 100 seconds to capture the minimum distances.

For method 2, the speed scales as n_radius^2. That is, "neighbor radius squared", which really means that the algorithm scales ~ linearly with number of included neighbors. Using a Radius of ~ 5 (more than enough given application) it took 5 seconds, for the set of 23k points, to provide a list of mins in the same order as the point_list themselves. The difference matrix between the "exact solution" and Method 2 is basically zero.

Thanks for everyones' help!


Solution

  • Similar to Caleb's answer, but you could stop the iterative loop if you get a distance greater than some previous minimum distance (sorry - no code).

    I used to program video games. It would take too much CPU to calculate the actual distance between two points. What we did was divide the "screen" into larger Cartesian squares and avoid the actual distance calculation if the Delta-X or Delta-Y was "too far away" - That's just subtraction, so maybe something like that to qualify where the actual Eucledian distance metric calculation is needed (extend to n-dimensions as needed)?

    EDIT - expanding "too far away" candidate pair selection comments. For brevity, I'll assume a 2-D landscape. Take the point of interest (X0,Y0) and "draw" an nxn square around that point, with (X0,Y0) at the origin.

    Go through the initial list of points and form a list of candidate points that are within that square. While doing that, if the DeltaX [ABS(Xi-X0)] is outside of the square, there is no need to calculate the DeltaY.

    If there are no candidate points, make the square larger and iterate.

    If there is exactly one candidate point and it is within the radius of the circle incribed by the square, that is your minimum.

    If there are "too many" candidates, make the square smaller, but you only need to reexamine the candidate list from this iteration, not all the points.

    If there are not "too many" candidates, then calculate the distance for that list. When doing so, first calculate DeltaX^2 + DeltaY^2 for the first candidate. If for subsequent candidates the DetlaX^2 is greater than the minumin so far, no need to calculate the DeltaY^2.

    The minimum from that calculation is the minimum if it is within the radius of the circle inscribed by the square.

    If not, you need to go back to a previous candidate list that includes points within the circle that has the radius of that minimum. For example, if you ended with one candidate in a 2x2 square that happened to be on the vertex X=1, Y=1, distance/radius would be SQRT(2). So go back to a previous candidate list that has a square greated or equal to 2xSQRT(2).

    If warranted, generate a new candidate list that only includes points withing the +/- SQRT(2) square. Calculate distance for those candidate points as described above - omitting any that exceed the minimum calcluated so far.

    No need to do the square root of the sum of the Delta^2 until you have only one candidate.

    How to size the initial square, or if it should be a rectangle, and how to increase or decrease the size of the square/rectangle could be influenced by application knowledge of the data distribution.

    I would consider recursive algorithms for some of this if the language you are using supports that.