Compare certain indices of many lists

Viewed 80

I have a list containing of lists containing of x- and y-coordinates:
[[x1, y1], [x2, y2], … [xn, yn]].

I want the smallest and the largest x- and y-coordinates.

Since the x-coordinates are always at index 0, I want to compare all values at index 0 and find the min and the max. Same for index 1 (y-coordinates).

My solution right now:

listX = []
listY = []
for i in list:
    listX.append(i[0])
    listY.append(i[1])

xmin = min(listX)
xmax = max(listX)
ymin = min(listY)
ymax = max(listY)

Is there a better way? For example "Comparing all values at index 0 and find the min" in one line? In total this would be four lines instead of eight.

Context

I need this because I have a white image with not known shapes in it. For each shape I want to know the smallest rectangle there is to cover each shape completely. (4 shapes -> 4 rectangles not 1 rectangle for all). Different shapes can have the same color, but if that‘s the case they are not touching. Shapes of different colors could touch each other. The rectangles are allowed to overlap. This list in my question contains of all the pixels that are part of a certain shape.

4 Answers

You can use the key parameter on min and max functions.

xmin = min(lst, key = lambda v: v[0])[0]  # use [0] since min returns [x, y] pair
ymin = min(lst, key = lambda v:v[1])[1]   # use [1] since max returns [x, y] pair 
xmax = max(lst, key = lambda v: v[0])[0]
ymax = max(lst, key = lambda v:v[1])[1]

Another way could be decouple the list of pairs into list of xs and ys, order them and choose the 1st and last term.

coords = [(1, 2), (11, 15), (-4, 9)]

# decoupling
xs, ys = tuple(zip(*coords))

# get first and last term of a list with term in increasing order
x_min, x_max = sorted(xs)[::len(coords)-1]
y_min, y_max = sorted(ys)[::len(coords)-1]

# out
print(x_min, x_max)
print(y_min, y_max)

or simply using sort with the callback key

x_min, x_max = [x for x, _ in sorted(coords, key=lambda p: p[0])[::len(coords)-1]]
y_min, y_max = [y for _, y in sorted(coords, key=lambda p: p[1])[::len(coords)-1]]

You could transform the list to a numpy array

import numpy as np
lst = [[5, 2], [8, 2], [3, 9], [1, 6]]
a = np.array(lst)

and either use np.amin() and np.amax():

xmin, ymin = np.amin(a, axis=0)
xmax, ymax = np.amax(a, axis=0)

or slice the array and use min() and max():

xmin = min(a[:,0])
xmax = max(a[:,0])
ymin = min(a[:,1])
ymax = max(a[:,1])

This is slightly verbose, but you could use numpy to transpose the list so that all x values and y values are grouped in rows and then just get the min and max from each:

lst = [[1,2],[9,9],[2,2],[3,9]]

import numpy as np
def min_max(arr):
    lst = np.array(arr).transpose()
    min_x, max_x = np.min(lst[0]), np.max(lst[0])
    min_y, max_y= np.min(lst[1]), np.max(lst[1])
    return min_x, max_x, min_y, max_y

print(min_max(lst))

Outputs:

(1, 9, 2, 9)

Another way is to iterate over the list and keep track of the global min and max values:
lst = [[1,2],[9,9],[2,2],[3,9]]

def min_max2(arr):
    min_x = arr[0][0]
    min_y = arr[0][1]
    max_x = arr[0][0]
    max_y = arr[0][1]
    for x, y in arr:
        if x < min_x:
            min_x = x
        if y < min_y:
            min_y = y
        if x > max_x:
            max_x = x
        if y > max_y:
            max_y = y
    return min_x, max_x, min_y, max_y

print(min_max2(lst))

Outputs:

(1, 9, 2, 9)
Related