4
$\begingroup$

I have matrix in as shown, consisting of real numbers and 0. How can I sort it to become out as shown?

in ={
   {0, 0, 3.411, 0, 1.343},
   {0, 0, 4.655, 2.555, 3.676},
   {0, 3.888, 0, 3.867, 1.666}
   };
out ={
   {1.343, 3.411, 0, 0, 0},
   {2.555, 3.676, 4.655, 0, 0},
   {1.666, 3.867, 3.888, 0, 0}
   };

This is related to a question I asked. It is much easier to add the columns by sorting it this way than in previous question, and easier to visualize than trying to take the first non-zero value in a row.

$\endgroup$
  • 1
    $\begingroup$ Re: "consisting of real numbers and 0" -> "Consisting of the humanity and me" $\endgroup$ – Dr. belisarius Nov 16 '12 at 2:46
6
$\begingroup$

The simplest way would be to replace zeros with Null, map Sort onto it and then replace Null with zeros. This works because the default sorting function OrderedQ will place Null at the end, as per your needs.

mat =  {{0, 0, 3.411, 0, 1.343}, {0, 0, 4.655, 2.555, 3.676}, {0, 3.888, 0, 3.867, 1.666}};
Map[Sort, mat /. (0 | 0.) -> Null] /. Null -> 0
(*  1.343   3.411   0       0   0
    2.555   3.676   4.655   0   0
    1.666   3.867   3.888   0   0 *)
$\endgroup$
  • 1
    $\begingroup$ This is much faster and cleaner than my solution. $\endgroup$ – Andy Ross Nov 16 '12 at 2:30
  • $\begingroup$ @AndyRoss Yours can be made faster simply by making it f[0|0.] = Infinity, since the OP said that it only contains zeros. Right now, it's unnecessarily testing every element (twice) with PossibleZeroQ $\endgroup$ – rm -rf Nov 16 '12 at 2:32
  • 1
    $\begingroup$ Right. But the custom sorting function is going to be much slower regardless. $\endgroup$ – Andy Ross Nov 16 '12 at 2:33
  • 1
    $\begingroup$ Hi @rm -rf like your solution $\endgroup$ – sebastian c. Nov 16 '12 at 2:42
  • 1
    $\begingroup$ I would change "will place Null at the end" by "will place any symbol after any number" $\endgroup$ – Rojo Nov 16 '12 at 12:58
6
$\begingroup$

You can map Sort over the rows using a custom ordering function which treats 0 as infinity.

data = RandomChoice[{0, 1}, {5, 5}]*RandomReal[{1, 10}, {5, 5}];

f[0|0.]= \[Infinity];
f[x_] := x
Sort[#, f[#1] <= f[#2] &] & /@ data

(*{{6.07883, 7.33113, 0., 0., 0.}, {2.74761, 0., 0., 0., 0.},
   {6.09223, 8.11442, 0., 0., 0.}, {3.16126, 4.72089, 7.72369, 0.,0.}, 
   {9.25964, 0., 0., 0., 0.}}*)
$\endgroup$
5
$\begingroup$

Since what you are doing is basically sorting each row, but 0 is treated as highest value. One way is to replace all zeros with Infinity before sorting and changing back after

r = RandomChoice[{0, Random[]}, {3, 5}];
r // MatrixForm

(Sort[#] & /@ (r /. {(0 | 0.) -> Infinity})) /. {Infinity -> 0} // MatrixForm

Output

Edit I like Andy Ross's solution better

$\endgroup$
  • 1
    $\begingroup$ Btw, you could also do something like RandomChoice[{0, Random[]}, {3, 5}] $\endgroup$ – Rojo Nov 16 '12 at 13:30
  • $\begingroup$ Or if your list is big, a faster solution may be Times@@Through@{RandomReal, RandomChoice}[{0, 1}, {3, 5}], adding //Chop if you care about having integer 0 $\endgroup$ – Rojo Nov 16 '12 at 13:39
1
$\begingroup$
PadRight[#, Length@in[[1]], 0] & /@ Sort /@ DeleteCases[in, 0 | 0., 2]

=>

{{1.343, 3.411, 0, 0, 0}, {2.555, 3.676, 4.655, 0, 0}, {1.666, 3.867, 3.888, 0, 0}}

$\endgroup$
1
$\begingroup$
in = {{0, 0, 3.411, 0, 1.343}, {0, 0, 4.655, 2.555, 3.676}, {0, 3.888,
     0, 3.867, 1.666}};

A possible solution

Sort /@ (in I - Unitize[in]) // Im
$\endgroup$
1
$\begingroup$

You might use:

SortBy[#, # /. 0 -> {} &] & /@ in

This works because {} will be placed after atomic elements such as real numbers. If your zeros may not always be precise (head Integer) you may use:

SortBy[#, # /. x_ /; x == 0 -> {} &] & /@ in
$\endgroup$
  • $\begingroup$ That is a nice one. $\endgroup$ – user1066 Nov 18 '12 at 9:49

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.