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
  • 2
    $\begingroup$ Re: "consisting of real numbers and 0" -> "Consisting of the humanity and me" $\endgroup$ Nov 16, 2012 at 2:46

7 Answers 7

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$
6
  • 1
    $\begingroup$ This is much faster and cleaner than my solution. $\endgroup$
    – Andy Ross
    Nov 16, 2012 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, 2012 at 2:32
  • 1
    $\begingroup$ Right. But the custom sorting function is going to be much slower regardless. $\endgroup$
    – Andy Ross
    Nov 16, 2012 at 2:33
  • 1
    $\begingroup$ Hi @rm -rf like your solution $\endgroup$ Nov 16, 2012 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, 2012 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$
2
  • 1
    $\begingroup$ Btw, you could also do something like RandomChoice[{0, Random[]}, {3, 5}] $\endgroup$
    – Rojo
    Nov 16, 2012 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, 2012 at 13:39
2
$\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$
2
$\begingroup$

Another possibility:

MapApply[in[[##]]&]@MapIndexed[{First@#2,#1}&,(Ordering/@Replace[in, 
    0->\[Infinity],2])]

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

Original Answer

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$

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$
1
  • $\begingroup$ That is a nice one. $\endgroup$
    – user1066
    Nov 18, 2012 at 9:49
1
$\begingroup$

Define your own ordering function:

f[_, 0] := 1
f[0, _] := -1
f[x_, y_] := x <= y

Sort[#, f] & /@ in // MatrixForm

enter image description here

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