The locations of (row-wise) minimum elements in a list

I have a list representing a 2D matrix, such as this 3 ⨉ 2 one:

{{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}}


Does anyone know how to find the minimum of each row, ideally outputting a new list of 1's where the row minimums are in the original matrix and 0's for all other elements? i.e.

{{0, 1}, {1, 0}, {0, 1}}


The answer should work for any m ⨉ n list.

-

Another way :

f = UnitStep[ Min @ # - #]&;
f /@ {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}}

{{0, 1}, {1, 0}, {0, 1}}

-
+1, I like this the best – acl Dec 21 '12 at 0:28
@acl Thanks, your method is also reasonable, +1. – Artes Dec 21 '12 at 0:36
I came up with 1 - Unitize[Min@# - # & /@ a] myself. Yours is cleaner. +1 – Mr.Wizard Dec 21 '12 at 1:16

There is one way:

list={{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}}
SetAttributes[Equal,Listable]
#==Min[#]&/@list//Boole
(*{{0,1},{1,0},{0,1}}*)


Or, as suggested by @swish, without change attributes:

list={{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}}
Thread[# == Min[#]] & /@ list // Boole
(*{{0,1},{1,0},{0,1}}*)

-
Maybe better Thread[# == Min[#]] & /@ list // Boole instead of setting Listable attribute? – swish Dec 20 '12 at 23:55
@swish nice tip! Updated – Murta Dec 20 '12 at 23:57
This is quite neat ;) +1 – Vitaliy Kaurov Dec 20 '12 at 23:59
nice (and first :) ) – acl Dec 21 '12 at 0:28

Since we have a pile of methods but no timings I'll perform that test.

First my own:

mrwizard[a_] := UnitStep[Min /@ a - a]


And other functions as I'll use them:

murta[list_] := Thread[# == Min[#]] & /@ list // Boole

acl[mat_] := SparseArray[Position[#, Min[#]] -> 1, Length@#] & /@ mat // Normal

artes[a_] := UnitStep[ Min @ # - #]& /@ a

swish[a_] := Thread[KroneckerDelta[Min@#, #]] & /@ a


I leave out RiemannZeta's method as it only works on rows of length two, and therefore does not meet the question specification.

SetAttributes[timeAvg, HoldFirst]

timeAvg[func_] :=
Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}]

funcs = {murta, acl, artes, swish, mrwizard};


Many short rows:

a = RandomReal[{-9, 9}, {150000, 3}];

timeAvg[#@a] & /@ funcs

{0.452, 1.42, 0.515, 0.468, 0.04184}

SameQ @@ (#@a & /@ funcs)


True

Long rows:

a = RandomReal[{-9, 9}, {100, 30000}];

timeAvg[#@a] & /@ funcs

{0.827, 0.421, 0.02496, 1.108, 0.1216}

SameQ @@ (#@a & /@ funcs)


True

From this it appears that Artes' method is fastest on a matrix with a limited number of long rows, and my method which is based on his is fastest on a matrix with many short rows.

-
Comparative analysis became your speciality +1. – Artes Dec 21 '12 at 2:27
I decided not to look and ended up with the same answer as you, +1 – Rojo Dec 21 '12 at 2:47
Great analysis - now we have elegance and empirical evidence for the best method. (Maybe I jumped the gun when awarding the solution, but it was late and my requirements were related to a small list!) – rnoodle Dec 21 '12 at 12:12
I add my timings since they are a bit different (perhaps some random issue) for a = RandomReal[{-9, 9}, {150000, 3}]; timeAvg[#@a] & /@ funcs only your approach was better than mine : {1.201208, 3.915625, 0.102961, 1.341609, 0.0411843}, while for a = RandomReal[{-9, 9}, {100, 30000}]; timeAvg[#@a] & /@ funcs returned {2.277615, 2.496016, 0.078000, 2.839218, 0.121681}. – Artes Dec 21 '12 at 12:48
@rnoodle You can always change your Accept. – Mr.Wizard Dec 21 '12 at 13:28

If mat = {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}} then

SparseArray[Position[#, Min[#]] -> 1, Length@#] & /@ mat // Normal
(*{{0, 1}, {1, 0}, {0, 1}}*)


If the minimal element appears twice, this returns 1 in both places. Note the Normal isn't necessary, it's for presentation purposes.

-

One more:

m = {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}};
(*{{0, 1}, {1, 0}, {0, 1}}*)


Or:

Thread /@ KroneckerDelta /@ (Min /@ m - m)


We can get rid of Thread:

SetAttributes[KroneckerDelta, Listable]
KroneckerDelta[Min@#, #] & /@ m
KroneckerDelta /@ (Min /@ m - m)

-
m = {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}};
Ordering /@ -m - 1

(* {{0, 1}, {1, 0}, {0, 1}} *)

-