11
$\begingroup$

I have the following set:

list = {{32/39, 1/5, 0, 0, 0}, {5/33, 3/5, 1/3, 0, 3/4}};

I need to find the position of maximum value from each subset. I tried

Position[list, Max[list]]

and it gives the position {{1,1}}. But my result should be {{1,1}, {2,5}}/

$\endgroup$
1
  • 4
    $\begingroup$ From the documentation for Ordering (=> Props and Relations), to find the position of the maximum element: Ordering[#1, -1] & /@ list -> {{1}, {5}}. See also here $\endgroup$
    – user1066
    Jan 27, 2018 at 13:42

12 Answers 12

11
$\begingroup$

Here is an answer that makes extensive use of internal functions, but is significantly faster.

maxPositions[list_?MatrixQ] := With[
    {
    mask = UnitStep @ Subtract[
        list,
        Random`Private`MapThreadMax[Transpose@list]
    ]
    },
    Mod[Random`Private`PositionsOf[Flatten @ mask, 1], Dimensions[list][[2]], 1]
]

The function does not return the indices, only the maxima. Comparison:

data = RandomReal[10, {10^5, 5}];

r1 = maxPositions[data]; //RepeatedTiming
r2 = Position[Unitize[#-Max@#]&/@data,0]; //RepeatedTiming (* kglr *)

Thread[{Range[10^5], r1}] === r2

{0.00737, Null}

{0.088, Null}

True

The other answers are much slower.

Update

It seems that Random`Private`MapThreadMax was introduced between 10.3.1 and 11.1. For versions of Mathematica that don't have that function you can use:

maxPositions[list_?MatrixQ] := With[
    {mask = UnitStep[list + Random`Private`MapThreadMin[-Transpose@list]},
    Mod[Random`Private`PositionsOf[Flatten @ mask, 1], Dimensions[list][[2]], 1]
]
$\endgroup$
5
  • $\begingroup$ thanks for introducing me to these internal functions. +1 of course :) $\endgroup$
    – ubpdqn
    Jan 27, 2018 at 2:39
  • 2
    $\begingroup$ @Carl Woll I tried to use your answer but those Private functions do not get identified, Using windows10, math v11.0 $\endgroup$ Jan 27, 2018 at 11:48
  • $\begingroup$ Thank you for the help!! $\endgroup$ Jan 30, 2018 at 14:43
  • $\begingroup$ Wow, RandomPrivatePositionsOf will be super-helpful. Thanks for introducing me to it! $\endgroup$ Nov 18, 2018 at 17:00
  • $\begingroup$ I'm surprised to see Random`Private`PositionsOf[Range[1., 3.], 2.] comes back unevaluated. $\endgroup$
    – Greg Hurst
    Apr 23, 2019 at 13:04
11
$\begingroup$
Position[Unitize[# - Max @ #]& /@ list, 0]

{{1, 1}, {2, 5}}

Also:

MapIndexed[Join[#2, Ordering[#, -1]]&, list]
Join @@@ MapIndexed[Prepend, Position[#, Max @ #] & /@ list]

{{1, 1}, {2, 5}}

$\endgroup$
1
  • $\begingroup$ Thanks, it worked!! $\endgroup$ Jan 30, 2018 at 14:42
9
$\begingroup$
MapIndexed[
 Join[#2, Position[#, Max[#]][[1]]] &
, list
]
$\endgroup$
3
  • $\begingroup$ Appreciate it!! $\endgroup$ Jan 30, 2018 at 14:43
  • $\begingroup$ @Kuba Is this solution robust for a list of floats? $\endgroup$
    – Al Guy
    Mar 29, 2020 at 21:48
  • $\begingroup$ @AlGuy I am not sure but I would say probably because the number matched by position was earlier picked by Max from that sublist, it exactly that number should be found. $\endgroup$
    – Kuba
    Mar 30, 2020 at 6:16
6
$\begingroup$
MapThread[Flatten@{#1, Position[#2, #3]} &,
 {Range@Length@list, list, Max /@ list}]
$\endgroup$
6
$\begingroup$
list = {{32/39, 1/5, 0, 0, 0}, {5/33, 3/5, 1/3, 0, 3/4}};

From the documentation for Max: "Max[{Subscript[x, 1], Subscript[x, 2],…}, {Subscript[y, 1],…},…] yields the largest element of any of the lists." Consequently,

Max[list]

(* 32/39 *)

From the documentation for Position: "Position returns a list of positions in a form suitable for use in Extract, ReplacePart, and MapAt. The form is different from the one used in Part."

pos = Position[list, Max[list]]

(* {{1, 1}} *)

Extract[list, pos]

(* {32/39} *)

If you want the Max of each of the sublists of list

Max /@ list

(* {32/39, 3/4} *)

pos2 = Position[#, Max[#]] & /@ list

(* {{{1}}, {{5}}} *)

Extract[list[[#]], pos2[[#]]] & /@ {1, 2}

(* {{32/39}, {3/4}} *)
$\endgroup$
0
5
$\begingroup$
fun = Compile[{{x, _Real, 1}}, Position[x, Max[x]][[1, 1]] , 
Parallelization -> True, RuntimeAttributes -> {Listable}, RuntimeOptions -> "Speed",  CompilationTarget -> "C"]; 

{Range@Length@Flatten[list, {1}], fun[list]} // Transpose

Compile seems very fast :slightly slower than Carl's r1 on matrix with fewer than 10^4 points, faster on matrix with over 10^5 points)

$\endgroup$
1
  • $\begingroup$ Thank you so much $\endgroup$ Jan 30, 2018 at 14:44
4
$\begingroup$

This also works but slow for large list.

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

Edit: Above code only works if elements are not repeated.

This one works for all cases and pretty fast.

Transpose@{Range@Length@list, Flatten[Position[#, Max[#]] & /@ list]}
$\endgroup$
2
  • $\begingroup$ Try list = {{6, 5}, {5, 1}} $\endgroup$
    – Kuba
    Jan 27, 2018 at 21:13
  • $\begingroup$ Thank you so much for the help $\endgroup$ Jan 30, 2018 at 14:45
4
$\begingroup$

Since Version 13.2 there is PositionLargest:

list = {{32/39, 1/5, 0, 0, 0}, {5/33, 3/5, 1/3, 0, 3/4}};

ps = PositionLargest /@ list 

{{1}, {5}}

mi = MapIndexed[{#2[[1]], #1[[1]]} &] @ ps

{{1, 1}, {2, 5}}

Extract[mi] @ list

enter image description here

$\endgroup$
2
  • $\begingroup$ Thanks for pointing to PositionLargest (+1). I simply cannot remember this one. However, for X = RandomReal[{-1, 1}, {10000000}];, a call to Ordering[X, -1] takes just 0.0455106 seconds on my machine while PositionLargest[X] requires 0.385012 seconds. More than 8 times as long! (And more than 60 times as long as a similar C++ function would take...) =/ $\endgroup$ Jan 7 at 14:08
  • 1
    $\begingroup$ Thanks, Henrik, I wasn't aware that PositionLargest is such a poor performer. Many of the newer functions (all of the Sequence-functions, DeleteElements etc.) are pretty slow. I hope, that WR will accelerate them in future releases. $\endgroup$
    – eldo
    Jan 7 at 17:05
4
$\begingroup$

Another method using MaximalBy:

Position[list, #][[1]] & @@@ (list // Map[x |-> MaximalBy[x, Position]])

(*{{1, 1}, {2, 5}}*)

Or using SortBy:

Position[list, #][[1]] & /@ Last /@ (SortBy[#, Position] & /@ list)

(*{{1, 1}, {2, 5}}*)

Or using GroupBy:

Values@((x |-> Position[list, x][[1]]) /@ AssociationMap[Reverse, GroupBy[list, x |-> Max[x]]])

(*{{1, 1}, {2, 5}}*)

Another possibility is to use GroupBy, Position and KeyMap as follows:

Keys@KeyMap[Position[list, #][[1]] &, GroupBy[list, Max]]

(*{{1, 1}, {2, 5}}*)
$\endgroup$
3
$\begingroup$

We could also use TakeLargest

list = {{32/39, 1/5, 0, 0, 0}, {5/33, 3/5, 1/3, 0, 3/4}};

max = Splice @ TakeLargest[# -> All, 1] & /@ list

enter image description here

Dataset @ max

enter image description here

Values @ max

{{32/39, 1}, {3/4, 5}}

To only get the positions:

Lookup[max, "Index"]

{1, 5}

Splice @ TakeLargest[# -> "Index", 1] & /@ list

{1, 5}

Positions of the two largest elements:

TakeLargest[# -> "Index", 2] & /@ list

{{1, 2}, {5, 2}}

$\endgroup$
2
$\begingroup$

Reinventing the bicycle here, but could be useful to someone.

list = {{32/39, 1/5, 0, 0, 0}, {5/33, 3/5, 1/3, 0, 3/4}};

Table[Flatten[{i, Position[aux = list[[i]], Max[aux]]}], {i, 1, 
Length[list]}]

{{1, 1}, {2, 5}}

$\endgroup$
2
$\begingroup$

Using Threshold:

list = {{32/39, 1/5, 0, 0, 0}, {5/33, 3/5, 1/3, 0, 3/4}};

pos = list // Map[Threshold[#, {"LargestValues", 1}] &] // 
   Position[#, Except[0], {2}, Heads -> False] & // 
  GatherBy[#, First] &

{{{1, 1}}, {{2, 5}}}

Extract[list, #] & /@ pos

{{32/39}, {3/4}}

$\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.