Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #81641 > unrolled thread
| Started by | albert@spenarnc.xs4all.nl (Albert van der Horst) |
|---|---|
| First post | 2014-11-27 17:34 +0000 |
| Last post | 2014-12-04 09:09 -0500 |
| Articles | 14 — 7 participants |
Back to article view | Back to comp.lang.python
How is max supposed to work, especially key. albert@spenarnc.xs4all.nl (Albert van der Horst) - 2014-11-27 17:34 +0000
Re: How is max supposed to work, especially key. Peter Otten <__peter__@web.de> - 2014-11-27 19:01 +0100
Re: How is max supposed to work, especially key. albert@spenarnc.xs4all.nl (Albert van der Horst) - 2014-12-04 10:09 +0000
Re: How is max supposed to work, especially key. Chris Angelico <rosuav@gmail.com> - 2014-12-04 21:18 +1100
Re: How is max supposed to work, especially key. albert@spenarnc.xs4all.nl (Albert van der Horst) - 2014-12-04 10:35 +0000
Re: How is max supposed to work, especially key. Jussi Piitulainen <jpiitula@ling.helsinki.fi> - 2014-12-04 12:51 +0200
Re: How is max supposed to work, especially key. Peter Otten <__peter__@web.de> - 2014-12-04 12:31 +0100
Re: How is max supposed to work, especially key. Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-12-04 23:03 +1100
Re: How is max supposed to work, especially key. Terry Reedy <tjreedy@udel.edu> - 2014-12-04 10:47 -0500
Re: How is max supposed to work, especially key. Jussi Piitulainen <jpiitula@ling.helsinki.fi> - 2014-12-04 12:45 +0200
Re: How is max supposed to work, especially key. Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-12-04 22:55 +1100
Re: How is max supposed to work, especially key. Jussi Piitulainen <jpiitula@ling.helsinki.fi> - 2014-12-04 14:58 +0200
Re: How is max supposed to work, especially key. Peter Otten <__peter__@web.de> - 2014-12-04 12:24 +0100
Re: How is max supposed to work, especially key. random832@fastmail.us - 2014-12-04 09:09 -0500
| From | albert@spenarnc.xs4all.nl (Albert van der Horst) |
|---|---|
| Date | 2014-11-27 17:34 +0000 |
| Subject | How is max supposed to work, especially key. |
| Message-ID | <547760b3$0$6876$e4fe514c@dreader36.news.xs4all.nl> |
In the Rosetta code I come across this part of
LU-decomposition.
def pivotize(m):
"""Creates the pivoting matrix for m."""
n = len(m)
ID = [[float(i == j) for i in xrange(n)] for j in xrange(n)]
for j in xrange(n):
row = max(xrange(j, n), key=lambda i: abs(m[i][j]))
if j != row:
ID[j], ID[row] = ID[row], ID[j]
return ID
That it's using a cast from boolean to float and using
at the other moment a float as a boolean, suggest that this
code is a bit too clever for its own good, but anyway.
My problem is with the max. I never saw a max with a key.
In my python help(max) doesn't explain the key. It says that
max can handle an iterator (I didn't know that), and you can
pass and optional "key=func", but that's all.
I expect it to be something like
elements in the iterator are taken into account only if the
key applied to the iterator evaluates to a True value.
However that doesn't pan out:
"
max(xrange(100,200), key=lambda i: i%17==0 )
102
"
I expect the maximum number that is divisible by 17 in the
range, not the minimum.
Can anyone shed light on this?
Groetjes Albert
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
[toc] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2014-11-27 19:01 +0100 |
| Message-ID | <mailman.16378.1417111312.18130.python-list@python.org> |
| In reply to | #81641 |
Albert van der Horst wrote: > In the Rosetta code I come across this part of > LU-decomposition. > > def pivotize(m): > """Creates the pivoting matrix for m.""" > n = len(m) > ID = [[float(i == j) for i in xrange(n)] for j in xrange(n)] > for j in xrange(n): > row = max(xrange(j, n), key=lambda i: abs(m[i][j])) > if j != row: > ID[j], ID[row] = ID[row], ID[j] > return ID > > That it's using a cast from boolean to float and using > at the other moment a float as a boolean, suggest that this > code is a bit too clever for its own good, but anyway. > > My problem is with the max. I never saw a max with a key. > > In my python help(max) doesn't explain the key. It says that > max can handle an iterator (I didn't know that), and you can > pass and optional "key=func", but that's all. > > I expect it to be something like > elements in the iterator are taken into account only if the > key applied to the iterator evaluates to a True value. > > However that doesn't pan out: > " > max(xrange(100,200), key=lambda i: i%17==0 ) > 102 > " > > I expect the maximum number that is divisible by 17 in the > range, not the minimum. > > Can anyone shed light on this? Given a function f() max(items, key=f) returns the element of the `items` sequence with the greatest f(element), e. g. for max(["a", "bcd", "ef"], key=len) the values 1, 3, 2 are calculated and the longest string in the list is returned: >>> max(["a", "bcd", "ef"], key=len) 'bcd' If there is more than one item with the maximum calculated the first is given, so for your attempt max(xrange(100,200), key=lambda i: i%17==0 ) the values False, False, True, False, ... are calculated and because >>> True > False True the first one with a True result is returned.
[toc] | [prev] | [next] | [standalone]
| From | albert@spenarnc.xs4all.nl (Albert van der Horst) |
|---|---|
| Date | 2014-12-04 10:09 +0000 |
| Message-ID | <548032dd$0$15926$e4fe514c@dreader35.news.xs4all.nl> |
| In reply to | #81646 |
In article <mailman.16378.1417111312.18130.python-list@python.org>, Peter Otten <__peter__@web.de> wrote: >Albert van der Horst wrote: > >> In the Rosetta code I come across this part of >> LU-decomposition. >> >> def pivotize(m): >> """Creates the pivoting matrix for m.""" >> n = len(m) >> ID = [[float(i == j) for i in xrange(n)] for j in xrange(n)] >> for j in xrange(n): >> row = max(xrange(j, n), key=lambda i: abs(m[i][j])) >> if j != row: >> ID[j], ID[row] = ID[row], ID[j] >> return ID >> >> That it's using a cast from boolean to float and using >> at the other moment a float as a boolean, suggest that this >> code is a bit too clever for its own good, but anyway. >> >> My problem is with the max. I never saw a max with a key. >> >> In my python help(max) doesn't explain the key. It says that >> max can handle an iterator (I didn't know that), and you can >> pass and optional "key=func", but that's all. >> >> I expect it to be something like >> elements in the iterator are taken into account only if the >> key applied to the iterator evaluates to a True value. >> >> However that doesn't pan out: >> " >> max(xrange(100,200), key=lambda i: i%17==0 ) >> 102 >> " >> >> I expect the maximum number that is divisible by 17 in the >> range, not the minimum. >> >> Can anyone shed light on this? > >Given a function f() max(items, key=f) returns the element of the `items` >sequence with the greatest f(element), e. g. for > >max(["a", "bcd", "ef"], key=len) > >the values 1, 3, 2 are calculated and the longest string in the list is >returned: > >>>> max(["a", "bcd", "ef"], key=len) >'bcd' > >If there is more than one item with the maximum calculated the first is >given, so for your attempt > >max(xrange(100,200), key=lambda i: i%17==0 ) > >the values False, False, True, False, ... are calculated and because > >>>> True > False >True > >the first one with a True result is returned. > So in that case max doesn't return the maximum (True), but instead something else. Useful as that function may be, it shouldn't have been called max. I don't blame myself for being misled. Groetjes Albert -- Albert van der Horst, UTRECHT,THE NETHERLANDS Economic growth -- being exponential -- ultimately falters. albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-12-04 21:18 +1100 |
| Message-ID | <mailman.16552.1417688329.18130.python-list@python.org> |
| In reply to | #81888 |
On Thu, Dec 4, 2014 at 9:09 PM, Albert van der Horst <albert@spenarnc.xs4all.nl> wrote: >>If there is more than one item with the maximum calculated the first is >>given, so for your attempt >> >>max(xrange(100,200), key=lambda i: i%17==0 ) > >> >>the values False, False, True, False, ... are calculated and because >> >>>>> True > False >>True >> >>the first one with a True result is returned. >> > > So in that case max doesn't return the maximum (True), but instead > something else. > > Useful as that function may be, it shouldn't have been called max. > > I don't blame myself for being misled. If lots of them are equally the largest, by whatever definition of largest you have, it has to do one of three things: 1) Raise an exception 2) Return multiple items (either as a tuple, or a generator, or something) 3) Pick one of them and return it. Python's max() does the third, and for the picking part, uses the first one it comes across - a decent way to do it. If there's no clear maximum, it can't do any better than that. It's still returning something for which there is no greater. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | albert@spenarnc.xs4all.nl (Albert van der Horst) |
|---|---|
| Date | 2014-12-04 10:35 +0000 |
| Message-ID | <548038f8$0$15841$e4fe514c@dreader35.news.xs4all.nl> |
| In reply to | #81890 |
In article <mailman.16552.1417688329.18130.python-list@python.org>, Chris Angelico <rosuav@gmail.com> wrote: >On Thu, Dec 4, 2014 at 9:09 PM, Albert van der Horst ><albert@spenarnc.xs4all.nl> wrote: >>>If there is more than one item with the maximum calculated the first is >>>given, so for your attempt >>> >>>max(xrange(100,200), key=lambda i: i%17==0 ) >> >>> >>>the values False, False, True, False, ... are calculated and because >>> >>>>>> True > False >>>True >>> >>>the first one with a True result is returned. >>> >> >> So in that case max doesn't return the maximum (True), but instead >> something else. >> >> Useful as that function may be, it shouldn't have been called max. >> >> I don't blame myself for being misled. > >If lots of them are equally the largest, by whatever definition of >largest you have, it has to do one of three things: > >1) Raise an exception >2) Return multiple items (either as a tuple, or a generator, or something) >3) Pick one of them and return it. > >Python's max() does the third, and for the picking part, uses the >first one it comes across - a decent way to do it. > >If there's no clear maximum, it can't do any better than that. It's >still returning something for which there is no greater. I agree that it is a useful function and that it is doing the right thing. What is wrong is the name. I refer to the fact that it is not returning the maximum. It returns the iterator value that leads to the maximum. A function that doesn't return a maximum shouldn't be called maximum. > >ChrisA -- Albert van der Horst, UTRECHT,THE NETHERLANDS Economic growth -- being exponential -- ultimately falters. albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
[toc] | [prev] | [next] | [standalone]
| From | Jussi Piitulainen <jpiitula@ling.helsinki.fi> |
|---|---|
| Date | 2014-12-04 12:51 +0200 |
| Message-ID | <qot7fy74ri3.fsf@ruuvi.it.helsinki.fi> |
| In reply to | #81894 |
Albert van der Horst writes: > Chris Angelico wrote: > > If there's no clear maximum, it can't do any better than > > that. It's still returning something for which there is no > > greater. > > I agree that it is a useful function and that it is doing > the right thing. What is wrong is the name. > I refer to the fact that it is not returning the maximum. > It returns the iterator value that leads to the maximum. > A function that doesn't return a maximum shouldn't be called > maximum. It's called max. A maximal element need not be unique even in mathematics: <http://en.wikipedia.org/wiki/Maximal_element>.
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2014-12-04 12:31 +0100 |
| Message-ID | <mailman.16557.1417692680.18130.python-list@python.org> |
| In reply to | #81894 |
Albert van der Horst wrote: > I agree that it is a useful function and that it is doing > the right thing. What is wrong is the name. > I refer to the fact that it is not returning the maximum. > It returns the iterator value that leads to the maximum. > A function that doesn't return a maximum shouldn't be called > maximum. But a maximum is often ambiguous. What is max(people), the oldest, tallest, or richest person? The key argument is a convenient way to disambiguate.
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-12-04 23:03 +1100 |
| Message-ID | <54804d7a$0$13012$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #81894 |
Albert van der Horst wrote: > I agree that it is a useful function and that it is doing > the right thing. What is wrong is the name. > I refer to the fact that it is not returning the maximum. > It returns the iterator value that leads to the maximum. That is incorrect. It returns the maximum value, that is, the value greater than all the other values. What is different is the way "this value is greater than that value" is tested for the purposes of calculating this maximum. > A function that doesn't return a maximum shouldn't be called > maximum. Naturally. But this does calculate the maximum, according to the given key function. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2014-12-04 10:47 -0500 |
| Message-ID | <mailman.16562.1417708068.18130.python-list@python.org> |
| In reply to | #81894 |
On 12/4/2014 5:35 AM, Albert van der Horst wrote: > I agree that it is a useful function and that it is doing > the right thing. What is wrong is the name. > I refer to the fact that it is not returning the maximum. > It returns the iterator value that leads to the maximum. > A function that doesn't return a maximum shouldn't be called > maximum. The key function serves the same purpose as with sort. It define a custom ordering of items, not the values returned. 'x < y' is defined as key(x) < key(y), where the comparison of keys uses the standard (and for numbers, natural) built-in ordering. So max returns the max value according to the order you define with the key function. The default key is the identity function, so the default order is the builtin order. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Jussi Piitulainen <jpiitula@ling.helsinki.fi> |
|---|---|
| Date | 2014-12-04 12:45 +0200 |
| Message-ID | <qotbnnj4rst.fsf@ruuvi.it.helsinki.fi> |
| In reply to | #81888 |
Albert van der Horst writes:
> Useful as that function [Python's max with a key] may be, it
> shouldn't have been called max.
The meaning of the key should be added to help(max), if it still isn't
- "returns a maximal element or an element that maximizes the key".
In some communities they call it arg max, with a thin space between
the components of the name. Or maybe no space: argmax.
Would you also want sorted called something else when used with a key?
Because it doesn't produce a sorted list of the keys either:
>>> data = ("short", "long", "average")
>>> sorted(data, key=len)
['long', 'short', 'average']
>>> max(data, key=len)
'average'
>>> sorted(map(len, data))
[4, 5, 7]
>>> max(map(len, data))
7
While the key is not a filter, a filter is as available as the map
above:
>>> max(filter(lambda i : i % 17 == 0, range(100, 200)))
187
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-12-04 22:55 +1100 |
| Message-ID | <54804bb6$0$12988$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #81896 |
Jussi Piitulainen wrote:
> Would you also want sorted called something else when used with a key?
> Because it doesn't produce a sorted list of the keys either:
>
> >>> data = ("short", "long", "average")
> >>> sorted(data, key=len)
> ['long', 'short', 'average']
> >>> max(data, key=len)
> 'average'
I agree with the point you are making, but I disagree with the wording you
use. The result of calling sort() with key=len *is* sorted. It is sorted by
length of the word.
Same for calling max() with a key. The result is still the maximum value.
The difference is how you decide which of two elements is greater:
max(list_of_foods, key=calories)
max(list_of_foods, key=weight)
max(list_of_foods, key=cost)
That is three different ways to decide which is the maximal food in the
list: by number of calories, by weight, or by cost.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Jussi Piitulainen <jpiitula@ling.helsinki.fi> |
|---|---|
| Date | 2014-12-04 14:58 +0200 |
| Message-ID | <qot1tofa7xj.fsf@ruuvi.it.helsinki.fi> |
| In reply to | #81902 |
Steven D'Aprano writes:
> Jussi Piitulainen wrote:
>
> > Would you also want sorted called something else when used with a
> > key? Because it doesn't produce a sorted list of the keys either:
> >
> > >>> data = ("short", "long", "average")
> > >>> sorted(data, key=len)
> > ['long', 'short', 'average']
> > >>> max(data, key=len)
> > 'average'
>
> I agree with the point you are making, but I disagree with the
> wording you use. The result of calling sort() with key=len *is*
> sorted. It is sorted by length of the word.
It's sorted but it's not a list of the keys. That seemed to be a point
of contention about naming max-with-key max: it doesn't return a key.
> Same for calling max() with a key. The result is still the maximum
> value. The difference is how you decide which of two elements is
> greater:
>
> max(list_of_foods, key=calories)
> max(list_of_foods, key=weight)
> max(list_of_foods, key=cost)
>
> That is three different ways to decide which is the maximal food in
> the list: by number of calories, by weight, or by cost.
Yes. I don't see any disagreement between us.
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2014-12-04 12:24 +0100 |
| Message-ID | <mailman.16556.1417692302.18130.python-list@python.org> |
| In reply to | #81888 |
Albert van der Horst wrote:
> In article <mailman.16378.1417111312.18130.python-list@python.org>,
> Peter Otten <__peter__@web.de> wrote:
>>Albert van der Horst wrote:
>>
>>> In the Rosetta code I come across this part of
>>> LU-decomposition.
>>>
>>> def pivotize(m):
>>> """Creates the pivoting matrix for m."""
>>> n = len(m)
>>> ID = [[float(i == j) for i in xrange(n)] for j in xrange(n)]
>>> for j in xrange(n):
>>> row = max(xrange(j, n), key=lambda i: abs(m[i][j]))
>>> if j != row:
>>> ID[j], ID[row] = ID[row], ID[j]
>>> return ID
>>>
>>> That it's using a cast from boolean to float and using
>>> at the other moment a float as a boolean, suggest that this
>>> code is a bit too clever for its own good, but anyway.
>>>
>>> My problem is with the max. I never saw a max with a key.
>>>
>>> In my python help(max) doesn't explain the key. It says that
>>> max can handle an iterator (I didn't know that), and you can
>>> pass and optional "key=func", but that's all.
>>>
>>> I expect it to be something like
>>> elements in the iterator are taken into account only if the
>>> key applied to the iterator evaluates to a True value.
>>>
>>> However that doesn't pan out:
>>> "
>>> max(xrange(100,200), key=lambda i: i%17==0 )
>>> 102
>>> "
>>>
>>> I expect the maximum number that is divisible by 17 in the
>>> range, not the minimum.
>>>
>>> Can anyone shed light on this?
>>
>>Given a function f() max(items, key=f) returns the element of the `items`
>>sequence with the greatest f(element), e. g. for
>>
>>max(["a", "bcd", "ef"], key=len)
>>
>>the values 1, 3, 2 are calculated and the longest string in the list is
>>returned:
>>
>>>>> max(["a", "bcd", "ef"], key=len)
>>'bcd'
>>
>>If there is more than one item with the maximum calculated the first is
>>given, so for your attempt
>>
>>max(xrange(100,200), key=lambda i: i%17==0 )
>
>>
>>the values False, False, True, False, ... are calculated and because
>>
>>>>> True > False
>>True
>>
>>the first one with a True result is returned.
>>
>
> So in that case max doesn't return the maximum (True), but instead
> something else.
>
> Useful as that function may be, it shouldn't have been called max.
>
> I don't blame myself for being misled.
I believe you still misunderstand. Again, this time with an almost real-
world car example:
max(values key=key)
calculates key(value) for every value in values. key() can be len() or an
attribute getter, so that
max(cars, key=lambda car: car.weight)
finds the heaviest car and
max(cars, key=lambda car: car.speed)
finds the fastest car. (If there is a tie the first car with maximum
weight/speed is returned.)
The advantage of this approach is that you don't have to choose a "natural"
order, i. e. should
max(cars)
find the fastest, or the heaviest, or the [you name it] car?
Also, you are really interested in the car, and
max(car.speed for car in cars)
would only give you the speed of the fastest car, not the car object itself.
To find the fastest car without a key argument you'd have to write
fastest_car = max((car.speed, car) for car in cars)[-1]
or even
fastest_car = max((car.speed, i, car) for (i, car) in enumerate(cars))[-1]
if you are thorough and want the first car in the sequence with maximum
speed or have to deal with car objects that aren't comparable.
The example where the key returns a boolean value is very uncommon, but
booleans are not treated specially. So while you can write
first_red_car = max(cars, key=lambda car: car.color == RED)
if first_red_car.color == RED:
print(first_red_car)
else:
print("no red cars available")
that is unidiomatic. It is also inefficient because max() always iterates
over the whole cars sequence in search for an even redder car. False == 0,
and True == 1, and max() cannot be sure that the key function will never
return 2 or TruerThanTrue ;) -- but the programmer usually knows.
Better:
red_cars = (car for car in cars if car.color == RED)
first_red_car = next(red_cars, None)
if first_red_car is not None:
print(first_red_car)
else:
print("no red cars available")
PS: Another thing to consider is the similarity with sorted().
You can sort by speed
sorted(cars, key=lambda car: car.speed)
or "redness"
sorted(cars, key=lambda car: car.color == RED)
but you will see the former much more often than the latter.
[toc] | [prev] | [next] | [standalone]
| From | random832@fastmail.us |
|---|---|
| Date | 2014-12-04 09:09 -0500 |
| Message-ID | <mailman.16560.1417702186.18130.python-list@python.org> |
| In reply to | #81888 |
On Thu, Dec 4, 2014, at 05:09, Albert van der Horst wrote: > So in that case max doesn't return the maximum (True), but instead > something else. If you want to find the "largest" item in a list of of strings, sorted case-insensitively, you might use str.lower or locale.strxfrm as the key function. If two strings are case-insensitively identical, using str.lower will return an arbitrary one (or maybe the first or last found in the original list), using locale.strxfrm will return the one that is in uppercase in the first position where they are different. But you don't want it to return the all-lowercase version (or the messy binary thing returned by strxfrm), you want it to return the string from the original list. Think of it this way: the key function returns a proxy object whose comparison operators compare according to the relationship you want to compare the originals by. Bool does not make a good proxy object. If you want to use the original as a tiebreaker, you might use lambda x: (str.lower(x), x). A key function is supposed to be a function that will always return the same result on the same input, so if you want to know "the maximum" [i.e. the actual key value that was compared by the max function] you can simply apply it again on the result.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web