开发者

What's the best way to select the maximum one from a list of lists by their last element?

开发者 https://www.devze.com 2023-03-19 08:06 出处:网络
In Mathematica, Max[] is the most efficient function to get the maximum number in a list of numbers, but how do I find the list with the maximum last element in a list of lists? e.g. the 2-d coordinat

In Mathematica, Max[] is the most efficient function to get the maximum number in a list of numbers, but how do I find the list with the maximum last element in a list of lists? e.g. the 2-d coordinate with the biggest x part in a series of coordinates.

My best try is SortBy, but obviously I don't need the prog开发者_C百科ram to sort my list, only the maximum one I need.


Perhaps:

list = {{4, 3}, {5, 10}, {-2, 1}, {3, 7}}

Reverse /@ Take[#, Ordering[#, -1]] &@(Reverse /@ #) &@ list
(*
-> {{5, 10}}
*)

Exploiting the fact that Ordering[ ] orders lists by their first element

Edit

Or much better (I think):

Take[#, Ordering[Last /@ #, -1]] &@ list

Edit

Also:

#[[Ordering[#, -1, Last@#2 > Last@#1 &]]] &@list

Edit

Perhaps faster:

#[[First@Position[#, Max@#] &@(Last /@ #)]] &@list


Here is my approach using Pick

maxBy[list_, n_] := With[{s = list[[All, n]]}, Pick[list, s, Max[s]]]

maxBy[{{4, 3}, {5, 10}, {-2, 1}, {3, 7}}, 2]

(* output: 
  {{5, 10}}  
*)

This version works on any number of elements per sublist provided n is less than or equal to the length of the shortest sublist.

Timings for this version on my machine

list2 = RandomInteger[{-10^7, 10^7}, {10^6, 2}];
list3 = RandomInteger[{-10^7, 10^7}, {10^6, 3}];
list9 = RandomInteger[{-10^7, 10^7}, {10^6, 9}];

maxBy[list2, 2]; // Timing
maxBy[list3, 2]; // Timing
maxBy[list9, 2]; // Timing

(* output: 
  {0.030341, Null}  
  {0.030912, Null}  
  {0.033313, Null}  
*)

Compared to David's code

maxBy[list2, 2]; // Timing
maxBy[list3, 2]; // Timing
maxBy[list9, 2]; // Timing

(* ouput:
  {0.186175, Null}  
  {0.184989, Null}  
  {0.262018, Null}  
*)

Yoda's code

maxBy[list2, 2]; // Timing
maxBy[list3, 2]; // Timing
maxBy[list9, 2]; // Timing

(* ouput:
  {0.944016, Null}
  {0.83094, Null}
  {0.874126, Null}
*)

And belisarius' code

Reverse /@ Take[#, Ordering[#, -1]] &@(Reverse /@ #) &@list2; // Timing
Take[#, Ordering[Last /@ #, -1]] &@list2; // Timing
#[[Ordering[#, -1, Last@#2 > Last@#1 &]]] &@list2; // Timing
#[[First@Position[#, Max@#] &@(Last /@ #)]] &@list2; // Timing 

(* output:
  {0.211016, Null}
  {0.099253, Null}
  {2.03415, Null}
  {0.266934, Null}
*)


Not the most efficient but simpler?

max = Max@list[[All, -1]];
Cases[list, {_, max}]

or

max = Max@list3[[All, -1]];
Cases[list3, {_,_, max}]

Usage

list = {{40, 3}, {5, 10}, {-2, 1}, {3, 10}}

max = Max@list[[All, -1]];
Cases[list, {_, max}]

Output:

{{5, 10}, {3, 10}}


How about this function (defined here only for 2D lists):

maxBy = Module[{pattern = Reverse@Insert[{Max@#1[[All, #2]]}, _, #2]},
               Cases[#1, pattern]] &

Example:

list = {{4, 3}, {5, 10}, {20, 1}, {3, 7}};

maxBy[list, 1]    
Out[1]= {{20, 1}}

maxBy[list, 2]  
Out[2]= {{5, 10}}


Here's an approach that relies on Transpose:

maxBy = #1[[Position[t = Transpose[#1][[#2]], Max[t]][[All, 1]]]] &;

For example: list = {{4, 3}, {5, 10}, {20, 1}, {3, 7}};

maxBy[list, 1]
(* {{20, 1}}   *)

maxBy[list, 2]
(* {{5, 10}} *)

It can handle more than two elements per sublist, provided that the sublists are all the same length.

r:=RandomInteger[{-10^5,10^5}];
list3=Table[{r,r,r},{j,10^2}];             (* 3 numbers in each sublist *)
list9=Table[{r,r,r,r,r,r,r,r,r},{j,10^2}]; (* 9 numbers *)

maxBy[list3, 2]     (* Find max in position 2 of list3 *)
(* {{-93332, 99582, 4324}}  *)

maxBy[list9, 5]     (* Find max in position 5 of list9 *)
(* {{7680, 85508, 51915, -58282, 94679, 50968, -12664, 75246, -82903}} *)

Of course, the results will vary according to the random numbers you have generated.

Edit

Here's some timing data for large lists. SortBy is clearly slower. but doesn't seem as influenced by the number of elements in each sublist. First, my maxBy code followed by SortBy:

What's the best way to select the maximum one from a list of lists by their last element?

Using the same list2, here's some timing data for Yoda's code. Although his routine is also called maxBy, it is his that produced the output that follows:

What's the best way to select the maximum one from a list of lists by their last element?

Again, with the same list2, some data for Belisarius' code:

What's the best way to select the maximum one from a list of lists by their last element?

His second suggestion is the fastest of all tested.


After reading some documentations and doing a few experiments, I managed to get a clearer view of this problem.

I actually was wondering why Max[] seemed intentionally avoid providing directives that make it return not only the max element itself, but also its position. After all, providing position doesn't change the O(n) complexity of the algorithm. For example, imagine:

In[1]:= Max[{991, 993, 992}, ReturnPosition -> True]

Out[1]= {2}

If that could be done, you can simply use the code below to solve my problem:

list[[Max[list[[All, -1]], ReturnPosition -> True]]]

But now I realize that the system function Max[] is not designed for finding the max element in lists. You can tell that the Wolfram team obviously made Max[] more like a traditional max function in mathematics ― it does simple symbolic simplifications, it automatically flatten out all lists, it can be in a plotable function, and most importantly, it's Orderless:

In[2]:= Attributes[Max]

Out[2]= {Flat, NumericFunction, OneIdentity, Orderless, Protected}

Which makes positions meaningless. In a word, it treats all the lists inside as mathematical sets.

So philosophically it's not trivial for Mathematica to compute this. All I need to do is to "DIY" a function with the O(n) complexity and can do the job. I think TomD is heading the right direction, although I prefer:

maxLast[l_] := Cases[l, {___, Max[Last/@l]}]

And Heike (黑客?) adopted Pick which may have better techniques especially designed for selecting elements, but there must be no virtual difference in the complexity of the algorithm. And I may rewrite it this way: (fewer names and heads, faster the speed)

maxLast[l_] := Pick[l, #, Max[#]] &[Last /@ l]

They're both good answers.

0

精彩评论

暂无评论...
验证码 换一张
取 消