简体   繁体   中英

max(*args, key = ... ). How to modify key to get just certain values?

I am a bit confused with max(*args, key =..) function. Imagine I have a following list: my_list = [2, 3, 4, -5, -2, -1] . I want to find maximum value within the negative values in my_list. For this, I am using the following code max(my_list, key = lambda x: x<0) , but it gives me -5 . Can you please explain me the logic?

What you are doing when calling max(my_list, key = lambda x: x<0) is saying: "which value in the list is largest, but compare the value of the result for the expression (x<0) for each element instead of the element's value." Therefore, the max operator basically sees the list as [False, False, False, True, True, True] when deciding which is biggest and in python this is equivalent to [0, 0, 0, 1, 1, 1] . max returns the first maximum element it sees, which corresponds to -5.

You likely want to filter the list to only negative values first, then call max on the result

While I wouldn't use max alone for this, you could , with an appropriate key function that doesn't collapse multiple values:

max(my_list, key=lambda x: -float('inf') if x > 0 else x)

This causes all positive values to compare as if they were negative infinity, which cannot be the largest value in a list that contains any non-positive values.

(If all the values are all positive, you'll need to adapt this in some way, because it will still return the first value in the list: no successive value is bigger. Unless requested, I consider that beyond the scope of the question as currently stated.)

Yes, you CAN do this by a single line with max function. I explain it below.

And ALSO there is a better way than list comprehension that I explained in the end.

Reason of your problem:

This is how the max function works if you have provided the key parameter. It will loop through the values of the list and then it applies the lambda function (named key ) on each value. So all of the values will be mapped to the result of the lambda function. Then it performs the max function on these results.

So when you have key=lambda x: x<0 this will return True or False as x<0 is a boolean expression. So the result list would be like [False, False, False, True, True, True] . These False and True values are processed as 0 and 1 values for the max function.

The max function gets [0, 0, 0, 1, 1, 1] and it finds the first one that is the maximum number in the list (number 1) that is the first 1 in the list that corresponds to number -5 in the original list.

Solution:

There are multiple ways to find the maximum negative number like using a for loop or list comprehension or even combining filter and max . But, to understand the max function and it's parameters better I show you the way you can do the thing you want (find the maximum negative number) by the max function.

max(my_list, key = lambda x: min(my_list) if x>=0 else x)

This code will find the maximum negative number.

How does this code work?

The provided lambda expression will map all the number as this:

If the number is positive we replace it's value for the max function as the minimum number so it will never be chosen as the maximum negative number. (We could do this by removing all the negative number by filter but I decided to do all the job just with lambda to show you the way.)

If the number is negative it will keep it's own value. So for your example that the list is my_list = [2, 3, 4, -5, -2, -1] it will create [-5, -5, -5, -5, -2, -1] and then it will perform the max function on this list that it will find the -1 number that is what you want.

I wish it could help you understand how to find the maximum negative number just by the max function and the way maximum function works.

NOTE: This code is not optimal. Because every time it computes the minimum value. This code is only for learning purposes.

Best way to do this

I believe a better code is this:

max((num for num in my_list if num<0))

Note that this is not list comprehension. It is better than list comprehension . Instead it creates a generator that only process one value at a time. This is better that list comprehension because list comprehension creates the whole list at once and then send it to the max function. But this code process each value at a time and don't fill the memory with a big list. So this is more efficient.

I would use a list comprehension to filter out the positive values and then find the maximum.

max([i for i in my_list if i < 0]) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM