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?
source to share
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.
source to share