# How to find the nearest value of a number by comparing two nested cell arrays?

2 views (last 30 days)
maruljay on 3 Dec 2019
Edited: Adam Danz on 5 Dec 2019
I have a cell array idp{1x58} which contains the indices of local maxima of each time series. I have another cell array qq{1x58} that contains indices of local minima for every time series.
I want to compare every elemt in the array idp with qq and I want to find the closest values above and below the element in idp.
For ex: idp{1,3} = 411.
Comparing it with qq{1,3}, 376 and 497 are the closest values above and below 411.
How do I do this operation?  Adam Danz on 4 Dec 2019
Edited: Adam Danz on 4 Dec 2019
pairs is a cell array the same size as idp and qq. Each element is a 1x2 vector of the values in qq that are just below and above the values in idp.
% Inputs
idp = {381 402 411 359};
qq ={[168 262 498 637],[288,564,669],[88 257 376 497 535],[55 159 217 377 525 681]};
% closest values [below, above] idp
pairs = cellfun(@(a,b)[max(b(b<a)),min(b(b>a))],idp,qq,'UniformOutput',false);
If you'd rather return an nx2 matrix,
pairs = cell2mat(pairs');

Adam Danz on 4 Dec 2019
"1)Is there a way to get the indices of the cells in [pairs] which have only one element"
Sure, that's easy using cellfun.
If you didn't implement the [-inf,inf] suggestion, this will return a logical array identifying elements of pairs that only have 1 value.
idx = cellfun(@(x)numel(x)<2,pairs);
If you did implement the [-inf,inf] suggestion,
idx = cellfun(@(x)any(isinf(x)),pairs);
"2)Once I have the indices, ... "
You could do it that way but it doesn't sound streamlined. Do you have the idp values prior to the loop that calculates qq? If so, you could to a small test right there in that loop. It would look like this
for i = 1:length(files)
[~,PM{i}]=islocalmin(TFWS{i,:});
loc{i}=PM{:,i}>0.9;
qq{i}=find(loc{:,i});
% Test if there is a min and max in qq for the corresponding idp
if ~Your_Test_Here % FALSE means the test failed and you have to change prominence
loc{i}=PM{:,i} > 0.1;
qq{i}=find(loc{:,i});
end
end
maruljay on 5 Dec 2019
Thanks a lot. Based on your suggestions, I wrote the following code and it worked.
for i = 1:length(files)
[~,PM{i}]=islocalmin(TFWS{i,:});
loc{i}=PM{:,i}>0.9;
qq{i}=find(loc{:,i});
pairs = cellfun(@(a,b)[max(b(b<a)),min(b(b>a))],idp,qq,'UniformOutput',false);
if length(pairs{1,i})==1
loc{i}=PM{:,i}>0.1;
qq{i}=find(loc{:,i});
pairs = cellfun(@(a,b)[max(b(b<a)),min(b(b>a))],idp,qq,'UniformOutput',false);
end
end
Adam Danz on 5 Dec 2019
Good work!
I just though of a small improvement you could make but it's not necessary. In your current version, there are two lines that call cellfun() and those lines are identical. A small problem with this approach is that if you ever make changes to one line, you need to make the same changes to the other line and that's often overlooked (imagine you make a change a year from now and forget that you must do the same for the other line).
To avoid that problem, you could wrap the cellfun() line into an anonymous function so that it's only defined once. The first line below has been added and the two celfun() lines have been replaced. Also, I added {i} to (idp{i},qq{i}) -- I think that's what you meant to do.
pairsFunc = @(a,b)cellfun(@(a,b)[max(b(b<a)),min(b(b>a))],a,b,'UniformOutput',false);
for i = 1:length(files)
[~,PM{i}]=islocalmin(TFWS{i,:});
loc{i}=PM{:,i}>0.9;
qq{i}=find(loc{:,i});
pairs = pairsFunc(idp{i},qq{i});
if length(pairs{1,i})==1
loc{i}=PM{:,i}>0.1;
qq{i}=find(loc{:,i});
pairs = pairsFunc(idp{i},qq{i});
end
end
Also note that you're overwriting the pairs variable on each iteration instead of storing the values. Is that really what you want to do?