dleal
dleal

Reputation: 2314

matlab array into "stacked" matrix

Is there a way to do the following?

I would like to turn a MATLAB array:

>> (1:10)'

ans =

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10

Into the following sequential matrix (I am not sure what is the name for this):

ans =

   NaN   NaN   NaN     1
   NaN   NaN   NaN     2
   NaN   NaN   NaN     3
     1     2     3     4
     2     3     4     5
     3     4     5     6
     4     5     6     7
     5     6     7     8
     6     7     8     9
     7     8     9    10

I am able to do this, with the following function, but it iterates over each row and it is slow:

function V = vec2stack(X, num_sequences)
    
n = numel(X);

len_out = n - num_sequences + 1;

V = zeros(len_out,num_sequences);

for kk = 1:len_out
    V(kk,:) = X(kk:kk +num_sequences - 1)';
end
V = [nan(num_sequences,num_sequences); V(1:end-1,:)];
end

X = 1:10;
AA = vec2stack(X,3)

Running the above function for a vector of length 1,000,000 takes about 1 second, which is not fast enough for my purposes,

tic;
Lag = vec2stack(1:1000000,5);
toc;

Elapsed time is 1.217854 seconds.

Upvotes: 2

Views: 154

Answers (1)

pho
pho

Reputation: 25490

You can use repmat() to repeat the X vector horizontally. Then, notice that each column is one more than the previous. You can add a row-vector that has the same number of columns as the matrix, and Matlab will broadcast the vector onto the entire matrix and do the addition for you.

In older versions of Matlab, you might need to explicitly repmat() the row vector to get the shapes to match.

function V = vec2stack(X, num_sequences)

    % Repeat X, 1 time vertically, num_seq times horizontally
    % X(:) ensures it's a column vector so we have the correct shape
    mat = repmat(X(:), 1, num_sequences);
    
    % make a row vector from 0:num_sequences-1
    vec = 0:(num_sequences-1);
    
    % or explicitly repmat on vec if you need to:
    % vec = repmat(0:(num_sequences-1), numel(X), 1);
    
    % Add the two. Matlab broadcasts the row vector onto the matrix
    % Because they have the same number of columns
    mat = mat + vec;
    
    % Build the return matrix
    V = [nan(num_sequences, num_sequences); mat];

end

X = (1:10)';
AA = vec2stack(X,3)

% You can easily add the last column as another column

Testing speedon Octave Online:

%% Loopy version
tic;
Lag = vec2stack(1:1000000,5);
toc;

Elapsed time is 17.4092 seconds
%% Vectorized version
tic;
Lag = vec2stack((1:1000000)',5);
toc;

Elapsed time is 0.110762 seconds.

~150x speedup. Pretty cool!

Upvotes: 2

Related Questions