Count the number of rows between each instance of a value in a matrix

Suppose the following matrix:

myMatrix = [
1   0   1
1   0   0
1   1   1
1   1   1
0   1   1
0   0   0
0   0   0
0   1   0
1   0   0
0   0   0
0   0   0
0   0   1
0   0   1
0   0   1    
];

      

With the above in mind (and handling each column myself), I am trying to create a matrix that will hold the number of rows since the last value 1

is "shown". For example, in the first column, the first four values ​​will become 0

, since 1

there are 0 rows between each of those rows and the previous value .

Line 5 becomes 1

, line 6 = 2

, line 7 = 3

, line 8 = 4

. Since line 9 contains a 1

, it will become 0

, and the counter will start again at line 10. The final matrix should look like this:

FinalMatrix = [
0   1   0
0   2   1
0   0   0
0   0   0
1   0   0
2   1   1
3   2   2
4   0   3
0   1   4
1   2   5
2   3   6
3   4   0
4   5   0
5   6   0     
];

      

What's a good way to do something like this?

EDIT: I am currently using the following code:

[numRow,numCol] = size(myMatrix);
oneColumn = 1:numRow;
FinalMatrix = repmat(oneColumn',1,numCol);
toSubtract = zeros(numRow,numCol);
for m=1:numCol
    rowsWithOnes = find(myMatrix(:,m));
    for mm=1:length(rowsWithOnes);
        toSubtract(rowsWithOnes(mm):end,m) = rowsWithOnes(mm);
    end
end
FinalMatrix = FinalMatrix - toSubtract;

      

which is about 5x faster than a solution bsxfun

hosted across multiple samples and datasets (about 1500 x 2500 in size). Can the above code be optimized?

+3


source to share


2 answers


find + diff + cumsum

-

offset_array = zeros(size(myMatrix));
for k1 = 1:size(myMatrix,2)
    a = myMatrix(:,k1);
    widths = diff(find(diff([1 ; a])~=0));
    idx = find(diff(a)==1)+1;
    offset_array(idx(idx<=numel(a)),k1) = widths(1:2:end);
end
FinalMatrix1 = cumsum(double(myMatrix==0) - offset_array);

      


Benchmarking



Here's some comparative code to compare the above approach with the one in the question.

clear all
myMatrix = round(rand(1500,2500)); %// create random input array
for k = 1:50000
    tic(); elapsed = toc(); %// Warm up tic/toc
end

disp('------------- With FIND+DIFF+CUMSUM based approach') %//'#
tic
offset_array = zeros(size(myMatrix));
for k1 = 1:size(myMatrix,2)
    a = myMatrix(:,k1);
    widths = diff(find(diff([1 ; a])~=0));
    idx = find(diff(a)==1)+1;
    offset_array(idx(idx<=numel(a)),k1) = widths(1:2:end);
end
FinalMatrix1 = cumsum(double(myMatrix==0) - offset_array);
toc
clear FinalMatrix1 offset_array idx widths a

disp('------------- With original approach') %//'#
tic
[numRow,numCol] = size(myMatrix);
oneColumn = 1:numRow;
FinalMatrix = repmat(oneColumn',1,numCol); %//'#
toSubtract = zeros(numRow,numCol);
for m=1:numCol
    rowsWithOnes = find(myMatrix(:,m));
    for mm=1:length(rowsWithOnes);
        toSubtract(rowsWithOnes(mm):end,m) = rowsWithOnes(mm);
    end
end
FinalMatrix = FinalMatrix - toSubtract;
toc

      

The results I got were

------------- With FIND+DIFF+CUMSUM based approach
Elapsed time is 0.311115 seconds.
------------- With original approach
Elapsed time is 7.587798 seconds.

      

+1


source


For one column, you can do this:

col = 1; %// desired column
vals = bsxfun(@minus, 1:size(myMatrix,1), find(myMatrix(:,col)));
vals(vals<0) = inf;
result = min(vals, [], 1).';

      



Result for the first column:

result =
     0
     0
     0
     0
     1
     2
     3
     4
     0
     1
     2
     3
     4
     5

      

+3


source







All Articles