Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.

a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]

>>a =

 1     2     3     0     0
 1     0     1     3     2
 0     1     2     5     0

However, I want to erase only the ones after the last non-zero value of each line. This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.

I want to then store the remaining values in a vector. In the case of the example this would result in the vector

b=[1 2 3 1 0 1 3 2 0 1 2 5]

The only way I figured out involves a for loop that I would like to avoid:

b=[];
for ii=1:size(a,1)
    l=max(find(a(ii,:)));
    b=[b a(ii,1:l)];
end

Is there a way to vectorize this code?

like image 708
shamalaia Avatar asked May 27 '16 13:05

shamalaia


2 Answers

There are many possible ways to do this, here is my approach:

 arotate = a' %//rotate the matrix a by 90 degrees
 b=flipud(arotate)  %//flips the matrix up and down
 c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
 arotate(c==0)=[] 

 arotate =

  1     2     3     1     0     1     3     2     0     1     2     5

=========================EDIT=====================

just realized cumsum can have direction parameter so this should do:

 arotate = a'
 b = cumsum(arotate,1,'reverse')
 arotate(b==0)=[] 

This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.

like image 116
GameOfThrows Avatar answered Nov 15 '22 14:11

GameOfThrows


Here's an approach using bsxfun's masking capability -

M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction

%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);

%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(@le,(1:M)',M+1-idx'))

Sample run (to showcase mask usage) -

>> a
a =
     1     2     3     0     0
     1     0     1     3     2
     0     1     2     5     0
>> M = size(a,2);
>> at = a.'; 
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(@le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
     1     1     1
     1     1     1
     1     1     1
     0     1     1
     0     1     0
>> at(bsxfun(@le,(1:M)',M+1-idx')).'
ans =
     1     2     3     1     0     1     3     2     0     1     2     5
like image 38
Divakar Avatar answered Nov 15 '22 15:11

Divakar