# Grouping and repeating the grouped rows inside a matrix

I'm interested in finding the quickest way of grouping the rows of a large matrix in sub-groups based on the position of non-zero elements in the first column and then repeating them number of times based on the value of the non-zero element. The final output matrix is the repeated (or expanded) matrix. An example is shown as an image below: The first row, first element is always non-zero. one non-zero element in the first column can be followed by another non-zero element without being followed by zero. If a non-zero element is followed by zeros it is added to the group as shown in the figure. Length of a group is from one non-zero element until before the next non-zero element. Here is another example for more clarity.

• What if the matrix starts with a 0? Mar 16, 2020 at 14:02
• No, it will never start with zero. Mar 16, 2020 at 14:03
• Do nonzero entries appear singly in the first column? Mar 16, 2020 at 14:07
• Yes, it can. For example, first column -> {3,0,1,0,0,2,3,0,1}. Non-zero entities can be followed by other non-zero entity in this case {2,3}. Mar 16, 2020 at 15:25

m = {
{3, 1, 2},
{0, 5, 1},
{1, 4, 2},
{0, 6, 9},
{0, 4, 7},
{2, 6, 8}
};

Flatten[ConstantArray[#, #[[1, 1]]] & /@ Split[m, #2[[1]] == 0 &], 2]


$$\left( \begin{array}{ccc} 3 & 1 & 2 \\ 0 & 5 & 1 \\ 3 & 1 & 2 \\ 0 & 5 & 1 \\ 3 & 1 & 2 \\ 0 & 5 & 1 \\ 1 & 4 & 2 \\ 0 & 6 & 9 \\ 0 & 4 & 7 \\ 2 & 6 & 8 \\ 2 & 6 & 8 \\ \end{array} \right)$$

Here is the explanation for the above one-line code:

Flatten[ConstantArray[#, #[[1, 1]]] & /@ Split[m, #2[[1]] == 0 &], 2]

1. First, Split function scans through the matrix m by taking two consecutive rows (#1, #2) and checks for the condition such that the 2nd row's first element is equal to zero (#2[[1]] == 0&), if it is True then it will add it to the group, otherwise it will add a split and start a new group, thereby grouping the matrix as show in the above example figure in the question section.
2. Then, ConstantArray function is mapped (/@) onto the grouped (split) matrix such that it will replicate the grouped rows inside the matrix, "1st row's first element" (#[[1, 1]]) number of times, thereby replacing grouped rows with repeated rows.
3. Finally, Flatten function flattens out the resultant list by removing all the unwanted curly braces up to the 2nd level, thereby creating the final resultant matrix.
m = {{3, 1, 2}, {0, 5, 1}, {1, 4, 2}, {0, 6, 9}, {0, 4, 7}, {2, 6, 8}}
SequenceReplace[m, seq : {{x_ /; x > 0, __},
Repeated[{0, __}, {0, Infinity}]} :> Table[seq, x]]
Flatten[%, 2]


This could be a starter for discussions:

A = RandomInteger[{0, 3}, {6, 3}];
idx = Pick[Range[Length[A]], Unitize[A[[All, 1]]], 1];
Join @@ ConstantArray[A[[#1 ;; #2]], A[[#1, 1]]] &,
{
idx,
Append[Rest@idx - 1, Length[A]]
}
];


Hm. Building the list of rows first and to read from A only once seems to be slightly faster.

idx = Pick[Range[Length[A]], Unitize[A[[All, 1]]], 1];
B2 = A[[
ConstantArray[Range[#1, #2], #3] &,
{idx, Append[Rest@idx - 1, Length[A]], A[[idx, 1]]}
]
]];

• Thank you very much for the quick reply. It works. I checked it for my application. Mar 16, 2020 at 14:21
• Great! You're welcome. Mar 16, 2020 at 14:23
ClearAll[f]
f = Module[{m = #,  p = SequencePosition[#, {{Except[0], __}, {0, __} ...}]},
Join @@ (Join @@ ConstantArray[m[[Span @@ #]], m[[#[[1]], 1]]] & /@ p)] &;


Examples:

a = {{3, 1, 2}, {0, 5, 1}, {1, 4, 2}, {0, 6, 9}, {0, 4, 7}, {2, 6, 8}};

MatrixForm /@ {a, f @ a}


SeedRandom[1]
b = RandomInteger[3, {7, 3}];

MatrixForm /@ { b, f @ b}