somtoolbox2/som_label.m
4dbef185
 function [sTo] = som_label(sTo, mode, inds, labels)
 
 %SOM_LABEL Give/clear labels to/from map or data struct.
 %
 % sTo = som_label(sTo, mode, inds [, labels])
 % 
 %   sD = som_label(sD,'add',20,'a_label');
 %   sM = som_label(sM,'replace',[2 4],'a_label');
 %   sM = som_label(sM,'add',som_bmus(sM,x),'BMU');
 %   sD = som_label(sD,'prune',[1:10]');
 %   sM = som_label(sM,'clear','all');         
 %
 %  Input and output arguments ([]'s are optional): 
 %   sTo      (struct) data or map struct to which the labels are put 
 %   mode     (string) 'add' or 'replace' or 'prune' or 'clear'
 %   inds     (vector) indeces of the vectors to which the labels
 %                     are put. Note: this must be a column vector!
 %            (matrix) subscript indeces to the '.labels' field. The vector 
 %                     is given by the first index (e.g. inds(i,1)). 
 %            (string) for 'prune' and 'clear' modes, the string 'all'
 %                     means that all vectors should be pruned/cleared
 %   [labels]          The labels themselves. The number of rows much match 
 %                     the number of given indeces, except if there is either
 %                     only one index or only one label. If mode is
 %                     'prune' or 'clear', labels argument is ignored.
 %            (string) Label.
 %            (string array) Each row is a label.
 %            (cell array of strings) All labels in a cell are handled 
 %                     as a group and are applied to the same vector given 
 %                     on the corresponding row of inds.
 %
 % Note: If there is only one label/index, it is used for each specified
 % index/label.
 %
 % For more help, try 'type som_label' or check out online documentation.
 % See also  SOM_AUTOLABEL, SOM_SHOW_ADD, SOM_SHOW.
 
 %%%%%%%%%%%%% DETAILED DESCRIPTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 % som_label
 %
 % PURPOSE
 %
 % Add (or remove) labels to (from) map and data structs.
 %
 % SYNTAX
 %
 %  sTo = som_label(sTo, 'clear', inds)
 %  sTo = som_label(sTo, 'prune', inds)
 %  sTo = som_label(sTo, 'add', inds, labels)
 %  sTo = som_label(sTo, 'replace', inds, labels)
 %
 % DESCRIPTION
 %
 % This function can be used to give and remove labels in map and data
 % structs. Of course the same operation could be done by hand, but this
 % function offers an alternative and hopefully slightly user-friendlier
 % way to do it.
 %
 % REQUIRED INPUT ARGUMENTS
 %
 %   sTo    (struct) data or map struct to which the labels are put 
 %   mode   (string) The mode of operation. 
 %                    'add'     : adds the given labels
 %                    'clear'   : removes labels
 %                    'replace' : replaces current labels with given
 %                                labels; basically same as 'clear'
 %                                followed by 'add'
 %                    'prune'   : removes empty labels ('') from between
 %                                non-empty labels, e.g. if the labels of
 %                                a vector were {'A','','','B','','C'}
 %                                they'd become {'A','B','C'}. Some empty
 %                                labels may be left at the end of the list.
 %
 %   inds            Identifies the vectors to which the operation
 %                   (given by mode) is applied to.
 %          (vector) Linear indexes of the vectors, size n x 1.
 %                   Notice! This should be a column vector!
 %          (matrix) The labels are in a cell matrix. By giving matrix 
 %                   argument for inds, you can address this matrix
 %                   directly. The first index gives the vector and the
 %                   second index the vertical position of the label in
 %                   the labels array. Size n x 2, where n is the 
 %                   number of labels. 
 %          (string) for 'prune' and 'clear' modes, the string 'all'
 %                   means that all vectors should be pruned/cleared
 %
 % OPTIONAL INPUT ARGUMENTS 
 %
 %   [labels]        The labels themselves. The number of rows much match 
 %                   the number of given indeces, except if there is either
 %                   only one index or only one label. 
 %          (string) Label, e.g. 'label'
 %          (string array) Each row is a label, 
 %                   e.g. ['label1'; 'label2'; 'label3']
 %          (cell array of strings) All labels in a cell are handled 
 %                   as a group and are applied to the same vector given 
 %                   on the corresponding row of inds.
 %                   e.g. three labels: {'label1'; 'label2'; 'label3'}
 %                   e.g. a group of labels: {'label1', 'label2', 'label3'}
 %                   e.g. three groups: {{'la1'},{'la21','la22'},{'la3'}
 %
 % OUTPUT ARGUMENTS
 % 
 %   sTo    (struct) the given data/map struct with modified labels
 % 
 % EXAMPLES
 %
 %  This is the basic way to add a label to map structure:
 %   sMap = som_label(sMap,'add',3,'label');
 %
 %  The following examples have identical results: 
 %   sMap = som_label(sMap,'add',[4; 13], ['label1'; 'label2']);
 %   sMap = som_label(sMap,'add',[4; 13], {{'label1'};{'label2'}});
 %
 %  Labeling the BMU of a vector x (and removing any old labels)
 %   sMap = som_label(sMap,'replace',som_bmus(sMap,x),'BMU');
 %
 %  Pruning labels 
 %   sMap = som_label(sMap,'prune','all');
 %
 %  Clearing labels from a struct
 %   sMap = som_label(sMap,'clear','all');
 %   sMap = som_label(sMap,'clear',[1:4, 9:30]');
 %
 % SEE ALSO
 % 
 %  som_autolabel   Automatically label a map/data set.
 %  som_show        Show map planes.
 %  som_show_add    Add for example labels to the SOM_SHOW visualization.
 
 % Copyright (c) 1997-2000 by the SOM toolbox programming team.
 % http://www.cis.hut.fi/projects/somtoolbox/
 
 % Version 2.0beta juuso 101199
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %% check arguments
 
 error(nargchk(3, 4, nargin));  % check no. of input args is correct
 
 % sTo
 switch sTo.type, 
 case 'som_map',  [dlen dim] = size(sTo.codebook);
 case 'som_data', [dlen dim] = size(sTo.data);
 end
 maxl = size(sTo.labels,2); % maximum number of labels for a single vector
 
 % inds
 if ischar(inds) & strcmp(inds,'all'), 
   inds = [1:dlen]'; 
 end
 if length(inds)>2 & size(inds,2)>2, inds = inds'; end
 ni = size(inds,1);
 n = ni; 
 
 % labels
 if nargin==4, 
   % convert labels to a cell array of cells
   if ischar(labels), labels = cellstr(labels); end
   if iscellstr(labels), 
     tmplab = labels; 
     nl = size(labels,1);
     labels = cell(nl,1);  
     for i=1:nl, 
       if ~iscell(tmplab{i}) 
 	if ~isempty(tmplab{i}), labels{i} = tmplab(i,:);
 	else labels{i} = {}; end
       else
 	labels(i) = tmplab(i);
       end
     end
     clear tmplab;
   end
   nl = size(labels,1);    
 end
 
 % the case of a single label/index
 if any(strcmp(mode,{'add','replace'})),
   n = max(nl,ni);   
   if n>1, 
     if ni==1, 
       inds = zeros(n,1)+inds(1); 
     elseif nl==1,
       label = labels{1}; 
       labels = cell(n,1); 
       for i=1:n, labels{i} = label; end
     elseif ni ~= nl,
       error('The number of labels and indexes does not match.'); 
     end
   end
 end
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %% action
 
 switch mode,   
  case 'clear',
   if size(inds,2)>2, 
     inds = inds(find(inds(:,2)<=maxl),:); % ignore if subindex is out-of-range
     inds = sub2ind([dlen maxl],inds(:,1),inds(:,2)); 
     sTo.labels{inds} = []; 
   else
     sTo.labels(inds,:) = cell(n,maxl); 
   end
  case 'prune', 
   if size(inds,2)==1, 
     % subindex gives the index from which the pruning is started
     inds = [inds, ones(n,1)]; % from 1 by default
   end 
   select = ones(1,maxl);     
   for i=1:n, 
     v = inds(i,1); s = inds(i,2); select(:) = 1; 
     for j=s:maxl, select(j) = ~isempty(sTo.labels{v,j}); end
     if ~all(select), 
       labs = cell(1,maxl); 
       labs(1:sum(select)) = sTo.labels(v,find(select));
       sTo.labels(v,:) = labs; 
     end
   end
  case 'add', 
   if size(inds,2)==1, 
     % subindex gives the index from which the adding is started
     inds = [inds, ones(n,1)]; % from 1 by default
   end 
   for i=1:n, 
     v = inds(i,1); s = inds(i,2); l = length(labels{i});
     for j=1:l, 
       while s<=size(sTo.labels,2) & ~isempty(sTo.labels{v,s}), s=s+1; end
       sTo.labels{v,s} = labels{i}{j}; 
       s=s+1; 
     end
   end
  case 'replace', 
   if size(inds,2)==1, 
     % subindex gives the index from which the replacing is started
     inds = [inds, ones(n,1)]; % from 1 by default
   end 
   for i=1:n, 
     v = inds(i,1); s = inds(i,2); l = length(labels(i)); 
     for j=1:l, sTo.labels{v,s-1+j} = labels{i}{j}; end 
   end
  otherwise
   error(['Unrecognized mode: ' mode]);
 end
 
 sTo.labels = remove_empty_columns(sTo.labels);
 
 [dlen maxl] = size(sTo.labels);
 for i=1:dlen, 
   for j=1:maxl, 
     if isempty(sTo.labels{i,j}) & ~ischar(sTo.labels{i,j}), 
       sTo.labels{i,j} = ''; 
     end
   end
 end
 
 return;
 
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %% subfunctions
 
 function labels = remove_empty_columns(labels)
 
   [dlen maxl] = size(labels);
   
   % find which columns are empty
   cols = zeros(1,maxl); 
   for i=1:dlen, 
     for j=1:maxl,
       cols(j) = cols(j) + ~isempty(labels{i,j}); 
     end
   end
   while maxl>0 & cols(maxl)==0, maxl = maxl-1; end % check starting from end
 
   if maxl==0, labels = cell(dlen,1); 
   elseif maxl<size(labels,2), labels = labels(:,1:maxl); 
   else % ok
   end
   % end of remove_empty_columns
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%