Search code examples
matlabmatrix-indexing

Inserting columns to a matrix in Matlab


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  

Solution

  • 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.