texnic
texnic

Reputation: 4108

Matlab: how to find an enclosing grid cell index for multiple points

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

Answers (4)

knedlsepp
knedlsepp

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.

Example:

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

freude
freude

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

Divakar
Divakar

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

Luis Mendo
Luis Mendo

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

Related Questions