# HG changeset patch
# User Stefano Guidoni <ilguido@users.sf.net>
# Date 1609684216 -3600
#      Sun Jan 03 15:30:16 2021 +0100
# Node ID 477b5e53e8cf7d45d964f4d8c3beef2299ef31b3
# Parent  8d55ef9d1e432cdaeae578b93a356662dc3dfff4
new function: inconsistent

diff -r 8d55ef9d1e43 -r 477b5e53e8cf inst/inconsistent.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inst/inconsistent.m	Sun Jan 03 15:30:16 2021 +0100
@@ -0,0 +1,107 @@
+## Copyright (C) 2020 Stefano Guidoni <ilguido@users.sf.net>
+##
+## This program is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free Software
+## Foundation; either version 3 of the License, or (at your option) any later
+## version.
+##
+## This program is distributed in the hope that it will be useful, but WITHOUT
+## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+## details.
+##
+## You should have received a copy of the GNU General Public License along with
+## this program; if not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{Y} =} inconsistent (@var{Z})
+## @deftypefnx {Function File} {@var{Y} =} inconsistent (@var{Z}, @var{d})
+##
+## Compute the inconsistency coefficient for each link of a hierarchical cluster
+## tree
+##
+## Given a hierarchical cluster tree @var{Z} generated by the @code{linkage}
+## function, @code{inconsistent} computes the inconsistency coefficient for each
+## link of the tree, using all the links down to the @var{d}-th level below that 
+## link.
+##
+## The default depth @var{d} is 2, which means that only two levels are 
+## considered: the level of the computed link and the level below that.
+##
+## Each row of @var{Y} corresponds to the row of same index of @var{Z}.
+## The columns of @var{Y} are respectively: the mean of the heights of the links
+## used for the calculation, the standard deviation of the heights of those
+## links, the number of links used, the inconsistency coefficient.
+##
+## @strong{Reference}
+## Jain, A., and R. Dubes. Algorithms for Clustering Data. 
+## Upper Saddle River, NJ: Prentice-Hall, 1988.
+## @end deftypefn
+##
+## @seealso{dendrogram,linkage,pdist,squareform}
+
+## Author: Stefano Guidoni <ilguido@users.sf.net>
+
+function Y = inconsistent(Z, d = 2)
+  
+  ## check the input
+  if (nargin < 1) || (nargin > 2)
+    print_usage ();
+  endif
+  
+  ## MATLAB compatibility:
+  # when d = 0, which does not make sense, the result of inconsistent is the 
+  # same as d = 1, which is... inconsistent
+  if ((d < 0) || (! isscalar(d)))
+    error('inconsistent: d must be a positive integer number');
+  endif
+  
+  if ((columns(Z) != 3) || (! isnumeric(Z)) || ...
+      (! (max(Z(end,1:2)) == rows(Z) * 2)))
+    error('inconsistent: Z must be a matrix generated by the linkage function');
+  endif
+
+  # number of observations
+  n = rows(Z) + 1;
+  
+  ## recursive function
+  # while depth > 1 search the links (columns 1 and 2 of Z) below the current
+  # link and then append the height of the current link to the vector v.
+  # The height of the starting link should be the last one of the vector.
+  function v = inconsistent_recursion(index, depth)
+    v = [];
+    if (depth > 1)
+      for j = 1:2
+        if (Z(index,j) > n)
+          new_index = Z(index,j) - n;
+          v = [v inconsistent_recursion(new_index, depth - 1)];
+        endif
+      endfor
+    endif
+    v(end+1) = Z(index,3);
+  endfunction
+    
+  
+  ## compute the inconsistency coefficient for every link
+  for i = 1:rows(Z)
+    v = inconsistent_recursion(i, d);
+    
+    Y(i,1) = mean(v);
+    Y(i,2) = std(v);
+    Y(i,3) = length(v);
+    # the inconsistency coefficient is (current_link_height - mean) / std;
+    # if the standard deviation is zero, it is zero by definition
+    if (Y(i,2) != 0)
+      Y(i,4) = (v(end) - Y(i,1)) / Y(i,2);
+    else
+      Y(i,4) = 0;
+    endif
+  endfor
+  
+endfunction
+
+## this test requires the fisheriris dataset
+%!xtest
+%! load fisheriris;
+%! Z = linkage(meas,'average','chebychev');
+%! assert (cond (inconsistent (Z)), 39.9, 1e-3);
