Find min and max value of multiple element in Python

Viewed 616

I'm newbie both in the forum and in Python, I hope I'll do everything in the right way (tell me everything I'm doing wrong so I can improve it).

About my question: I've many class-object (is this the correct name?) in a list (this class-object are all the rectangle inside a matrix(=nested list)).

All of this rectangle has many attributes, some of these are: "X_SX", "Y_SX", "X_DX", "Y_DX" (they are the coordinates of left-up vertex ("X_SX", "Y_SX") and right-down vertex ("X_DX", "Y_DX") of the rectangle.

I've to find the min value of X_SX and Y_SX and the max value of X_DX and Y_SX. This is what I've done:

def find_box(rectangles):
    x_sx = (min(getattr(rec, 'x_sx') for rec in rectangles))
    y_sx = (min(getattr(rec, 'y_sx') for rec in rectangles))
    x_dx = (max(getattr(rec, 'x_dx') for rec in rectangles))
    y_dx = (max(getattr(rec, 'y_dx') for rec in rectangles))

It actually work but I was wondering if there is a way to do it in a better way and avoid to call the for-loop 4 times in the same list

NOTE: I'm not sure if I explain this badly but for a better comprehension this is the list "rectangles" where "Rectangle" is the Class I've definied:

[<__main__.Rettangolo object at 0x000001FAF3971C70>, <__main__.Rettangolo object at 0x000001FAF36E2B80>, <__main__.Rettangolo object at 0x000001FAF3971850>, <__main__.Rettangolo object at 0x000001FAF3B55A30>]

Thank you for your time and your help!

3 Answers

I agree with Turun Ambartanen that your code is readable as is. Should you desire to speed things up, I will point you to the numpy python standard library (numpy is "numerical python.")

Numpy arrays will allow you to eliminate iterations, if that's desirable. If you have a lot of rectangles in your list, you will get a big performance boost by using them. (I think I see just four in your example, so this might go beyond what you're looking for.) The disadvantage of arrays is that they may result in harder to read code. I may be going beyond what a person new to python is trying to learn, but here goes:

## import numpy and give it the short name 'np'
import numpy as np

## Define a numpy array from the lists.
## np.array takes lists and turns them into the axes of
## an array.

corners = np.array([[rec.x_sx,
                     rec.x_sy, 
                     rec.x_dx, 
                     rec.x_dy] for rec in rectangles])

## Numpy provides functions for doing math along the rows
## and columns of an array. (And lots more!)
## np.max returns the largest element in an array
## the axis argument says which dimension to find the max
## on. This produces a 1 row, 4 column array

maxima = np.max(corners, axis = 0)

## And for the min-values
minima = np.min(corners, axis = 0)

##Edit: finally, so the values are stored as you did above:
x_sx = maxima[0]
y_sx = maxima[1]
x_dx = minima[2]
y_dx = minima[3]

You can index numpy arrays in ways similar to lists (except they can be multi-dimensional.) The above code gives a 1D array, so you can get the x_sx value you want with maxima[0]

Aside from using rec.x_sx instead of getattr() this seems like a very readable solution to me.

If you definitely want to get rid of the four list comprehensions you could make one big loop in which you track the least/greatest value of the desired property (x_sx, ...) and the rectangle to which it belongs.

min_x_sx = 1000  # a very big number. 
                 # tracking max values in a list requires starting with a very small number
min_x_sx_rectangle = None
# for the other three corners as well
for rec in rectangles:
    if rec.x_sx < min_x_sx:
        min_x_sx_rectangle = rec
        min_x_sx = rec.x_sx
    # for the other three corners as well

min and max take a key function, so

x_sx = (min(getattr(rec, 'x_sx') for rec in rectangles))

can be written as:

from operator import attrgetter
x_sx = min(rectangles, key=attrgetter('x_sx')).x_sx
Related