Reputation: 111
Let say I have value A = [20 35 50 57 78 90 105 120 143]
, B=[2 40; 45 80; 85 145];
A
is position of maximum peak value and B
is range.
How to identify a number of maximum peak values in every range?
The answer I want is [2 3 4]
Additionally I'd like a cellarray peak
containing the corresponding peak values:
peak =
3 x 1 cell:
[20 35]
[50 57 78]
[90 105 120 143]
Upvotes: 1
Views: 142
Reputation: 18176
A = [20 35 50 57 78 90 105 120 143];
B = [2 40; 45 80; 85 145];
C = zeros(size(B,1),1); % Initialise C
pks = cell(size(B,1),1); % Initalise peak value collector
for ii = 1:size(B,1)
pks{ii,1} = A(find(A>B(ii,1) && A<B(ii,2)));
C(ii,1) = numel(pks{ii,1});
end
C =
2 3 4
Explanation:
The for
loop crawls through B
via its rows. For each row it uses the value in column one as a lower threshold and the value in column 2 as a higher threshold. The values in array A
are checked against this and collected in the cell array pks
. Finally numel
returns just the number of elements found this way and stores that in C
.
I assumed you are defining your ranges as open intervals, i.e. the boundaries do not belong to the set. If on the other hand you want closed intervals, simple use this:
C(ii) = numel(find(A>=B(ii,1) & A<=B(ii,2)));
A slightly faster method using logicals instead of find
, thanks to @rayryeng:
C = zeros(size(B,1),1); % Initialise C
for ii = 1:size(B,1)
tmp = A > B(ii,1) & A < B(ii,2);
pks{ii,1} = A(tmp);
C(ii,1) = sum(tmp);
end
This works approximately the same as above, albeit faster. The logicals A > B(ii,1) & A < B(ii,2)
place a 1
on every location within the interval and a 0
on all other locations. Just sum the ones and you've got your desired number. This can be used as logical indexing to get the values of A
. Open and closed intervals work the same as above.
Upvotes: 4
Reputation: 221524
Here's one using bsxfun
, max
and nanmax
-
%// Mask of valid binning for A within the bins defined by the columns of B
mask = bsxfun(@ge,A(:),B(:,1).') & bsxfun(@le,A(:),B(:,2).')
%// Create interval version of A
A_intv = bsxfun(@times,mask,A(:))
A_intv(~mask) = nan
%// Get argmax for each bin
[~,max_mask_idx] = nanmax(A_intv,[],1)
%// Get start+1 indices for each bin
[~,start_idx] = max(mask,[],1)
%// Finally the output after subtracting start indices from max_mask_idx
out = max_mask_idx - (start_idx-1)
Upvotes: 3
Reputation: 104474
An alternative method is to use bsxfun
and permute
. What we can do is make this into a 3D problem where each slice tells you how many elements are in between each of the corresponding ranges in B
. You'd them sum up over every column of each slice:
%// Given
A = [20 35 50 57 78 90 105 120 143];
B = [2 40; 45 80; 85 145];
%// Create the 3D matrix we talked about
BL = permute(B(:,1), [2 3 1])
BR = permute(B(:,2), [2 3 1])
out = bsxfun(@ge, A, BL) & bsxfun(@le, A, BR);
%// Reshape output into a vector
vals = reshape(sum(out, 2), 1, []);
We get:
>> vals
vals =
2 3 4
Upvotes: 3