5
$\begingroup$

I want to perform some operations on a matrix depending on some condition. The matrix is:

mat1={{0.2,a1,b1,c1},{0.8,a2,b2,c2},{0.3,a3,b3,c3}}
  • when the element in the first column is less than 0.5: nothing should happen to that specific row;
  • when the element in the first column is larger than 0.5: I want to swap the second and the third element and define the fourth as their ratio;

For the above matrix, this operation should return:

mat2={{0.2,a1,b1,c1},{0.8,b2,a2,a2/b2},{0.3,a3,b3,c3}}

I would also like to keep track of the rows where it was necessary to make the swap operation.

what is the most efficient way to perform this operation in Mathematica?

$\endgroup$
6
$\begingroup$

The following quickly does the requested transformation.

mat2 = mat1 /. {z1_, z2_, z3_, _} /; z1 > 0.5 -> {z1, z3, z2, z2/z3}
(* {{0.2, a1, b1, c1}, {0.8, b2, a2, a2/b2}, {0.3, a3, b3, c3}} *)

One way to identify which rows were transformed is

# > .5 & /@ First@Transpose[mat1]
(* {False, True, False} *)

# > .5 & /@ mat2[[All, 1]]

gives the same result, as do several other options.

Efficiency: The computations just described are very fast. Applied to a 1000000 row matrix, the transformation in the first line of code takes about 1.6 sec, while determining which rows were transformed (the second line of code) takes about 0.5 sec.

$\endgroup$
  • $\begingroup$ works like a charm! and indeed is very fast! thanks @bbgodfrey $\endgroup$ – Luigi Nov 25 '16 at 8:17
5
$\begingroup$

Update: Another, much faster, alternative:

ClearAll[f0, f1]
f1 = Module[{m2 = Transpose[#][[{1, 3, 2, 4}]], us = UnitStep[#[[All, 1]] - .5]}, 
    m2[[-1]] = Divide @@ m2[[{3, 2}]]; us Transpose[m2] + (1 - us) #] &;

f0 = # /. {z1_, z2_, z3_, _} /; z1 > 0.5 -> {z1, z3, z2, z2/z3} &;

f1@mat1

Mathematica graphics

Equal @@ Through[{f0, f1}@mat1]

True

Timings:

results = {0, 0};
m1 = RandomReal[1, {1000000, 4}];
i = 1; 
Grid[Transpose[{{"f0", "f1"}, First[AbsoluteTiming[results[[i++]]= #@m1;]]& /@{f0, f1}}], 
  Dividers -> All] 

Mathematica graphics

Original post:

If[# < .5, {##}, {#, #3, #2, #2/#3}] & @@@ mat1

Mathematica graphics

Also:

mat2 = MapAt[Append[#[[{1, 3, 2}]], Divide @@ #[[{2, 3}]]] &, mat1, 
  List /@ Position[mat1[[All, 1]] - .5 , _?Positive]]

Mathematica graphics

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