Reverse minor characteristic in python sorted ()

I'm trying to sort a list and I can't figure out how to change the order of the second sorting characteristic.

import math
ps = {1:(1,1),2:(3,2),3:(3,-3),4:(-3,4),5:(-2,-2),6:(3,3),7:(1,-1)} 
l = []
for x in range(1,8):

    l.append((math.atan2(ps[x][1],ps[x][0]),ps[x]))

for c in sorted(l, key = lambda t:(t[0], math.sqrt((0-t[1][0])**2 + (0-t[1][1])**2)),reverse = True):
    print(c)

      

The first sorting property is sorted by angle, the second is sorted by distance from the origin, if the angle is. Does anyone know how to do this. Thanks in advance for your help.

+3


source to share


4 answers


Place a minus sign in front of the second sort characteristic:



for c in sorted(l, key = lambda t:(t[0], -math.sqrt((0-t[1][0])**2 + (0-t[1][1])**2)),reverse = True):
    print(c)

      

+3


source


You can do two of the least significant. Python sorting is stable, so the order defined by the first sort will be held when you do the second.

for c in sorted(sorted(l, key = lambda t:math.sqrt((0-t[1][0])**2 + (0-t[1][1])**2)), key = lambda t:t[0], reverse=True):

      



This method works even if the keys are not numeric.

+2


source


If your key

-function gets long and / or complex, you can also use a custom class that compares how you want. It's a little slower, but might be more readable, especially since you can comment out the code:

import math

class Angle_Distance(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.angle = math.atan2(self.y, self.x)
        self.distance = math.hypot(self.x, self.y)

    def __repr__(self):
        return '{self.__class__.__name__}(x={self.x}, y={self.y})'.format(self=self)

    def __eq__(self, other):
        # this method is not strictly necessary, but makes the class more generally useful.
        return self.x == other.x and self.y == other.y

    def __lt__(self, other):
        if self.angle < other.angle:
            return True
        elif self.angle == other.angle:  # break ties
            return self.distance > other.distance
        else:
            return False

      

And this can be applied in the list:

>>> ps = {1:(1,1),2:(3,2),3:(3,-3),4:(-3,4),5:(-2,-2),6:(3,3),7:(1,-1)} 
>>> l = [Angle_Distance(i, j) for i, j in ps.values()]
>>> sorted(l, reverse=True)
[Angle_Distance(x=-3, y=4),
 Angle_Distance(x=1, y=1),
 Angle_Distance(x=3, y=3),
 Angle_Distance(x=3, y=2),
 Angle_Distance(x=1, y=-1),
 Angle_Distance(x=3, y=-3),
 Angle_Distance(x=-2, y=-2)]

      

But you can also use it as a key

-function:

>>> ps = {1:(1,1),2:(3,2),3:(3,-3),4:(-3,4),5:(-2,-2),6:(3,3),7:(1,-1)}
>>> sorted(ps.values(), key=lambda x: Angle_Distance(x[0], x[1]), reverse=True)
[(-3, 4), (1, 1), (3, 3), (3, 2), (1, -1), (3, -3), (-2, -2)]

      

+1


source


You already have some good answers, but I thought you might appreciate another one.;)

Paul Cornelius's answer shows an easy way to do this: just negate one of the numbers generated by your key function. However, as Mark Ransome pointed out, you can only do this with numeric values, but luckily Python TimSort is stable. This way you can sort the list multiple times according to multiple criteria, and each subsequent sort will not interfere with items that are equal according to the current sort key function. It's a little less efficient for multiple passes, so it's best to use Paul's technique when you can. OTOH, TimSort is very efficient at handling partially sorted lists, so when doing multi-pass sorts, extra passes are usually pretty fast.

You can build your l

list a little more efficiently using a list comprehension. But even if you don't want to use the comp list, it would be better to quote directly above the values ps

rather than using range

- it is more efficient and also more general as it works if the keys aren a contiguous range. The values ​​may not be in numerical key order (although they will be in Python 3.6+), but that doesn't matter, since we're sorting anyway l

.

Here's a demo:

import math

ps = {
    1: (1, 1), 2: (3, 2), 3: (3, -3), 4: (-3, 4), 
    5: (-2, -2), 6: (3, 3), 7: (1, -1),
}

l = []
for t in ps.values():
    l.append((math.atan2(t[1], t[0]), t))

for t in l:
    print(t)

      

Output

(0.7853981633974483, (1, 1))
(0.5880026035475675, (3, 2))
(-0.7853981633974483, (3, -3))
(2.214297435588181, (-3, 4))
(-2.356194490192345, (-2, -2))
(0.7853981633974483, (3, 3))
(-0.7853981633974483, (1, -1))

      

Using the comp list, we can build l

in one line instead of three lines:

l = [(math.atan2(t[1], t[0]), t) for t in ps.values()]

      

We can shorten this value a bit by using the extended cut for the inverse t

and the *

"splat" operator to unpack the inverse t

:

l = [(math.atan2(*t[::-1]), t) for t in ps.values()]

      


Your expression

math.sqrt((0-t[1][0])**2 + (0-t[1][1])**2)

      

too wordy. (0-x)**2

equals x**2

, so we can rewrite this expression as

math.sqrt(t[1][0]**2 + t[1][1]**2)

      

However, there is an even better way. The module math

has a function hypot

that MSeifert used. hypot(x, y)

calculates the length of the hypotenuse of a right triangle with sides x

and y

, so your expression can be written as

math.hypot(t[1][0], t[1][1])

      

or using unpacking,

math.hypot(*t[1])

      


Putting it all together:

import math

ps = {
    1: (1, 1), 2: (3, 2), 3: (3, -3), 4: (-3, 4), 
    5: (-2, -2), 6: (3, 3), 7: (1, -1),
}

l = [(math.atan2(*t[::-1]), t) for t in ps.values()]
l.sort(key=lambda t: (-t[0], math.hypot(*t[1])))
for t in l:
    print(t)

      

Output

(2.214297435588181, (-3, 4))
(0.7853981633974483, (1, 1))
(0.7853981633974483, (3, 3))
(0.5880026035475675, (3, 2))
(-0.7853981633974483, (1, -1))
(-0.7853981633974483, (3, -3))
(-2.356194490192345, (-2, -2))

      


If you want to sort the tuples and don't need to preserve the corners, you can do it like this:

l = sorted(ps.values(), key=lambda t: (-math.atan2(*t[::-1]), math.hypot(*t)))
print(l)

      

Output

[(-3, 4), (1, 1), (3, 3), (3, 2), (1, -1), (3, -3), (-2, -2)]

      

+1


source







All Articles