MATLAB Answers

How to find the previous position in an array where a value is met?

11 views (last 30 days)
Shuo Zhang
Shuo Zhang on 28 Aug 2019
Commented: darova on 28 Aug 2019
Hi everyone,
As it is said in the title, I'd like to find the previous position(not the last position) in an array where a value is met. I have a program that works but I'm looking for improvement or optimazations of my program.
To be specific, let's suppose I have an array A=[1 0 1 0 0 0 0 1 1 1 0 0 0 0 1] and I'd like to fill in values in another array B based on values in A.
The rule is, if A(i)=1, then B(i)=1;
if A(i)=0, find the index of the previous element in A which is 1, and compute the difference between that index and current index number. If the difference is larger than 1 and no larger than 3, B(i)=2; otherwise B(i)=0;
For example, when i=5, the index of previous element in A which is 1 is 3, then 5-3=2<3, so B(5)=2; when i=7, the index of previouse element in A which is 1 is 3, then 7-3=4>3, so B(7)=0;
So in the end, I hope to have an array B=[1 0 1 0 2 2 0 1 1 1 0 2 2 0 1];
Here is what my current program looks like
A=[1 0 1 0 0 0 0 1 1 1 0 0 0 0 1];
B=NaN(length(A),1);
for i=1:length(A)
if A(i)==1
B(i)=1;
else
if i-find(B==1,1,'last')>1 && i-find(B==1,1,'last')<=3
B(i)=2;
else
B(i)=0;
end
end
end
The program works well. However the thing is, in future calculations, the length of array A could be as large as 1.5million and there will be some other calculations with indefinite amount of elements in the inner loop. So it's really time-comsuming to execute this find command in each iteration(last time I tried, it took 8hrs to compute for 500000 data points).
I tried to break the program into two one-layer-if-statements by allocating zero value to all data that satisfies the conditon in the inner loop and start another loop through them to find for which element I should specify 2 instead of 0. However, the problem is, the command find(X,n,direction) could only find the last position of an array where a value is met. So it always returns number 15 as it is the last position where value 1 appears in array A. I'm wondering, is there any other command that finds the index of the previous position where a value is met? For example, when i=5, the command would return number 3 while when i=11:14, it would return 10?

  1 Comment

darova
darova on 28 Aug 2019
Shouldn't B array be like that?
B=[1 0 1 0 2 3 0 1 1 1 0 2 3 0 1];
1Untitled.png

Sign in to comment.

Accepted Answer

Johannes Fischer
Johannes Fischer on 28 Aug 2019
Edited: Johannes Fischer on 28 Aug 2019
This is an ideal playgrouond to see how you can increase speed by avoiding for loops.
% My idea is to create an array, that in each field contains the value to
% the index of the previous '1' in A. To get there, I first get the indices
% where A is 1:
A=[1 0 1 0 0 0 0 1 1 1 0 0 0 0 1];
I = 1:numel(A);
AA = I;
AA(A==0)=0;
% AA = [1 0 3 0 0 0 0 8 9 10 0 0 0 0 15];
% Now the idea is to fill the zeros in AA with the value that preceeds
% contiguous zeros to get to Last = [1 1 3 3 3 3 3 8 9 10 10 10 10 10 15];
% Im not sure this is the most efficient or easiest to understand way but
% here is the idea: use cumsum() to propagate the information of the
% previous index along the array. For that to work the array must be
% prepared such that at index 8 there is a 5 that will add up to 8 = 5+3.
% The 5 in this case also is the number of zeros +1 between the 3 and 8 in
% AA. Since the indices are linearly increasing, we can get it by taking
% diff() where AA is not equal to 0.
d = [1 diff(AA(AA~=0))];
% you did not specify what to do if A(1) is 0. My approach only works when
% A(1) is 1
% now place the values in d at the position where A is equal to 1
a = A;
a(A~=0) = d;
% and calculate the cumulative sum
Last = cumsum(a);
% To get the distance to the last '1' in A its merely
diffToLast = I-Last;
% Followed by
B = zeros(1, numel(A));
B(A==1) = 1;
B(diffToLast == 2 | diffToLast == 3) = 2;
edit: restructured added more commentary to code.

  3 Comments

darova
darova on 28 Aug 2019
Something is wrong in this case. I used A = randi([0 1], 1, 10)
[A; B]'
0 0
0 2
1 2
1 2
1 2
0 2
0 0
1 2
1 2
1 2
Johannes Fischer
Johannes Fischer on 28 Aug 2019
As I said, that case wasnt specified, but if you replace with
if A(1) == 1
d = [1 diff(AA(AA~=0))];
else
d = [find(A==1, 1, 'first') diff(AA(AA~=0))];
end
the results for B should be identical except for the values before the second '1' in A. For the others, the result assumes that A(-1) is 1.

Sign in to comment.

More Answers (1)

darova
darova on 28 Aug 2019
Edited: darova on 28 Aug 2019
Probably Johannes's version could be faster but mine is simpler to understand
clc,clear
A = randi([0 1],10,1);
% divide array into sections
ind = find(abs(diff(A))>0);
% ignore first zeros
if A(1) == 0
ind(1) = [];
end
% if last element is zero - add index
if A(end) == 0
ind(end+1) = length(A);
end
B = A;
for i = 1:2:length(ind)
i1 = ind(i)+1; % first zero in current section
i2 = ind(i+1); % last zero
if i1+0 < i2 % if second zero exists
B(i1+1) = 2; % replace 2d zero with '2'
end
if i1+1 < i2 % if third zero exists
B(i1+2) = 2; % replace 3d zero with '2'
end
end
[A B]

  1 Comment

darova
darova on 28 Aug 2019
Shorter version. Same idea as Johannes: numerate zeros
clc,clear
A = randi([0 1], 1, 10);
A1 = A*0;
i1 = find(A,1,'first'); % start from first '1'
for i = i1:length(A)
if A(i) == 0
k = k + 1;
else
k = 0;
end
A1(i) = k;
end
B = A;
B(A1==2 | A1==3) = 2;
[A;B]'

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by