Open In App

Python – Group Adjacent Coordinates

Sometimes, while working with Python lists, we can have a problem in which we need to perform the grouping of all the coordinates which occur adjacent on a matrix, i.e horizontally and vertically at distance 1. This is called Manhattan distance. This kind of problem can occur in competitive programming domain. Let’s discuss certain way in which this task can be performed.
 

Input : test_list = [(4, 4), (6, 4), (7, 8)] 
Output : [[(7, 8)], [(6, 4)], [(4, 4)]]



Input : test_list = [(4, 4), (5, 4)] 
Output : [[(5, 4), (4, 4)]] 

Method 1: Using product() + groupby() + list comprehension 
The combination of above methods can be used to solve this problem. In this, we perform the task of grouping of elements using groupby() and check for pairs using product(). The logic driving this solution is similar to union find algorithm.






# Python3 code to demonstrate working of
# Group Adjacent Coordinates
# Using product() + groupby() + list comprehension
from itertools import groupby, product
 
def Manhattan(tup1, tup2):
    return abs(tup1[0] - tup2[0]) + abs(tup1[1] - tup2[1])
 
# initializing list
test_list = [(4, 4), (6, 4), (7, 8), (11, 11),
                     (7, 7), (11, 12), (5, 4)]
 
# printing original list
print("The original list is : " + str(test_list))
 
# Group Adjacent Coordinates
# Using product() + groupby() + list comprehension
man_tups = [sorted(sub) for sub in product(test_list, repeat = 2)
                                         if Manhattan(*sub) == 1]
 
res_dict = {ele: {ele} for ele in test_list}
for tup1, tup2 in man_tups:
    res_dict[tup1] |= res_dict[tup2]
    res_dict[tup2] = res_dict[tup1]
 
res = [[*next(val)] for key, val in groupby(
        sorted(res_dict.values(), key = id), id)]
 
# printing result
print("The grouped elements : " + str(res))

Output
The original list is : [(4, 4), (6, 4), (7, 8), (11, 11), (7, 7), (11, 12), (5, 4)]
The grouped elements : [[(6, 4), (5, 4), (4, 4)], [(7, 8), (7, 7)], [(11, 12), (11, 11)]]

Time complexity: O(n log n)., where n is the length of the input list test_list.
Auxiliary space: O(n^2), as the man_tups list and the res_dict dictionary both have n^2 elements.

Method 2: Using numpy() : 

1.Define a function manhattan that takes two tuples and returns the Manhattan distance between them.
2.Create a NumPy array test_arr from the given list of tuples test_list.
3.Create two arrays x and y by unpacking the coordinates from test_arr.
4.Use np.meshgrid to create two 2D arrays xx and yy of all possible pairwise combinations of x and y.
5.Use np.dstack to stack xx and yy into a 3D array grid.
6.Use np.apply_along_axis to apply manhattan to the last two dimensions of grid to get a 2D array man_tups of tuples whose Manhattan distance is 1.
7.Use a dictionary comprehension to create a dictionary res_dict where each key is a coordinate from test_arr and each value is a set containing that coordinate.
8.Loop through the pairs of tuples in man_tups, update the corresponding sets in res_dict, and merge the sets.
9.Use a dictionary comprehension to create a dictionary res_dict2 where each key is a frozenset of the sets in res_dict.values() and each value is the first set in that frozenset.
10.Create a list res by looping through the values of res_dict2 and converting each set to a list.




import numpy as np
from itertools import groupby, product
 
def Manhattan(tup1, tup2):
    return abs(tup1[0] - tup2[0]) + abs(tup1[1] - tup2[1])
 
# initializing list
test_list = [(4, 4), (6, 4), (7, 8), (11, 11),
             (7, 7), (11, 12), (5, 4)]
 
# printing original list
print("The original list is : " + str(test_list))
 
# Group Adjacent Coordinates
# Using product() + groupby() + list comprehension
man_tups = [sorted(sub) for sub in product(test_list, repeat = 2)
                                    if Manhattan(*sub) == 1]
 
res_dict = {ele: {ele} for ele in test_list}
for tup1, tup2 in man_tups:
    res_dict[tup1] |= res_dict[tup2]
    res_dict[tup2] = res_dict[tup1]
 
res = [[*next(val)] for key, val in groupby(
        sorted(res_dict.values(), key = id), id)]
 
# converting tuples to numpy arrays
res = [np.array(sub) for sub in res]
 
# sorting numpy arrays lexicographically
res = sorted(res, key=lambda x: tuple(x[:,0]))
 
# converting numpy arrays back to tuples
res = [tuple(map(tuple, sub)) for sub in res]
 
# printing result
print("The grouped elements : " + str(res))
#This code is contributed by Jyothi pinjala.

Output:

The original list is : [(4, 4), (6, 4), (7, 8), (11, 11), (7, 7), (11, 12), (5, 4)]
The grouped elements : [((4, 4), (5, 4), (6, 4)), ((7, 7), (7, 8)), ((11, 12), (11, 11))]

The time complexity : O(n^2), where n is the number of coordinates in the input list. This is because it involves a nested loop over all pairs of coordinates to compute their Manhattan distance, which takes O(n^2) time. Additionally, there is a loop over the resulting list of adjacent coordinate pairs to construct the groups, which takes O(n) time in the worst case.

The auxiliary space :O(n^2), due to the creation of the manhattan distance matrix. This matrix has n^2 elements, each of which is a 32-bit integer (assuming the default data type for numpy arrays), so it requires O(n^2 * 4 bytes) = O(n^2) space. Additionally, the output list of groups may require up to O(n) space in the worst case.


Article Tags :