Removing data from a structure that is less than a certain threshold

10 views (last 30 days)
I am running a code to find the positions and trajectories of microparticles. I get a lot of noise as my particle concentration is quite dense. The false positives slow down my code significantly so I want to get rid of them every loop so I have made the following code:
c=1;
while c <= length(AllTraj.Passive)
if ((length(AllTraj.Passive(c).T) <= 10) && ...
(max(AllTraj.Passive(c).T) < (i-2)))
AllTraj.Passive(c) = [];
else
c=c+1;
end
c
end
AllTraj.Passive represents a struct with my connected particle trajectories each with time (T), X and Y values.
I want to get rid of any particles (rows) which have a trajectory of length 10 or less if they are no longer being tracked in the current frame (i-2 is current frame).
This code seems to run very slowly when I have a lot of particles and when I clear the rows which meet the criteria.
Is there a faster way to remove data that is less than a threshold time?
Thanks

Accepted Answer

Jan
Jan on 16 Jul 2019
Edited: Jan on 16 Jul 2019
The iterative growing and shrinking of arrays is extremely expensive. Better:
nTraj = numel(AllTraj.Passive);
remove = false(1, nTraj);
for c = 1:nTraj
tmp = AllTraj.Passive(c).T;
% [EDITED: remove -> remove(c)]
remove(c) = (numel(tmp) <= 10) && (max(tmp) < (i-2));
end
AllTraj.Passive(remove) = [];
Remember: To create a growing array, e.g.:
x = [];
for k = 1:1e6
x(k) = rand;
end
Matlab has to reserve sum(1:1e6)*8 bytes in the RAM and copy almost the same amount of memory. This is 4 TeraByte, although the final array has 8 MB only. Of course this is slow.
The solution is a pre-allocation:
x = zeros(1, 1e6);
Or in your case to collect the indices to remove at first and remove them in one step after the loop.
  4 Comments
Manny Kins
Manny Kins on 16 Jul 2019
Thanks so much for your help! I have made the changes and it works very well! The code has sped up quite a bit too just by getting rid of the false positives (improvement of 50%!)

Sign in to comment.

More Answers (1)

Guillaume
Guillaume on 16 Jul 2019
This code seems to run very slowly
The code you've posted either doesn' do anything because the if condition is never true, or errors (if the if condition is ever true). It can't be the code you actually use.
If your loop has a known number of iterations you should be using for not while. That way you can't mess up the iterator variable. Let's see what would happen if your if test was true:
%removed unnecessary brackets from the expression to make it easier to read
%check that the field T has less than 10 elements and that the maximum is less than something.
%i is a terrible variable name by the way.
if length(AllTraj.Passive(c).T) <= 10 && max(AllTraj.Passive(c).T) < (i-2)
The only thing you if the test is true is to replace the structure stored in Passive(c) by an empty array. The code then reaches the else. Since the if was true, the code in the else is not executed and c is not incremented. The code then reaches the end of the while loop.
Since c was not incremented, c is still the same as previous iteration. So, we know that AllTraj.Passive(c) is []. And therefore AllTraj.Passive(c).T is now an error. There is no field T, Passive(c) is not a structure anymore.
A for loop would avoid the bug:
for c = 1:numel(AllTraj.Passive)
if numel(AllTraj.Passive(c).T) <= 10 && max(AllTraj.Passive(c).T) < (i-2)
AllTraj.Passive(c) = [];
end
end
Unfortunately for you, there is no way to make this faster without completely changing the way you store your data originally.
  2 Comments
Guillaume
Guillaume on 16 Jul 2019
Oh, yes of course, I missed the forest for the trees. Removing elements of an array while you iterate an array is never a good idea. The only way to do it reliably is to either work backward (but that creates a lot of reallocation), or create a new array with the non-removed elements.
Simpler is to first have a loop that creates a logical array indicating which elements are to be removed, then removed them all afterwards, as Jan has show (once you remove the bug).
I would code it like this:
toremove = arrayfun(@(elem) numel(elem.T) < = 10 && max(elem.T) < i-2, AllTraj.Passive);
AllTraj.Passive(toremove) = [];

Sign in to comment.

Products


Release

R2019a

Community Treasure Hunt

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

Start Hunting!