1
votes

Within the context of writing a certain function, I have the following example matrix:

temp =

     1     2     0     0     1     0
     1     0     0     0     0     0
     0     1     0     0     0     1

I want to obtain an array whose each element indicates the number of the element out of all non-zero elements which starts that column. If a column is empty, the element should correspond to the next non-empty column. For the matrix temp, the result would be:

result = [1 3 5 5 5 6]

Because the first non-zero element starts the first column, the third starts the second column, the fifth starts the fifth column and the sixth starts the sixth column.

How can I do this operation for any general matrix (one which may or may not contain empty columns) in a vectorized way?

2
I would not call a column filled with zeros an 'empty' column. An empty column would be e.g. the middle column of [[1;3],[],[2;4]]Nibor
@Nibor MATLAB does not handle irregularly shaped matrices, such as having a gap in the middle. In the MATLAB universe, having a column of all zeroes is the correct terminology for empty.rayryeng
I'm assuming you're counting your non-zero elements in column-major order?excaza
Your expected results make sense assuming you are counting the index of where the non-zero values happen in column-major order. Your question does not reflect this minor but very crucial detail in order to understand the problem. Please verify and correct your question if necessary.rayryeng

2 Answers

5
votes

Code:

temp = [1 2 0 0 1 0; 1 0 0 0 0 0; 0 1 0 0 0 1]
t10  = temp~=0
l2 = cumsum(t10(end:-1:1))
temp2 = reshape(l2(end)-l2(end:-1:1)+1, size(temp))
result = temp2(1,:)

Output:

temp =
     1     2     0     0     1     0
     1     0     0     0     0     0
     0     1     0     0     0     1

t10 =
     1     1     0     0     1     0
     1     0     0     0     0     0
     0     1     0     0     0     1

l2 =
     1     1     1     1     1     2     2     2     2     2     2     2     3     3     4     4     5     6

temp2 =
     1     3     5     5     5     6
     2     4     5     5     6     6
     3     4     5     5     6     6

result =
     1     3     5     5     5     6

Printing values of each step may be clearer than my explanation. Basically we use cumsum to get the IDs of the non-zero elements. As you need to know the ID before reaching the element, a reversed cumsum will do. Then the only thing left is to reverse the ID numbers back.

2
votes

Here's another way:

temp = [1 2 0 0 1 0; 1 0 0 0 0 0; 0 1 0 0 0 1]; % data
[~, c] = find(temp); % col indices of nonzero elements
result = accumarray(c, 1:numel(c), [], @min, NaN).'; % index, among all nonzero
    % values, of the first nonzero value of each col; or NaN if none exists
result = cummin(result, 'reverse'); % fill NaN's using backwards cumulative maximum