7
$\begingroup$

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.

$\endgroup$
7
$\begingroup$

Another way :

f = UnitStep[ Min @ # - #]&;
f /@ {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}}
{{0, 1}, {1, 0}, {0, 1}}
$\endgroup$
  • $\begingroup$ @acl Thanks, your method is also reasonable, +1. $\endgroup$ – Artes Dec 21 '12 at 0:36
  • $\begingroup$ I came up with 1 - Unitize[Min@# - # & /@ a] myself. Yours is cleaner. +1 $\endgroup$ – Mr.Wizard Dec 21 '12 at 1:16
5
$\begingroup$

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}}*)
$\endgroup$
  • 2
    $\begingroup$ Maybe better Thread[# == Min[#]] & /@ list // Boole instead of setting Listable attribute? $\endgroup$ – swish Dec 20 '12 at 23:55
  • $\begingroup$ @swish nice tip! Updated $\endgroup$ – Murta Dec 20 '12 at 23:57
  • $\begingroup$ This is quite neat ;) +1 $\endgroup$ – Vitaliy Kaurov Dec 20 '12 at 23:59
  • 1
    $\begingroup$ nice (and first :) ) $\endgroup$ – acl Dec 21 '12 at 0:28
5
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ Comparative analysis became your speciality +1. $\endgroup$ – Artes Dec 21 '12 at 2:27
  • $\begingroup$ I decided not to look and ended up with the same answer as you, +1 $\endgroup$ – Rojo Dec 21 '12 at 2:47
  • $\begingroup$ 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!) $\endgroup$ – rnoodle Dec 21 '12 at 12:12
  • 1
    $\begingroup$ 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}. $\endgroup$ – Artes Dec 21 '12 at 12:48
  • $\begingroup$ @rnoodle You can always change your Accept. $\endgroup$ – Mr.Wizard Dec 21 '12 at 13:28
3
$\begingroup$

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.

$\endgroup$
3
$\begingroup$

One more:

m = {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}};
Thread[KroneckerDelta[Min@#, #]] & /@ m
(*{{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)
$\endgroup$
2
$\begingroup$
m = {{2.1, 1.01}, {0.01, 7.2}, {7.8, 2.0}};
Ordering /@ -m - 1

(* {{0, 1}, {1, 0}, {0, 1}} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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