4

I'd like to insert columns to a matrix, but the insertion column positions within the matrix differ by row. How can I do this without using for-loop?

Following is a simplified example in MATLAB; From A,X,P, I want to get APX without using for-loop.

>> A = zeros(4,5)     % inclusive matrix   

 A =  
     0    0    0    0    0  
     0    0    0    0    0  
     0    0    0    0    0  
     0    0    0    0    0  

>> X = [9,8;5,7;8,3;6,7]   % data to insert  

  X =  
   9   8  
   5   7   
   8   3  
   6   7  

>> P = [3;2;4;1]  % insertion position within the matrix  

P =  
   3  
   2  
   4  
   1  

>> APX = [0,0,9,8,0;0,5,7,0,0;0,0,0,8,3;6,7,0,0,0]   % what I want  

  APX =  
   0   0   9   8   0  
   0   5   7   0   0  
   0   0   0   8   3  
   6   7   0   0   0  
rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • 1
    What is wrong with using a for-loop? – ToBe May 10 '16 at 06:50
  • Vectorized codes are much faster than those with for-loops in Matlab especially when the matrix sizes are quite large; i.e., thousands x thousands – HyungDon Lee May 10 '16 at 07:10
  • 3
    @HyungDonLee You really should actually try using loops before trying it vectorized. MATLAB R2015b has an improved JIT engine where loops are now competitive. – rayryeng May 10 '16 at 07:12
  • 1
    First make it work, then make it work right, then worry about making it work fast. – sco1 May 10 '16 at 14:09

1 Answers1

6

It's simply determining the right column-major indices to access the matrix so you can populate it with your desired values. This first requires generating the right row and column values to access the right positions in APX so you can use X to populate those positions.

Using P, each element tells you which column you should start populating for each row of X. You will need to generate column indices in increasing order up to as many columns as there are in X. To generate the row indices, simply create a matrix that is the same size as X where each column spans from 0 up to as many rows as there are in X minus 1 (i.e. 0:size(X,2)-1). This matrix gives you the correct offsets so that you can take P and add it with this matrix. Once you do that you will have a column index matrix that tells you specifically where each element should go with regards to the columns of the output matrix per row of P. Finally, use sub2ind to generate the column-major indices using the rows and columns generated above to place X in APX.

In other words:

P = [3;2;4;1];
X = [9,8;5,7;8,3;6,7];

rowInd = repmat((1:size(X,1)).', 1, size(X,2)); %'
colInd = bsxfun(@plus, P, 0:size(X,2)-1);
APX = zeros(size(X,1), max(colInd(:)));
APX(sub2ind(size(APX), rowInd, colInd)) = X;

To generate the row locations, we use repmat to create a matrix that is the same size as X where each column spans from 1 up to as many rows as X. To generate the column locations, we use bsxfun to create a matrix where each column is the vector P but increasing by 1 per column. We then create APX to be of compatible size then use sub2ind to finally populate the matrix.

With your above test inputs, we get:

APX =

     0     0     9     8     0
     0     5     7     0     0
     0     0     0     8     3
     6     7     0     0     0

Minor Note

You really should actually try using loops before trying it vectorized. Though using loops was slow in previous versions of MATLAB, MATLAB R2015b has an improved JIT engine where loops are now competitive. You should time your code using loops and ensuring that it is justifiable before switching to vectorized implementations.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Is `repmat` better than `meshgrid` in this case or `bsxfun(@plus, zeros(1,size(X,2))',1:size(X,1)).'`? – Stewie Griffin May 10 '16 at 07:52
  • @StewieGriffin I didn't consider `meshgrid` because it outputs two variables... I consider it wasteful. I figured `repmat` would be more conservative with resources. – rayryeng May 10 '16 at 07:53
  • @StewieGriffin Yes, I always prefer `bsxfun` over `repmat` whenever possible. I tried to use what I thought was best here :) – rayryeng May 10 '16 at 07:59