Reputation: 4108
I am trying to allocate (x, y) points to the cells of a non-uniform rectangular grid. Simply speaking, I have a grid defined as a sorted non-equidistant array
xGrid = [x1, x2, x3, x4];
and an array of numbers x
lying between x1
and x4
. For each x
, I want to find its position in xGrid
, i.e. such i
that
xGrid(i) <= xi <= xGrid(i+1)
Is there a better (faster/simpler) way to do it than arrayfun(@(x) find(xGrid <= x, 1, 'last'), x)
?
Upvotes: 3
Views: 197
Reputation: 6084
You are looking for the second output of histc
:
[~,where] = histc(x, xGrid)
This returns the array where
such that xGrid(where(i)) <= x(i) < xGrid(where(i)+1)
holds.
xGrid = [2,4,6,8,10];
x = [3,5,6,9,11];
[~,where] = histc(x, xGrid)
Yields the following output:
where =
1 2 3 4 0
If you want xGrid(where(i)) < x(i) <= xGrid(where(i)+1)
, you need to do some trickery of negating the values:
[~,where] = histc(-x,-flip(xGrid));
where(where~=0) = numel(xGrid)-where(where~=0)
This yields:
where =
1 2 2 4 0
Because x(3)==6
is now counted for the second interval (4,6]
instead of [6,8)
as before.
Upvotes: 3
Reputation: 3832
If x
is a column this might help
xg1=meshgrid(xGrid,1:length(x));
xg2=ndgrid(x,1:length(xGrid));
[~,I]=min(floor(abs(xg1-xg2)),[],2);
or a single line implementation
[~,I]=min(floor(abs(meshgrid(xGrid,1:length(x))-ndgrid(x,1:length(xGrid)))),[],2);
Example: xGrid=[1 2 3 4 5], x=[2.5; 1.3; 1.7; 4.8; 3.3]
Result:
I =
2
1
1
4
3
Upvotes: 2
Reputation: 221754
See if this works for you -
matches = bsxfun(@le,xGrid(1:end-1),x(:)) & bsxfun(@ge,xGrid(2:end),x(:))
[valid,pos] = max(cumsum(matches,2),[],2)
pos = pos.*(valid~=0)
Sample run -
xGrid =
5 2 1 6 8 9 2 1 6
x =
3 7 14
pos =
8
4
0
Explanation on the sample run -
First element of x, 3
occurs last between ...1 6
with the criteria of xGrid(i) <= xi <= xGrid(i+1)
at the backend of xGrid
and that 1
is at the eight position, so the first element of the output pos
is 8
. This continues for the second element 7
, which is found between 6 and 8
and that 6
is at the fourth place in xGrid
, so the second element of the output is 4
. For the third element 14
which doesn't find any neighbours to satisfy the criteria xGrid(i) <= xi <= xGrid(i+1)
and is therefore outputted as 0
.
Upvotes: 2
Reputation: 112769
Using bsxfun
for the comparisons and exploiting find
-like capabilities of max
's second output:
xGrid = [2 4 6 8]; %// example data
x = [3 6 5.5 10 -10]; %// example data
comp = bsxfun(@gt, xGrid(:), x(:).'); %'// see if "x" > "xGrid"
[~, result] = max(comp, [], 1); %// index of first "xGrid" that exceeds each "x"
result = result-1; %// subtract 1 to find the last "xGrid" that is <= "x"
This approach gives 0
for values of x
that lie outside xGrid
. With the above example values,
result =
1 3 2 0 0
Upvotes: 2