2

I have a 2-column matrix that describes a piece of data, and the number of times that data occurred within a set:

A = [1    6                              
     2    2
     3    8                                                                       
     4    1 
     5    3];

Given that, is there an "elegant" way to produce the underlying dataset? i.e.,

B = [1 1 1 1 1 1 2 2 3 3 3 3 3 3 3 3 4 5 5 5];                                       

There are plenty of ways to go from B to A (tabulate, using unique and histc, etc) but I couldn't find any way to go from A to B. The best I could do is not elegant:

B = [];
for ii = 1:size(A,1)
    B = [B repmat(A(ii,1), 1, A(ii,2))];
end

I have a sneaking suspicion the "correct" way to do this is to use bsxfun or accumarray, but I am not experienced enough to understand how these really work.

Community
  • 1
  • 1
Dang Khoa
  • 5,693
  • 8
  • 51
  • 80
  • 1
    possible duplicate of [Element-wise array replication according to a count](http://stackoverflow.com/questions/2382319/element-wise-array-replication-according-to-a-count), [MATLAB Array Manipulation](http://stackoverflow.com/questions/1975772/matlab-array-manipulation) – Amro Sep 11 '13 at 02:49
  • @Dang Khoa You're looking for [_run-length decoding_](http://en.wikipedia.org/wiki/Run-length_encoding). See the duplicate Amro posted! – Eitan T Sep 11 '13 at 06:59
  • 1
    @EitanT thanks! I didn't realize this kind of scheme had a name. I voted to close, as this is a duplicate. – Dang Khoa Sep 11 '13 at 15:04

5 Answers5

2

You can use 'arrayfun' in combination with 'cell2mat':

 B = cell2mat(arrayfun(@(x,y) ones(y,1)*x, A(:,1), A(:,2), 'uniformoutput', false))'

This results in

B =

  Columns 1 through 16

     1     1     1     1     1     1     2     2     3     3     3     3     3     3     3     3

  Columns 17 through 20

     4     5     5     5
H.Muster
  • 9,297
  • 1
  • 35
  • 46
2

Here's one more option. I wouldn't call it elegant, but it's pretty efficient.

ndx = cumsum([1; A(:,2)]);
B = zeros(1, ndx(end)-1);
B(ndx(1:end-1)) = 1;
B = A(cumsum(B), 1).';
shoelzer
  • 10,648
  • 2
  • 28
  • 49
1

Not super elegant either, but this may work:

B = {A(:,1)*ones(1,A(:,2)};
B = [B{:}];

Have no matlab to check syntax, idea is to remove loop from

B=[];
for ii=1:size(A,1)
   B=[B A(i,1)*ones(1,A(i,2)];
end;
Ilya Kobelevskiy
  • 5,245
  • 4
  • 24
  • 41
1

Here is a solution using bsxfun:

B = A(1+sum(bsxfun(@lt, cumsum(A(:,2)), 1:sum(A(:,2)))), 1).';
Mohsen Nosratinia
  • 9,844
  • 1
  • 27
  • 52
0

This is similar to H.Muster post

using repmat

B=cell2mat(arrayfun(@(x,y)(repmat(x,y,1) ), A(:,1), A(:,2), ...
                                   'UniformOutput', false));

B' is expected output.

P0W
  • 46,614
  • 9
  • 72
  • 119