wolffd@0: function [h,Coord,Color,height] = som_dendrogram(Z,varargin) wolffd@0: wolffd@0: %SOM_DENDROGRAM Visualize a dendrogram. wolffd@0: % wolffd@0: % [h,Coord,Color,height] = som_dendrogram(Z, [[argID,] value, ...]) wolffd@0: % wolffd@0: % Z = som_linkage(sM); wolffd@0: % som_dendrogram(Z); wolffd@0: % som_dendrogram(Z,sM); wolffd@0: % som_dendrogram(Z,'coord',co); wolffd@0: % wolffd@0: % Input and output arguments ([]'s are optional): wolffd@0: % h (vector) handle to the arc lines wolffd@0: % Z (matrix) size n-1 x 1, the hierarchical cluster matrix wolffd@0: % returned by functions like LINKAGE and SOM_LINKAGE wolffd@0: % n is the number of original data samples. wolffd@0: % [argID, (string) See below. The values which are unambiguous can wolffd@0: % value] (varies) be given without the preceeding argID. wolffd@0: % Coord (matrix) size 2*n-1 x {1,2}, the coordinates of the wolffd@0: % original data samples and cluster nodes used wolffd@0: % in the visualization wolffd@0: % Color (matrix) size 2*n-1 x 3, the colors of ... wolffd@0: % height (vector) size 2*n-1 x 1, the heights of ... wolffd@0: % wolffd@0: % Here are the valid argument IDs and corresponding values. The values wolffd@0: % which are unambiguous (marked with '*') can be given without the wolffd@0: % preceeding argID. wolffd@0: % 'data' *(struct) map or data struct: many other optional wolffd@0: % arguments require this wolffd@0: % (matrix) data matrix wolffd@0: % 'coord' (matrix) size n x 1 or n x 2, the coordinates of wolffd@0: % the original data samples either in 1D or 2D wolffd@0: % (matrix) size 2*n-1 x {1,2}, the coordinates of both wolffd@0: % original data samples and each cluster wolffd@0: % *(string) 'SOM', 'pca#', 'sammon#', or 'cca#': the coordinates wolffd@0: % are calculated using the given data and the wolffd@0: % required projection algorithm. The '#' at the wolffd@0: % end of projection algorithms refers to the wolffd@0: % desired output dimension and can be either 1 or 2 wolffd@0: % (2 by default). In case of 'SOM', the unit wolffd@0: % coordinates (given by SOM_VIS_COORDS) are used. wolffd@0: % 'color' (matrix) size n x 3, the color of the original data samples wolffd@0: % (matrix) size 2*n-1 x 3, the colors of both original wolffd@0: % data samples and each cluster wolffd@0: % (string) color specification, e.g. 'r.', used for each node wolffd@0: % 'height' (vector) size n-1 x 1, the heights used for each cluster wolffd@0: % (vector) size 2*n-1 x 1, the heights used for both original wolffd@0: % data samples and each cluster wolffd@0: % *(string) 'order', the order of combination determines height wolffd@0: % 'depth', the depth at which the combination wolffd@0: % happens determines height wolffd@0: % 'linecolor' (string) color specification for the arc color, 'k' by default wolffd@0: % (vector) size 1 x 3 wolffd@0: % wolffd@0: % See also SOM_LINKAGE, DENDROGRAM. wolffd@0: wolffd@0: % Copyright (c) 2000 by Juha Vesanto wolffd@0: % Contributed to SOM Toolbox on June 16th, 2000 by Juha Vesanto wolffd@0: % http://www.cis.hut.fi/projects/somtoolbox/ wolffd@0: wolffd@0: % Version 2.0beta juuso 160600 wolffd@0: wolffd@0: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% wolffd@0: %% read the arguments wolffd@0: wolffd@0: % Z wolffd@0: nd = size(Z,1)+1; wolffd@0: nc = size(Z,1); wolffd@0: wolffd@0: % varargin wolffd@0: Coordtype = 'natural'; Coord = []; codim = 1; wolffd@0: Colortype = 'none'; Color = []; wolffd@0: height = [zeros(nd,1); Z(:,3)]; wolffd@0: M = []; wolffd@0: linecol = 'k'; wolffd@0: wolffd@0: i=1; wolffd@0: while i<=length(varargin), wolffd@0: argok = 1; wolffd@0: if ischar(varargin{i}), wolffd@0: switch varargin{i}, wolffd@0: case 'data', i = i + 1; M = varargin{i}; wolffd@0: case 'coord', wolffd@0: i=i+1; wolffd@0: if isnumeric(varargin{i}), Coord = varargin{i}; Coordtype = 'given'; wolffd@0: else wolffd@0: if strcmp(varargin{i},'SOM'), Coordtype = 'SOM'; wolffd@0: else Coordtype = 'projection'; Coord = varargin{i}; wolffd@0: end wolffd@0: end wolffd@0: case 'color', wolffd@0: i=i+1; wolffd@0: if isempty(varargin{i}), Colortype = 'none'; wolffd@0: elseif ischar(varargin{i}), Colortype = 'colorspec'; Color = varargin{i}; wolffd@0: else Colortype = 'given'; Color = varargin{i}; wolffd@0: end wolffd@0: case 'height', i=i+1; height = varargin{i}; wolffd@0: case 'linecolor', i=i+1; linecol = varargin{i}; wolffd@0: case 'SOM', wolffd@0: Coordtype = 'SOM'; wolffd@0: case {'pca','pca1','pca2','sammon','sammon1','sammon2','cca','cca1','cca2'}, wolffd@0: Coordtype = 'projection'; Coord = varargin{i}; wolffd@0: case {'order','depth'}, height = varargin{i}; wolffd@0: end wolffd@0: elseif isstruct(varargin{i}), M = varargin{i}; wolffd@0: else wolffd@0: argok = 0; wolffd@0: end wolffd@0: if ~argok, wolffd@0: disp(['(som_dendrogram) Ignoring invalid argument #' num2str(i+1)]); wolffd@0: end wolffd@0: i = i+1; wolffd@0: end wolffd@0: wolffd@0: switch Coordtype, wolffd@0: case 'SOM', wolffd@0: if isempty(M) | ~any(strcmp(M.type,{'som_map','som_topol'})) , wolffd@0: error('Cannot determine SOM coordinates without a SOM.'); wolffd@0: end wolffd@0: if strcmp(M.type,'som_map'), M = M.topol; end wolffd@0: case 'projection', wolffd@0: if isempty(M), error('Cannot do projection without the data.'); end wolffd@0: if isstruct(M), wolffd@0: if strcmp(M.type,'som_data'), M = M.data; wolffd@0: elseif strcmp(M.type,'som_map'), M = M.codebook; wolffd@0: end wolffd@0: end wolffd@0: if size(M,1) ~= nd, wolffd@0: error('Given data must be equal in length to the number of original data samples.') wolffd@0: end wolffd@0: case 'given', wolffd@0: if size(Coord,1) ~= nd & size(Coord,1) ~= nd+nc, wolffd@0: error('Size of given coordinate matrix does not match the cluster hierarchy.'); wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% wolffd@0: %% initialization wolffd@0: wolffd@0: % Coordinates wolffd@0: switch Coordtype, wolffd@0: case 'natural', o = leavesorder(Z)'; [dummy,Coord] = sort(o); codim = 1; wolffd@0: case 'SOM', Coord = som_vis_coords(M.lattice,M.msize); codim = 2; wolffd@0: case 'projection', wolffd@0: switch Coord, wolffd@0: case {'pca','pca2'}, Coord = pcaproj(M,2); codim = 2; wolffd@0: case 'pca1', Coord = pcaproj(M,1); codim = 1; wolffd@0: case {'cca','cca2'}, Coord = cca(M,2,20); codim = 2; wolffd@0: case 'cca1', Coord = cca(M,1,20); codim = 1; wolffd@0: case {'sammon','sammon2'}, Coord = sammon(M,2,50); codim = 2; wolffd@0: case 'sammon1', Coord = sammon(M,1,50); codim = 1; wolffd@0: end wolffd@0: case 'given', codim = min(size(Coord,2),2); % nill wolffd@0: end wolffd@0: wolffd@0: if size(Coord,1) == nd, wolffd@0: Coord = [Coord; zeros(nc,size(Coord,2))]; wolffd@0: for i=(nd+1):(nd+nc), wolffd@0: leaves = leafnodes(Z,i,nd); wolffd@0: if any(leaves), Coord(i,:) = mean(Coord(leaves,:),1); else Coord(i,:) = Inf; end wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: % Colors wolffd@0: switch Colortype, wolffd@0: case 'colorspec', % nill wolffd@0: case 'none', Color = ''; wolffd@0: case 'given', wolffd@0: if size(Color,1) == nd, wolffd@0: Color = [Color; zeros(nc,3)]; wolffd@0: for i=(nd+1):(nd+nc), wolffd@0: leaves = leafnodes(Z,i,nd); wolffd@0: if any(leaves), Color(i,:) = mean(Color(leaves,:),1); wolffd@0: else Color(i,:) = 0.8; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: % height wolffd@0: if ischar(height), wolffd@0: switch height, wolffd@0: case 'order', height = [zeros(nd,1); [1:nc]']; wolffd@0: case 'depth', height = nodedepth(Z); height = max(height) - height; wolffd@0: end wolffd@0: else wolffd@0: if length(height)==nc, height = [zeros(nd,1); height]; end wolffd@0: end wolffd@0: wolffd@0: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% wolffd@0: %% draw wolffd@0: wolffd@0: % the arcs wolffd@0: lfrom = []; lto = []; wolffd@0: for i=1:nd+nc, wolffd@0: if i<=nd, ch = []; wolffd@0: elseif ~isfinite(Z(i-nd,3)), ch = []; wolffd@0: else ch = Z(i-nd,1:2)'; wolffd@0: end wolffd@0: if any(ch), wolffd@0: lfrom = [lfrom; i*ones(length(ch),1)]; wolffd@0: lto = [lto; ch]; wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: % the coordinates of the arcs wolffd@0: if codim == 1, wolffd@0: Lx = [Coord(lfrom), Coord(lto), Coord(lto)]; wolffd@0: Ly = [height(lfrom), height(lfrom), height(lto)]; wolffd@0: Lz = []; wolffd@0: else wolffd@0: Lx = [Coord(lfrom,1), Coord(lto,1), Coord(lto,1)]; wolffd@0: Ly = [Coord(lfrom,2), Coord(lto,2), Coord(lto,2)]; wolffd@0: Lz = [height(lfrom), height(lfrom), height(lto)]; wolffd@0: end wolffd@0: wolffd@0: washold = ishold; wolffd@0: if ~washold, cla; end wolffd@0: wolffd@0: % plot the lines wolffd@0: if isempty(Lz), wolffd@0: h = line(Lx',Ly','color',linecol); wolffd@0: else wolffd@0: h = line(Lx',Ly',Lz','color',linecol); wolffd@0: if ~washold, view(3); end wolffd@0: rotate3d on wolffd@0: end wolffd@0: wolffd@0: % plot the nodes wolffd@0: hold on wolffd@0: switch Colortype, wolffd@0: case 'none', % nill wolffd@0: case 'colorspec', wolffd@0: if codim == 1, plot(Coord,height,Color); wolffd@0: else plot3(Coord(:,1), Coord(:,2), height, Color); wolffd@0: end wolffd@0: case 'given', wolffd@0: som_grid('rect',[nd+nc 1],'line','none','Coord',[Coord, height],... wolffd@0: 'Markersize',10,'Markercolor',Color); wolffd@0: end wolffd@0: if ~washold, hold off, end wolffd@0: wolffd@0: return; wolffd@0: wolffd@0: wolffd@0: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% wolffd@0: %% subfunctions wolffd@0: wolffd@0: function depth = nodedepth(Z) wolffd@0: wolffd@0: nd = size(Z,1)+1; wolffd@0: nc = size(Z,1); wolffd@0: depth = zeros(nd+nc,1); wolffd@0: ch = nc+nd-1; wolffd@0: while any(ch), wolffd@0: c = ch(1); ch = ch(2:end); wolffd@0: if c>nd & isfinite(Z(c-nd,3)), wolffd@0: chc = Z(c-nd,1:2); wolffd@0: depth(chc) = depth(c) + 1; wolffd@0: ch = [ch, chc]; wolffd@0: end wolffd@0: end wolffd@0: return; wolffd@0: wolffd@0: function inds = leafnodes(Z,i,nd) wolffd@0: wolffd@0: inds = []; wolffd@0: ch = i; wolffd@0: while any(ch), wolffd@0: c = ch(1); ch = ch(2:end); wolffd@0: if c>nd & isfinite(Z(c-nd,3)), ch = [ch, Z(c-nd,1:2)]; end wolffd@0: if c<=nd, inds(end+1) = c; end wolffd@0: end wolffd@0: return; wolffd@0: wolffd@0: function order = leavesorder(Z) wolffd@0: wolffd@0: nd = size(Z,1)+1; wolffd@0: order = 2*nd-1; wolffd@0: nonleaves = 1; wolffd@0: while any(nonleaves), wolffd@0: j = nonleaves(1); wolffd@0: ch = Z(order(j)-nd,1:2); wolffd@0: if j==1, oleft = []; else oleft = order(1:(j-1)); end wolffd@0: if j==length(order), oright = []; else oright = order((j+1):length(order)); end wolffd@0: order = [oleft, ch, oright]; wolffd@0: nonleaves = find(order>nd); wolffd@0: end wolffd@0: return; wolffd@0: wolffd@0: wolffd@0: