1

I am working on a python project where from a function I am getting coordinate values x, y in a dict like below:

centroid_dict = {0: (333, 125), 1: (288, 52), 2: (351, 41)}

where 0, 1, 2 are the objectId and (333, 125), (288, 52), (351, 41) are their (x, y) coordinate values respectively. I need to calculate the distance between each coordinate which means:

0 - 1 -> ((333, 125) - (288, 52))

0 - 2 -> ((333, 125) - (351, 41))

1 - 0 -> ((288, 52) - (333, 125))

1 - 2 -> ((288, 52) - (351, 41))

2 - 0 -> ((351, 41) - (333, 125))

2 - 1 -> ((351, 41) - (288, 52))

To calculate the distance, I can use:

def calculateDistance(x1, y1, x2, y2):
    dist = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
    return dist

but I am not able to think of any logic which can calculate distance between each points as the length of dict might increase in future. As of now, it is 3 but it can be 10. Can anyone please help me give some ideas on it. Thanks

S Andrew
  • 5,592
  • 27
  • 115
  • 237
  • 2
    You might want to look into [`itertools.combinations`](https://docs.python.org/3/library/itertools.html#itertools.combinations) to generate a combination of all possible coordinates in your mapping; you may wish to [refer to this thread](https://stackoverflow.com/questions/464864/how-to-get-all-possible-combinations-of-a-list-s-elements). – metatoaster May 03 '20 at 03:40
  • [kd tree](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html) this will help you , it is a graph prblem – sahasrara62 May 03 '20 at 03:51

3 Answers3

2

You can use combinations from itertools to form a new dictionary with every pair of objects as key:

from itertools import combinations:
distances = dict()
for (id1,p1),(id2,p2) in combinations(centroid_dict.items(),2):
    dx,dy = p1[0]-p2[0], p1[1]-p2[1]
    distances[id1,id2] = distances[id2,id1] = math.sqrt(dx*dx+dy*dy)

The drawback of this approach is that it will systematically calculate all distances and your program may not need to access all of those values. A more efficient approach could be to use a dictionary as a cache of distance and obtain them "on demand" using a function:

distanceCache = dict()
def getDist(id1,id2):
    if id1 > id2: return getDist(id2,id1) # only store each pair once
    if (id1,id1) in distanceCache: return distanceCache[id1,id2]
    x1,y1 = centroid_dict[id1]
    x2,y2 = centroid_dict[id2]
    dx,dy = x1-x2,y1-y2 
    return distanceCache.setDefault((id1,id2),math.sqrt(dx*dx+dy*dy))

This will allow you to simply clear the cache when object locations are changed without incurring an immediate delay of O(n^2) time

Note that you could also use the points (positions) themselves as key to the cache and also use an LRU cache (from functools)

from functools import lru_cache
import math

@lru_cache(1024)
def getDist(p1,p2):
    dx,dy = p1[0]-p2[0],p1[1]-p2[1]
    return math.sqrt(dx*dx+dy*dy)

def getObjectDist(id1,id2):
    return getDist(centroid_dict[id1],centroid_dict[id2])
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • In your first example, I am not able to understand this line `distances[id1,id2] = distances[id2,id1] = math.sqrt(dx*dx+dy*dy)` can you explain what is happening here. – S Andrew May 03 '20 at 04:43
  • Assigning the distance dictionary for the pair of object id (tuples) in both orders. (i.e. (id1,id2) and (id2 ,id1)) because it is the same distance no matter which one is the origin and which one is the destination. This will allow calculating the distance only once per pair while providing access to the distance without having to use a specific order of object Ids. – Alain T. May 03 '20 at 05:29
1

using solution from here, it is k d tree graph problem

Find the Euclidean distances between four 2-D coordinates:

from scipy.spatial import distance
coords = [(35.0456, -85.2672),
          (35.1174, -89.9711),
          (35.9728, -83.9422),
          (36.1667, -86.7833)]
distance.cdist(coords, coords, 'euclidean')

array([[ 0.    ,  4.7044,  1.6172,  1.8856],
   [ 4.7044,  0.    ,  6.0893,  3.3561],
   [ 1.6172,  6.0893,  0.    ,  2.8477],
   [ 1.8856,  3.3561,  2.8477,  0.    ]])
sahasrara62
  • 10,069
  • 3
  • 29
  • 44
1

You can do something like this. No import required.

def dist(key1,key2):
    return calculateDistance(*centroid_dict[key1],*centroid_dict[key2])

all_dist = []
for key1 in centroid_dict:
    all_dist.extend([dist(key1,key2) for key2 in centroid_dict if not key1==key2])
iamvegan
  • 482
  • 4
  • 15