%TIERANK	Sorts a list of data points, returning the rank
%		of each element, such that tied points are assigned
%		the mean value of the ranks they'd have if they were
%		slightly different
%

% ---------------------------------
% Copyright (c) 2003 Rick Hoge
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License version 2
% as published by the Free Software Foundation.
% ---------------------------------

function rnk = tierank( list )

e = 1e-3;

n = length(list);

% get sorted list and indices:

[y1,ix1] = sort(list);

% generate ranks by sorting indices:

[y2,rnk] = sort(ix1);


% now check for ties using a really confusing algorithm:

% first, generate finite diff: of sorted list.  Runs of tied values
% will have zero difference:

	d = diff( y1 );

% to handle machine precision limits, set all 'd' values below 
% the parameter 'e' to zero:

	kill = find( abs(d) < e );
	d( kill ) = zeros(1,length(kill));

% next, find all the NON zeros values in 'd'.  These represent all the
% points between ties, I think.

	not_tied_to_next = find( d );

% The problem is that you may still have runs of un-tied 
% values.  You need the BOUNDARIES of the tie runs.
% To get rid of runs of non-ties, calculate the diff of 'not_tied_to_next':

% if the sorted data ENDS with a tie-run, you need to make sure that
% last non-tie entry is not ignored in 'diff' calculations.  
% Test for tie ending, and add dummy value if appropriate:

	if d(length(d)) == 0

	  not_tied_to_next = [ not_tied_to_next(:); n ]';

	end


	diffof_not_tied_to_next = diff( not_tied_to_next );

% all values of '1' represent consectutive non-tie entries.  If you get
% rid of these, you should be left with non-tie entries followed by ties.
% These give the starting points of all the tie runs:

	tie_starts = not_tied_to_next( find( diffof_not_tied_to_next > 1 ) );

% of course if 'list' starts with a tie run, then the start of 
% the first tie won't be indicated by the finite difference series.

	if d(1) == 0

	  tie_starts = [0 tie_starts];

	end

% The number of tie runs is the length of the above expression:

	nties = length(tie_starts);

% now you need a FOR loop:


	for ii = 1:nties

% first, figure out the start of the first tie run:

	  start = tie_starts( ii ) + 1;

% next, find the end:

	  fin = min( not_tied_to_next( find( not_tied_to_next > start ) ) );

% find mean value of indices assigned to tie value:

	  tie_indices = ix1( start:fin );

	  mean_index = mean( rnk( tie_indices ) );

% re-assign this value to the ranks of the tied entries:

	  rnk( tie_indices ) = ones(1,length(tie_indices)) .* ...
				mean_index;

	end




  
  			
    


    
  
  