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$
0

6 Answers 6

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$
2
  • $\begingroup$ @acl Thanks, your method is also reasonable, +1. $\endgroup$
    – Artes
    Dec 21, 2012 at 0:36
  • $\begingroup$ I came up with 1 - Unitize[Min@# - # & /@ a] myself. Yours is cleaner. +1 $\endgroup$
    – Mr.Wizard
    Dec 21, 2012 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$
4
  • 2
    $\begingroup$ Maybe better Thread[# == Min[#]] & /@ list // Boole instead of setting Listable attribute? $\endgroup$
    – swish
    Dec 20, 2012 at 23:55
  • $\begingroup$ @swish nice tip! Updated $\endgroup$
    – Murta
    Dec 20, 2012 at 23:57
  • $\begingroup$ This is quite neat ;) +1 $\endgroup$ Dec 20, 2012 at 23:59
  • 1
    $\begingroup$ nice (and first :) ) $\endgroup$
    – acl
    Dec 21, 2012 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$
6
  • $\begingroup$ Comparative analysis became your speciality +1. $\endgroup$
    – Artes
    Dec 21, 2012 at 2:27
  • $\begingroup$ I decided not to look and ended up with the same answer as you, +1 $\endgroup$
    – Rojo
    Dec 21, 2012 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, 2012 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, 2012 at 12:48
  • $\begingroup$ @rnoodle You can always change your Accept. $\endgroup$
    – Mr.Wizard
    Dec 21, 2012 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 and acknowledge you have read our privacy policy.

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