⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nearestneighbour.m

📁 在matlab环境下
💻 M
字号:
function [idx, tri] = nearestneighbour(varargin)
%NEARESTNEIGHBOUR    find nearest neighbours
%   IDX = NEARESTNEIGHBOUR(X) finds the nearest neighbour by Euclidean
%   distance to each point (column) in X from X. X is a matrix with points
%   as columns. IDX is a vector of indices into X, such that X(:, IDX) are
%   the nearest neighbours to X. e.g. the nearest neighbour to X(:, 2) is
%   X(:, IDX(2))
%
%   IDX = NEARESTNEIGHBOUR(P, X) finds the nearest neighbour by Euclidean
%   distance to each point in P from X. P and X are both matrices with the
%   same number of rows, and points are the columns of the matrices. Output
%   is a vector of indices into X such that X(:, IDX) are the nearest
%   neighbours to P
%
%   IDX = NEARESTNEIGHBOUR(I, X) where I is a logical vector or vector of
%   indices, and X has at least two rows, finds the nearest neighbour in X
%   to each of the points X(:, I).
%   I must be a row vector to distinguish it from a single point.
%   If X has only one row, the first input is treated as a set of 1D points
%   rather than a vector of indices
%
%   IDX = NEARESTNEIGHBOUR(..., Property, Value)
%   Calls NEARESTNEIGHBOUR with the indicated parameters set. Property
%   names can be supplied as just the first letters of the property name if
%   this is unambiguous, e.g. NEARESTNEIGHBOUR(..., 'num', 5) is equivalent
%   to NEARESTNEIGHBOUR(..., 'NumberOfNeighbours', 5). Properties are case
%   insensitive, and are as follows:
%      Property:                         Value:
%      ---------                         ------
%         NumberOfNeighbours             natural number, default 1
%            NEARESTNEIGHBOUR(..., 'NumberOfNeighbours', K) finds the closest
%            K points in ascending order to each point, rather than the
%            closest point
%
%         DelaunayMode                   {'on', 'off', |'auto'|}
%            DelaunayMode being set to 'on' means NEARESTNEIGHBOUR uses the
%            a Delaunay triangulation with dsearchn to find the points, if
%            possible. Setting it to 'auto' means NEARESTNEIGHBOUR decides
%            whether to use the triangulation, based on efficiency.
%
%         Triangulation                  Valid triangulation produced by
%                                        delaunay or delaunayn
%            If a triangulation is supplied, NEARESTNEIGHBOUR will attempt
%            to use it (in conjunction with dsearchn) to find the
%            neighbours.
%
%   [IDX, TRI] = NEARESTNEIGHBOUR( ... )
%   If the Delaunay Triangulation is used, TRI is the triangulation of X'.
%   Otherwise, TRI is an empty matrix
%
%   Example:
%
%     % Find the nearest neighbour in X to each column of X
%     x = rand(2, 10);
%     idx = nearestneighbour(x);
%
%     % Find the nearest neighbours to each point in p
%     p = rand(2, 5);
%     x = rand(2, 20);
%     idx = nearestneighbour(p, x)
%
%     % Find the five nearest neighbours to points x(:, [1 6 20]) in x
%     x = rand(4, 1000)
%     idx = nearestneighbour([1 6 20], x, 'NumberOfNeighbours', 5)
%
%   See also DELAUNAYN, DSEARCHN, TSEARCH

%TODO    Allow other metrics than Euclidean distance
%TODO    Implement the Delaunay mode for multiple neighbours

% Copyright 2006 Richard Brown. This code may be freely used and
% distributed, so long as it maintains this copyright line
error(nargchk(1, Inf, nargin, 'struct'));

% Default parameters
userParams.NumberOfNeighbours = 1     ;
userParams.DelaunayMode       = 'auto'; % {'on', 'off', |'auto'|}
userParams.Triangulation      = []    ;

% Parse inputs
[P, X, fIndexed, userParams] = parseinputs(userParams, varargin{:});

% Special case uses Delaunay triangulation for speed.

% Determine whether to use Delaunay - set fDelaunay true or false
nX  = size(X, 2);
nP  = size(P, 2);
dim = size(X, 1);

switch lower(userParams.DelaunayMode)
  case 'on'
	%TODO Delaunay can't currently be used for finding more than one
	%neighbour
	fDelaunay = userParams.NumberOfNeighbours == 1 && ...
	  size(X, 2) > size(X, 1)                      && ...
	  ~fIndexed;
  case 'off'
	fDelaunay = false;
  case 'auto'
	fDelaunay = userParams.NumberOfNeighbours == 1 && ...
	  ~fIndexed                                    && ...
	  size(X, 2) > size(X, 1)                      && ...
	  ( ~isempty(userParams.Triangulation) || delaunaytest(nX, nP, dim) );
end

% Try doing Delaunay, if fDelaunay.
fDone = false;
if fDelaunay
  tri = userParams.Triangulation;
  if isempty(tri)
	try
	  tri   = delaunayn(X');
	catch
	  msgId = 'NearestNeighbour:DelaunayFail';
	  msg = ['Unable to compute delaunay triangulation, not using it. ',...
		'Set the DelaunayMode parameter to ''off'''];
	  warning(msgId, msg);
	end
  end
  if ~isempty(tri)
	try
	  idx = dsearchn(X', tri, P')';
	  fDone = true;
	catch
	  warning('NearestNeighbour:DSearchFail', ...
		'dsearchn failed on triangulation, not using Delaunay');
	end
  end
else % if fDelaunay
  tri = [];
end

% If it didn't use Delaunay triangulation, find the neighbours directly by
% finding minimum distances
if ~fDone
  idx = zeros(userParams.NumberOfNeighbours, size(P, 2));

  % Loop through the set of points P, finding the neighbours
  Y = zeros(size(X));
  for iPoint = 1:size(P, 2)
	x = P(:, iPoint);

	% This is the faster than using repmat based techniques such as
	% Y = X - repmat(x, 1, size(X, 2))
	for i = 1:size(Y, 1)
	  Y(i, :) = X(i, :) - x(i);
	end

	% Find the closest points
	dSq = sum(abs(Y).^2, 1);
	if ~fIndexed
	  iSorted = minn(dSq, userParams.NumberOfNeighbours);
	else
	  iSorted = minn(dSq, userParams.NumberOfNeighbours + 1);
	  iSorted = iSorted(2:end);
	end
	idx(:, iPoint) = iSorted';
  end
end % if ~fDone

end % nearestneighbour




%DELAUNAYTEST   Work out whether the combination of dimensions makes
%fastest to use a Delaunay triangulation in conjunction with dsearchn.
%These parameters have been determined empirically on a Pentium M 1.6G /
%WinXP / 512MB / Matlab R14SP3 platform. Their precision is not
%particularly important
function tf = delaunaytest(nx, np, dim)
switch dim
  case 2
	tf = np > min(1.5 * nx, 400);
  case 3
	tf = np > min(4 * nx  , 1200);
  case 4
	tf = np > min(40 * nx , 5000);

	% if the dimension is higher than 4, it is almost invariably better not
	% to try to use the Delaunay triangulation
  otherwise
	tf = false;
end % switch
end % delaunaytest




%MINN   find the n most negative elements in x, and return their indices
%  in ascending order
function I = minn(x, n)
%Note: preallocation with I = zeros(1,n) can actually slow the code down,
%particularly if the matrix is small. I've put it in, however, because it
%is good practice
%Feel free to comment the next line if you want
I = zeros(1, n);

% Sort by finding the minimum entry, storing it, and replacing with Inf
for i = 1:n
  [xmin, I(i)] = min(x);
  x(I(i)) = Inf;
end

end % minn




%PARSEINPUTS    Support function for nearestneighbour
function [P, X, fIndexed, userParams] = parseinputs(userParams, varargin)
if length(varargin) == 1 || ~isnumeric(varargin{2})
  P           = varargin{1};
  X           = varargin{1};
  fIndexed    = true;
  varargin(1) = [];
else
  P             = varargin{1};
  X             = varargin{2};
  varargin(1:2) = [];

  % Check the dimensions of X and P
  if size(X, 1) ~= 1
	% Check to see whether P is in fact a vector of indices
	if size(P, 1) == 1
	  try
		P = X(:, P);
	  catch
		error('NearestNeighbour:InvalidIndexVector', ...
		  'Unable to index matrix using index vector');
	  end
	  fIndexed = true;
	else
	  fIndexed = false;
	end % if size(P, 1) == 1
  else % if size(X, 1) ~= 1
	fIndexed = false;
  end

  if ~fIndexed && size(P, 1) ~= size(X, 1)
	error('NearestNeighbour:DimensionMismatch', ...
	  'No. of rows of input arrays doesn''t match');
  end
end
% Parse the Property/Value pairs
if rem(length(varargin), 2) ~= 0
  error('NearestNeighbour:propertyValueNotPair', ...
	'Additional arguments must take the form of Property/Value pairs');
end

propertyNames = {'numberofneighbours', 'delaunaymode', 'triangulation'};
while length(varargin) ~= 0
  property = varargin{1};
  value    = varargin{2};

  % If the property has been supplied in a shortened form, lengthen it
  iProperty = find(strncmpi(property, propertyNames, length(property)));
  if isempty(iProperty)
	error('NearestNeighbour:InvalidProperty', 'Invalid Property');
  elseif length(iProperty) > 1
	error('NearestNeighbour:AmbiguousProperty', ...
	  'Supplied shortened property name is ambiguous');
  end
  property = propertyNames{iProperty};

  switch property
	case 'numberofneighbours'
	  if rem(value, 1) ~= 0 || ...
		  value > length(X) - double(fIndexed) || ...
		  value < 1
		error('NearestNeighbour:InvalidNumberOfNeighbours', ...
		  'Number of Neighbours must be an integer, and smaller than the no. of points in X');
	  end
	  userParams.NumberOfNeighbours = value;

	case 'delaunaymode'
	  fOn = strcmpi(value, 'on');
	  if strcmpi(value, 'off')
		userParams.DelaunayMode = 'off';
	  elseif fOn || strcmpi(value, 'auto')
		if userParams.NumberOfNeighbours ~= 1
		  if fOn
			warning('NearestNeighbour:TooMuchForDelaunay', ...
			  'Delaunay Triangulation method works only for one neighbour');
		  end
		  userParams.DelaunayMode = 'off';
		elseif size(X, 2) < size(X, 1) + 1
		  if fOn
			warning('NearestNeighbour:TooFewDelaunayPoints', ...
			  'Insufficient points to compute Delaunay triangulation');
		  end
		  userParams.DelaunayMode = 'off';

		elseif size(X, 1) == 1
		  if fOn
			warning('NearestNeighbour:DelaunayDimensionOne', ...
			  'Cannot compute Delaunay triangulation for 1D input');
		  end
		  userParams.DelaunayMode = 'off';
		else
		  userParams.DelaunayMode = value;
		end
	  else
		warning('NearestNeighbour:InvalidOption', ...
		  'Invalid Option');
	  end % if strcmpi(value, 'off')

	case 'triangulation'
	  if isnumeric(value) && size(value, 2) == size(X, 1) + 1 && ...
		  all(ismember(1:size(X, 2), value))
		userParams.Triangulation = value;
	  else
		error('NearestNeighbour:InvalidTriangulation', ...
		  'Triangulation not a valid Delaunay Triangulation');
	  end
  end % switch property

  varargin(1:2) = [];
end % while

end %parseinputs

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -