1
$\begingroup$

I want to split a matrix e.g.,

mat={{a1,b1,c1,1},{a2,b2,c2,1},{a3,b3,c3,2},{a4,b4,c4,2}}

into two matrices based on the last (or any) column (in this case, the last column has value 1 or 2):

mat1={{a1,b1,c1,1},{a2,b2,c2,1}} 
mat2={{a3,b3,c3,2},{a4,b4,c4,2}}

the mat1, mat2 should be generated according to available values of the "sorting column".

Any simple/efficient solutions ?

$\endgroup$
  • $\begingroup$ Thanks, it works. $\endgroup$ – Leo1215 Aug 6 '17 at 9:52
1
$\begingroup$

I would prefer GroupBy over GatherBy. GatherBy uses the ordering of occurrence of the keys in the matrix m which need not coincide with the natural order of the keys. GroupBy uses the same ordering as GatherBy but also utilizes Association to store the keys which makes it easier to access data. Here is an example for keys in the second column where the natural ordering gets mixed up.

SeedRandom[0];
m = RandomInteger[{1, 3}, {5, 5}];
a = GatherBy[m, #[[2]] &]
b = GroupBy[m, #[[2]] &]
a == Values[b]

(* {{{3, 3, 2, 1, 1}, {3, 3, 1, 3, 1}}, {{3, 1, 1, 3, 1}, {1, 1, 2, 3, 2}}, {{2, 2, 2, 3, 1}}} *)
(* <|3 -> {{3, 3, 2, 1, 1}, {3, 3, 1, 3, 1}}, 1 -> {{3, 1, 1, 3, 1}, {1, 1, 2, 3, 2}}, 2 -> {{2, 2, 2, 3, 1}}|> *)
(* True *)
$\endgroup$
4
$\begingroup$

Note, from the documentation, that SplitBy

splits list into sublists consisting of runs of successive elements that give the same value when f is applied.

So while SplitBy will work in the case you give, it may not be very robust if you can't always guarantee that your original matrix will contain "runs of successive elements". With that in mind, GatherBy might be the way to go. For mat as in the example you give, SplitBy and GatherBy give the same answer:

SplitBy[mat, Last] === GatherBy[mat, Last]

(* True *)

But suppose the rows of mat are mixed (say, switch rows 2 and 3) up so that the last entries are not sequential:

mixmat = {{a1, b1, c1, 1}, {a3, b3, c3, 2}, {a2, b2, c2, 1}, {a4, b4, c4, 2}};

Then SplitBy gives four sublists, each containing a single row, but GatherBy still gets the right result.

SplitBy[mixmat , Last]
GatherBy[mixmat , Last]

(* {{{a1, b1, c1, 1}}, {{a3, b3, c3, 2}}, {{a2, b2, c2, 1}}, {{a4, b4, c4, 2}}}

(* {{{a1, b1, c1, 1}, {a2, b2, c2, 1}}, {{a3, b3, c3, 2}, {a4, b4, c4, 2}}} *)

To form matrices based on the value of other rows, you can do, for example:

SeedRandom[0]; m = RandomInteger[{1, 3}, {4, 4}];
GatherBy[m, #[[2]] &]

(* {{{3, 3, 2, 1}, {1, 3, 1, 1}, {2, 3, 1, 1}}, {{3, 1, 2, 2}}} *)

which generates matrices based on the second entry in each row.

$\endgroup$
  • $\begingroup$ thanks point out the successive element. $\endgroup$ – partida Dec 10 '18 at 5:19

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.