Jason Juestin
Jason Juestin

Reputation: 11

Zero in vector moving preceding value into new column

I have a vector that looks like this:

A = [1 7 3 4 0 0 0 0 0 0 1 4 5 3 2 4 0 0 0 0 0 0 0 2 4 3 10 3 5 3 2 1]

I would like the vector to look like this:

1 7 3 4

1 4 5 3 2 4

2 4 3 10 3 5 3 2 1

I am trying to delete the zeroes out of the vector and, when there is a zero, replace the preceding number after into a new column and continue through the length of the vector. I am currently using the command L = A(A~=0) to delete the zeroes, but get stuck there.

Upvotes: 1

Views: 49

Answers (3)

Luis Mendo
Luis Mendo

Reputation: 112679

Here's a way to do it without loops:

A = [1 7 3 4 0 0 0 0 0 0 1 4 5 3 2 4 0 0 0 0 0 0 0 2 4 3 10 3 5 3 2 1]; %// data
nz = logical(A); %// logical index of nonzeros of A
ind = find(conv(2*([false nz])-1,[1 -1],'valid')==2); %// find ends of runs of zeros
B = zeros(size(A)); B(ind) = 1; B = cumsum(B); %// integer label for each group
result = accumarray(B(nz).', A(nz).', [], @(x){x.'}); %'// nonzero elements by group

The result is a cell array of row vectors. In your example,

>> celldisp(result)
 result{1} =
     1     7     3     4
result{2} =
     1     4     5     3     2     4
result{3} =
     2     4     3    10     3     5     3     2     1

Upvotes: 1

Dang Khoa
Dang Khoa

Reputation: 5823

If you want the result to be a matrix.. you can't. Matrices cannot have empty elements as you show. The best you can do is a cell array, where each element of the cell corresponds to a row in the "matrix".

First, we can find all the locations where there isn't a zero:

>> nonZeroIndices = find(A)

nonZeroIndices =
 1     2     3     4    11    12    13    14    15    16    24    25    26    27    28    29    30    31    32

It should be obvious that "runs" of indices corresponds to where there are non-zeroes in A. That is to say, the difference between some index n and n-1 is 1. Let's use diff to find the delineations:

>> diff(nonZeroIndices)
ans = 1     1     1     7     1     1     1     1     1     8     1     1     1     1     1     1     1     1

The non-one values correspond to where those "breaks" in the runs occur. Let's find those indices..

>> breakIndices = find(diff(nonZeroIndices) > 1)
breakIndices =
 4    10

So A(nonZeroIndices(1:4)), A(nonZeroIndices(5:10)), and A(nonZeroIndices(11:end)) correspond to the 3 "rows" we want. Note that each of these indices corresponds to the end of the run, so I'll prepend a 0 to it to make a for loop easier to work with.

Here is my final solution:

nonZeroIndices = find(A);
breakIndices = [0 find(diff(nonZeroIndices) > 1)];
for ii = 1:numel(breakIndices)
    if ii ~= numel(breakIndices)
        c{ii,:} = A(nonZeroIndices(breakIndices(ii)+1) : nonZeroIndices(breakIndices(ii+1)));
    else
        c{ii} = A(nonZeroIndices(breakIndices(ii)+1):end);
    end
    disp(c{ii})
end

Running this, you get the required output:

 1     7     3     4
 1     4     5     3     2     4
 2     4     3    10     3     5     3     2     1

Upvotes: 1

jez
jez

Reputation: 15349

Your desired output seems to not be a vector, but rather three separate vectors. I'm going to assume you want them as separate cells (c{1}, c{2} and c{3}) of a cell array c:

A = [1 7 3 4 0 0 0 0 0 0 1 4 5 3 2 4 0 0 0 0 0 0 0 2 4 3 10 3 5 3 2 1];
z = diff([0 A 0] == 0);
firstnonzero = find(z < 0);
lastnonzero  = find(z > 0) - 1;
c = {};
for i = 1:numel(firstnonzero)
   c{i} = A(firstnonzero(i):lastnonzero(i));
end
disp(c)

Upvotes: 0

Related Questions