13
$\begingroup$

Suppose we have a list of values and a function f. I want to find which of the elements maximizes the return value of the function in Mathematica. Let call the function ListMaxArg. On the following example,

ListMaxArg[Total, {{1, 2, 3}, {10}}]

it should return {10}. Is there any such function in Mathematica's library? If not, what is the simplest way to write it? I can for sure write the function using a loop, but I am looking for something more functional style.

$\endgroup$
2

2 Answers 2

15
$\begingroup$

While using Ordering as in Mr.Wizard's answer will most likely be the fastest non-compiled solution, it will not return all possible arguments that maximize the return value. Here's a simple way of writing a function that does this:

listMaxArg[f_, list_] := With[{max = f /@ list // Max}, Select[list, f@# == max &]]

Here's an example that compares the two solutions:

a = {{1, 2, 3}, {10}, {6, 4}};
listMaxArg[Total, a] 
(* Out[1]= {{10}, {6, 4}} *)

listMaxArgMrWiz[Total, a]
(*Out[2]= {{6, 4}} *)
$\endgroup$
13
  • $\begingroup$ +1. Note that Pick will be faster than Select here, about twice faster for really long lists. $\endgroup$ Commented Feb 23, 2012 at 11:43
  • $\begingroup$ @LeonidShifrin Thanks. I did remember that Pick should be faster than Select, and tried listMaxArgPick[f_, list_] := Pick[list, Unitize[##/Max[##] &@(f /@ list), 1], 1]. Unfortunately, this unpacks, which slows it down, so I put it aside to poke at later. Can you point me to what I'm doing incorrectly (or how that can be written better)? I generated a random list with l = Table[RandomInteger[5, RandomInteger[{1, 5}]], {100000}]; $\endgroup$
    – rm -rf
    Commented Feb 23, 2012 at 12:48
  • 3
    $\begingroup$ Pick has an optional third parameter, which allows to specify which element you want to be picked. The code I have is thus: listMaxArgAlt[f_, lst_] := Pick[lst, #, Max[#]] &[f /@ lst], and it is about twice faster, since I don't have to play games with Unitize. $\endgroup$ Commented Feb 23, 2012 at 12:57
  • $\begingroup$ It should be noted that Pick unpacks in version 7. $\endgroup$
    – Mr.Wizard
    Commented Feb 23, 2012 at 13:29
  • 1
    $\begingroup$ Interestingly, Select does not unpack, because for each element, Total applied to it, does not unpack. Still, it is slower than Pick here, because of the overhead of extra invocation of Function[f@#==max] (symbolic overhead). Pick has to unpack because it performs structural comparison (based on internal equivalent of SameQ actually, not Equal). This situation is similar to that in this question. This is an interesting and didactic example IMO, this present one. $\endgroup$ Commented Feb 23, 2012 at 14:30
17
$\begingroup$

Update 2017

Mathematica 10 introduced MaximalBy, becoming the canonical method.

MaximalBy[{{1, 2, 3}, {10}, {6, 2}, {3, 7}}, Total]
{{10}, {3, 7}}

It is fast as shown in the updated timings below.


If I understand the question this should be fastest:

listMaxArg[f_, L_List] := L ~Extract~ Ordering[f /@ L, -1]

listMaxArg[Total, {{1, 2, 3}, {10}}]
{10}

You could also find the top n values by using -n as the second argument to Ordering.
This could be included in the function, e.g. listMaxArg[f_, L_List, n_Integer] := . . .


This method should be fast for finding multiple maximum values:

listMaxArg[f_, L_List] := L ~Extract~ Position[#, Max@#] &[f /@ L]

listMaxArg[Total, {{1, 2, 3}, {10}, {6, 2}, {3, 7}}]
{{10}, {3, 7}}

I argue the superiority of the Extract-Position method (from Arnoud Buzing) over Select on the basis of timings. I will use Tr in the place of Total as it is faster on Packed Arrays, and therefore better shows the overhead of each method.

listMaxArgRM[f_, list_] :=
  With[{max = f /@ list // Max}, Select[list, f@# == max &]]

listMaxArgMrW[f_, L_List] :=
  L ~Extract~ Position[#, Max@#] &[f /@ L]

SeedRandom[1]
list = RandomInteger[7, #] & /@ RandomInteger[{1, 5}, 1000000];

r1 = listMaxArgRM[Tr, list];  // RepeatedTiming
r2 = listMaxArgMrW[Tr, list]; // RepeatedTiming
r3 = MaximalBy[list, Tr];     // RepeatedTiming
r1 === r2 === r3
{0.77, Null}

{0.219, Null}

{0.191, Null}

True
$\endgroup$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.