2
votes

I'm currently forming a matrix from a vector in MATLAB following the scheme described below:

Given is a vector x containing ones and zeros in an arbitrary order, e.g.

x = [0 1 1 0 1]; 

From this, I would like to form a matrix Y that is described as follows:

  • Y has m rows, where m is the number of ones in x (here: 3).
  • Each row of Y is filled with a one at the k-th entry, where k is the position of a one in vector x (here: k = 2,3,5)
  • For the example x from above, this would result in:

    Y = [0 1 0 0 0;
         0 0 1 0 0; 
         0 0 0 0 1]
    

    This is identical to an identity matrix, that has its (x=0)th rows eliminated.

I'm currently achieving this via the following code:

x = [0,1,1,0,1]; %example from above
m = sum(x==1);
Y = zeros(m,numel(x));
p = 1;
for n = 1:numel(x)
    if x(n) == 1 
       Y(p,n) = 1;
       p = p+1;
    end  
end

It works but I'm kind of unhappy with it as it seems rather inefficient and inelegant. Any ideas for a smoother implementation, maybe using some matrix multiplications or so are welcome.

5

5 Answers

5
votes

Here are a few one-line alternatives:

  • Using sparse:

    Y = full(sparse(1:nnz(x), find(x), 1));
    
  • Similar but with accumarray:

    Y = accumarray([(1:nnz(x)).' find(x(:))], 1);
    
  • Using eye and indexing. This assumes Y is previously undefined:

    Y(:,logical(x)) = eye(nnz(x));
    
4
votes

Use find to obtain the indices of ones in x which are also the column subscripts of ones in Y. Find the number of rows of Y by adding all the elements of the vector x. Use these to initialise Y as a zero matrix. Now find the linear indices to place 1s using sub2ind. Use these indices to change the elements of Y to 1.

cols = find(x);
noofones = sum(x);
Y = zeros(noofones, size(x,2));
Y(sub2ind(size(Y), 1:noofones, cols)) = 1;
4
votes

Here's an alternative using matrix multiplications:

x = [0,1,1,0,1];
I = eye(numel(x));

% construct identity matrix with zero rows
Y = I .* x;   % uses implicit expansion from 2016b or later
Y = Y(logical(x), :);   % take only non-zero rows of Y

Result:

Y =
   0   1   0   0   0
   0   0   1   0   0
   0   0   0   0   1

Thanks to @SardarUsama's comment for simplifying the code a bit.

1
votes

Thanks everybody for the nice alternatives! I tried out all your solutions and averaged execution times over 1e4 executions for random (1000-entry) x-vectors. Here are the results:

  1. (7.3e-4 sec) full(sparse(1:nnz(x), find(x), 1));
  2. (7.5e-4 sec) cols = find(x); noofones = sum(x); Y = zeros(noofones, size(x,2)); Y(sub2ind(size(Y), 1:noofones, cols)) = 1;
  3. (7.7e-4 sec) Y = accumarray([(1:nnz(x)).' find(x(:))], 1);
  4. (1.7e-3 sec) I = speye(numel(x)); Y = I .* x; Y = full(Y(logical(x), :));
  5. (3.1e-3 sec) Y(:,logical(x)) = eye(nnz(x));
1
votes

From your comment "This is identical to an identity matrix, that has its (x=0)th rows eliminated.", well, you can also explicitly generate it as such:

Y = eye(length(x));
Y(x==0, :) = [];

Very slow option for long x, but it works slightly faster than full(sparse(... for x with 10 elements on my computer.