2

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!

2
  • You iterated the rectangles four times. And it's better that iterating once, get x_sx, y_sx, x_dx, y_dx values and store them into separate lists. And at the end, get min, max for each list values. Commented Dec 2, 2020 at 20:11
  • @YuriRen getting the min and max of those lists (internally) requires looping once again, so you actually loop one time more. Could make it easier to read though. Commented Dec 2, 2020 at 20:15

3 Answers 3

2

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]

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

1

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

2 Comments

Unfortunately I cannot use external libraries. I forgot to mention it inside the post. Is there a way to do this without operator? Thank you!
operator is part of Python, but you can certainly write min(rectangle, key=lambda r:r.x_sx).x_sx if you prefer...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.