Open In App

Python – Group Adjacent Coordinates

Last Updated : 03 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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




# 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.

Python3




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.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads