22

For Example, list=RandomInteger[{1,100},2000]. Yes,I know Position[list,Max[list]] can do. But it's based on pattern matching! Ordering[list,-1] could find one position but not all. So how to find all the positions of max value of list in a more efficient way?Thank you.

WateSoyan
  • 1,854
  • 13
  • 19
  • list = RandomInteger[{1, 100}, 10^7]; Position[list, Max[list]] // AbsoluteTiming // First only takes 0.8220470second – xyz May 16 '15 at 14:43
  • @Shutao Tang Similar topic to find one position of max value of a matrix was discussed in Baidu Tieba. http://tieba.baidu.com/p/3717089683 (I notice that you are a Chinese student.) But I am not satisfied by the code posted by xzcyr which uses Ordering and only finds one position,so I come to here to ask experts. – WateSoyan May 16 '15 at 15:38

9 Answers9

20

A fast uncompiled alternative without pattern matching is to use the NonzeroPositions property of SparseArray, as long as you're dealing with numerical data.

list = RandomInteger[{1, 100}, 10^7];

SparseArray[Unitize[list - Max[list]] - 1]["NonzeroPositions"]; // RepeatedTiming
(* 0.0855 *)

Position[list, Max[list]] // RepeatedTiming
(* 0.509 *)

compPos[list, Max[list]] // RepeatedTiming (* Marius' solution *)
(* 0.0366 *)

SparseArray[Unitize[list - Max[list]], Automatic, 1][
   "NonzeroPositions"]; // RepeatedTiming (* MichaelE2's solutions *)
(* 0.0663 *)
C. E.
  • 70,533
  • 6
  • 140
  • 264
19

For a 1D list you can also use

Pick[Range@Length@list, list, Max@list]
Simon Woods
  • 84,945
  • 8
  • 175
  • 324
  • +1 For me this is the fastest here without requiring a compile, although probably takes more memory because of the range. Weird though, because it's still doing pattern matching yet much faster than Position – Histograms May 16 '15 at 19:23
  • 1
    @Histograms, Pick has been shown to often be very fast, e.g. here. The only thing beating it in that case is compiled Select, which is basically what I implemented in my answer. – Marius Ladegård Meyer May 16 '15 at 19:42
  • 1
    @Histograms Since version 8 Pick is optimized for packed arrays, bypassing pattern matching in a case like this. See: (11) – Mr.Wizard May 17 '15 at 05:07
13

For a one-dimensional list:

compPos = 
 Compile[{{list, _Integer, 1}, {max, _Integer}},
  Block[{copy = list, i = 1},
   Do[
    If[
     list[[j]] == max, copy[[i++]] = j],
    {j, Length[list]}];
   copy[[1 ;; i - 1]]
  ], 
   CompilationTarget -> "C"
  ];

Though I think Position is a good non-compiled alternative in this case, since the "pattern" you use is a number. There won't be any useless matches to this pattern.

Performance Test

list = RandomInteger[{1, 100}, 10^7];
Position[list, Max[list]] // AbsoluteTiming // First
0.8220470
compPos[list, Max[list]] // AbsoluteTiming // First
0.0830048

Obviously, 10 times speed-up

Marius Ladegård Meyer
  • 6,805
  • 1
  • 17
  • 26
11

It is possible to Compile Position itself for machine types (e.g. Integer or Real):

posmax = Compile[{{list, _Integer, 1}}, Position[list, Max@list] ];

Performance:

x = RandomInteger[{1, 100}, 10^7];

Position[x, Max@x] // Timing // First

posmax[x] // Timing // First
0.44754

0.0736

With a C compiler this should be faster still; I'll find out in a few minutes if the Microsoft compiler installs correctly.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • I have read every Q&A I can find about installing a C compiler but I still get "Compile::nogen: A library could not be generated from the compiled function." -- I am using Mathematica 10.1 under Windows 7 x64. If anyone can help me with this problem please comment here. – Mr.Wizard May 17 '15 at 06:24
  • I tried to get a Borland C++ compiler to work, with no luck as well. I wanted to mention something I found useful, in case you haven't come across it. I have been playing with the CreateExecutable function from the "CCompilerDriver`GenericCCompiler` package. Two options have been instructive: "ShellOutputFunction" -> Print and "CleanIntermediate" -> False. The shell output function allowed me to see what the compiler was complaining about (I discovered that the command line generated for the compiler was wrong); leaving the intermediate files behind also helped. No joy yet though... – MarcoB May 17 '15 at 07:32
  • @MarcoB Thanks for the ideas. ShellOutputFunction helps, showing that there seem to be missing .bat files in the installation for the x64 compilers, despite the x64 compilers themselves being present. I'm not sure what to make of that. If I'm going to have to debug an installation I think I'd rather go with GCC; I installed the Microsoft SDK because was supposed to be the most pain-free solution. – Mr.Wizard May 17 '15 at 09:26
  • Installing VisualStudio2013 may help...I seldom use C complier-though I know that compilefunction will run faster. – WateSoyan May 17 '15 at 14:59
  • Oddly, I found no improvement compiling to C. I suppose that means that the compiled versions of Max and Position are already optimal. (Mac OS) +1 of course. – Michael E2 May 18 '15 at 01:51
  • Which SDK version did you download? – Oleksandr R. May 18 '15 at 20:38
  • @Oleksandr "Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO)" Version 7.1 5/19/2010, x64 ISO: GRMSDKX_EN_DVD.iso -- apparently as installed I am missing vcvars64.bat among others. – Mr.Wizard May 19 '15 at 02:20
  • Hmm. Did you have .Net framework 4 installed before installing the compilers? This issue arose in (5487). See also some notes I wrote for myself on the subject. – Oleksandr R. May 19 '15 at 09:22
  • @OleksandrR. I had/have "Microsoft .NET Framework 4.5.2" installed according to the Add/Remove Programs dialog. Thanks, I'll take a look at the notes when I have time. – Mr.Wizard May 19 '15 at 09:24
  • Please note that the versions of .Net framework do not supersede each other! On my computer I have versions 2.0 SP2, 3.0 SP2, 3.5 SP1, and 4 installed (so far I found no reason to install 4.5). Version 4.5 is not sufficient as a replacement for version 4.0. – Oleksandr R. May 19 '15 at 09:38
  • @OleksandrR. I did not realize this. I suppose that is the explanation. Nevertheless I am a bit soured to a nearly 1GB compiler that needs yet more libraries. Have you every tried to configure GCC for Mathematica under Windows? – Mr.Wizard May 19 '15 at 09:45
  • Yes, I use MinGW-w64 myself. I am not sure how it is in version 10, but in versions 8 and 9 you have to modify the `CCompilerDriver`` package and rebuild the link libraries to make it work. It is not straightforward. But I will help you to do it if you like. – Oleksandr R. May 19 '15 at 09:56
  • @Oleksandr I certainly would like your help but I don't have the time or focus to do it tonight. If you feel like writing up instructions go ahead, otherwise contact me on another day when you have some spare time. Thanks! – Mr.Wizard May 19 '15 at 09:57
  • I wrote some instructions on MathGroup but was purposely vague as modifying Mathematica strictly contravenes the licence. If you want more detail, let me know and we will discuss it somewhere convenient. – Oleksandr R. May 19 '15 at 10:17
9

My proposal:

Nearest[list -> Automatic, Max[list], {All, 0.5}]

Among non-C solutions, it's slightly faster than Pickett's, but slower than Simon Woods's.

list = RandomInteger[{1, 100}, 10^7];

Needs["GeneralUtilities`"];

Nearest[list -> Automatic, Max[list], {All, 0.5}] // AccurateTiming
SparseArray[Unitize[list - Max[list]] - 1]["NonzeroPositions"] // AccurateTiming (* P. *)
Pick[Range@Length@list, list, Max@list] // AccurateTiming                       (* S.W. *)
(*
  0.0925321
  0.121649
  0.0403738
*)

I have to say I was surprised, because I'd become accustomed to the superiority of SparseArray. But Nearest has been improved in V10. For instance, it takes 2.5 sec on my machine in V9.0.1!! Wow.

Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • Nearest[list -> Automatic, Max[list], {All, 0.5}] gives the warning information in V8 – xyz May 17 '15 at 00:43
  • 2
    @ShutaoTang I don't see why one would use this pre-V10, though. Try replacing All by Length@list. (I don't have V8 to debug.) – Michael E2 May 17 '15 at 01:28
  • This time it works well in V8, thks a lot:-) – xyz May 17 '15 at 01:41
  • @ShutaoTang You're welcome. I assume it's slow, like in V9? – Michael E2 May 17 '15 at 01:42
  • 1
    In V8, @Michael E2, Nearest[list -> Automatic, Max[list], {Length@list, 0.5}]; // AbsoluteTiming takes 4.1367188 sec. Now, I cannot test it in V9 owing to that my OS didn't install V9. – xyz May 17 '15 at 01:47
7

If you don't mind using an undocumented internal function, you could try using Random`Private`PositionsOf:

list = RandomInteger[100, 10^7];

r1 = Random`Private`PositionsOf[list, Max[list]]; //RepeatedTiming
r2 = Pick[Range@Length@list, list, Max@list]; //RepeatedTiming

r1 === r2

{0.011, Null}

{0.037, Null}

True

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
4

I think this works Ordering[dat, -Count[dat, Max[dat]]] but it is actually slower than Position[dat,Max[dat]]

This also works, but again, it's still slower

pos1[list_, max_] := Block[{i = 1, l = Length[list]},
  Last[Reap[While[i <= l,
  If[list[[i]] == max, CompoundExpression[Sow[i], i++], i++]]]]]

... unfortunately so is this more compact solution

pos2[list_, max_] := MapIndexed[If[#==max,Sow@#2,##&[]]&,list]

You could try compiling them; I can't use Compile with CompilationTarget->"C" on my system for various reasons. I'd just stick with using Position.

Histograms
  • 2,276
  • 12
  • 21
1

Since V 13.2 we have PositionLargest and PositionSmallest

list = RandomInteger[{1, 100}, 2000];

PositionLargest[list]

{55, 99, 248, 614, 654, 749, 894, 967, 1029, 1144, 1209, 1392, 1866, 1893, 1932}

It is comparatively fast:

list = RandomInteger[{1, 100}, 10^7];

PositionLargest[list]; // Timing // First

0.173024

Position[list, Max @ list] // Timing // First

0.346482

eldo
  • 67,911
  • 5
  • 60
  • 168
1

Using PositionIndex: (introduced 9th July 2014)

SeedRandom[1];
list = RandomInteger[{1, 10}, 10^7];
PositionIndex[list][Max[list]]; // RepeatedTiming

{0.155408, Null}

Syed
  • 52,495
  • 4
  • 30
  • 85