function varargout = auto_classify(varargin)
% AUTO_CLASSIFY M-file for auto_classify.fig
%      AUTO_CLASSIFY, by itself, creates a new AUTO_CLASSIFY or raises the existing
%      singleton*.
%
%      H = AUTO_CLASSIFY returns the handle to a new AUTO_CLASSIFY or the handle to
%      the existing singleton*.
%
%      AUTO_CLASSIFY('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in AUTO_CLASSIFY.M with the given input arguments.
%
%      AUTO_CLASSIFY('Property','Value',...) creates a new AUTO_CLASSIFY or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before auto_classify_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to auto_classify_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Copyright 2002-2003 The MathWorks, Inc.

% Edit the above text to modify the response to help auto_classify

% Last Modified by GUIDE v2.5 21-Jun-2006 00:34:13

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @auto_classify_OpeningFcn, ...
                   'gui_OutputFcn',  @auto_classify_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before auto_classify is made visible.
function auto_classify_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to auto_classify (see VARARGIN)

% input parameters

if isempty(varargin)
    handles.matrix2classify = [];
    handles.ncepstral = 5;
else
    handles.matrix2classify = varargin{1};
    handles.ncepstral = varargin{2};
end

handles.distancemeasure = 'sqEuclidean';

handles.cluster_method = 'kmeans';

set(handles.CepstralPopupMenu,'String',num2str([0:handles.ncepstral]'));
set(handles.CepstralPopupMenu,'Value',handles.ncepstral); %handles.ncepstral +1 );
% set(handles.CepstralPopupMenu,'Value',handles.ncepstral-1);

% Choose default command line output for auto_classify
% handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes auto_classify wait for user response (see UIRESUME)
% uiwait(handles.figure1);

uiwait(handles.figure1);
    
% --- Outputs from this function are returned to the command line.
function varargout = auto_classify_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

close;

function clusters = clusterdata(X,k,dmeasure)
% A wrapper function for clustering

try
    clusters = kmeans(X,k,'distance',dmeasure,'EmptyAction','singleton','replicates',30);
catch
    clusters = k;
end

% --- Executes on button press in AutoClassifyButton.
function AutoClassifyButton_Callback(hObject, eventdata, handles)
% hObject    handle to AutoClassifyButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

set(handles.AcceptButton,'Enable','off');
guidata(gcbo,handles);

cncepstral = get(handles.CepstralPopupMenu,'Value');

matrix2classify = handles.matrix2classify;
matrix2classify = madnormalize(matrix2classify,1); % normalize the columns
matrix2classify = matrix2classify(:,1:cncepstral+1); % include the cepstral coefficients that you want

if length(matrix2classify(1,:)) > 3
    matrix2classify = matrix2classify(:,[1,3:cncepstral+1]); % exclude the first cepestral coefficient
end

try
    segweight = str2num(get(handles.SegLenWeight,'String'));
catch
    segweight = 1;
    set(handles.SegLenWeight,'String','1');
end

% Weight the segment lengths by square root of the weight
matrix2classify(:,1) = matrix2classify(:,1)*sqrt(segweight);
 
% Other weightings can be added here but the cepestral coefficients have a
% natural exponential decline which acts a weighting

%Handle transformations to segment length

contents = get(handles.TransformPopupMenu,'String');
value = get(handles.TransformPopupMenu,'Value');

switch contents{value}
    case 'None'
        matrix2classify = matrix2classify;
    case 'Exclude'
        matrix2classify = matrix2classify(:,2:length(matrix2classify(1,:)));
    case 'Log'
        matrix2classify(:,1) = log(matrix2classify(:,1));
    case 'Exp'
        matrix2classify(:,1) = exp(matrix2classify(:,1));
end

if length(matrix2classify(1,:)) == 0 % matrix to classify has no information
    return
end

rangestate = get(handles.RangeSpecify,'Value');
diagnosticstate = get(handles.DiagnosticCheckbox,'Value');

if strcmp(handles.cluster_method,'hierarchical')
    handles.classification = cluster_hierarchical(matrix2classify);
    nclusters = length(unique(handles.classification)) - 1;
    set(handles.KClasses,'String',nclusters);
    
    if diagnosticstate
        pcaplot(matrix2classify,handles.classification);
    end
    
else if strcmp(handles.cluster_method, 'kmeans')

if not(rangestate)

    kclassstr = get(handles.KClasses,'String');

    if not(isempty(kclassstr))    
        try
            nclasses = str2num(kclassstr);
        catch
            return;
        end
    
        classification =  clusterdata(matrix2classify,nclasses,handles.distancemeasure);% this can be changed to a different clustering algorithm
    
        % clusterdata shows how a clustering algorithm can be hooked in where the number of
        % classes need to be assigned.
        
        if length(classification) == 1
            msgbox('Error in clustering. Try changing the number of target clusters or rerunning.');
        else
            if diagnosticstate
                figure();
                [silh h] = silhouette(matrix2classify,classification);
                pcaplot(matrix2classify,classification);
            end
        handles.classification = classification;
        end
    else
        return;
    end
else
    try
        minclust = str2num(get(handles.MinClusters,'String'));
    catch
        return;
    end
    try
        maxclust = str2num(get(handles.MaxClusters,'String'));
    catch
        return;
    end 
    
    resultsclust = cell((1+ maxclust)-minclust,1);
    
    hw = waitbar(0,'Clustering data set ');
    for i = 1:(1+maxclust)-minclust
        resultsclust{i} = clusterdata(matrix2classify,minclust + (i-1),handles.distancemeasure);
        if length(resultsclust{i}) == 1
            msgbox(['Clustering failed at ' num2str(resultsclust{i}) ' clusters. Try changing the range of clusters or rerunning.']);
            return;
        end
        waitbar(i/(1+maxclust-minclust));
    end
    close(hw);
    
    silhmean = zeros((1+ maxclust)-minclust,1);
    
    for i = 1:1+(maxclust)-minclust
        
        if diagnosticstate
            figure();
            [silh h] = silhouette(matrix2classify,resultsclust{i});
            pcaplot(matrix2classify,resultsclust{i});
        else
            silh = silhouette(matrix2classify,resultsclust{i});
        end
            
        silhmean(i) = mean(silh);
    end
    
    [x I] = max(silhmean);
    handles.classification = resultsclust{I(1)};
    
    set(handles.KClasses,'String',num2str(minclust + (I(1)-1)));
end
end
    end


set(handles.AcceptButton,'Enable','on');

guidata(gcbo,handles);

% --- Executes on button press in CancelButton.
function CancelButton_Callback(hObject, eventdata, handles)
% hObject    handle to CancelButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

handles.output = 0;
guidata(hObject,handles);
uiresume(handles.figure1);

% --- Executes on button press in AcceptButton.
function AcceptButton_Callback(hObject, eventdata, handles)
% hObject    handle to AcceptButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

handles.output = handles.classification;
guidata(hObject,handles);
uiresume(handles.figure1);


function CepstralEdit_Callback(hObject, eventdata, handles)
% hObject    handle to CepstralEdit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of CepstralEdit as text
%        str2double(get(hObject,'String')) returns contents of CepstralEdit as a double


% --- Executes during object creation, after setting all properties.
function CepstralEdit_CreateFcn(hObject, eventdata, handles)
% hObject    handle to CepstralEdit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end



function MinClusters_Callback(hObject, eventdata, handles)
% hObject    handle to MinClusters (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of MinClusters as text
%        str2double(get(hObject,'String')) returns contents of MinClusters as a double


% --- Executes during object creation, after setting all properties.
function MinClusters_CreateFcn(hObject, eventdata, handles)
% hObject    handle to MinClusters (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end



function MaxClusters_Callback(hObject, eventdata, handles)
% hObject    handle to MaxClusters (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of MaxClusters as text
%        str2double(get(hObject,'String')) returns contents of MaxClusters as a double


% --- Executes during object creation, after setting all properties.
function MaxClusters_CreateFcn(hObject, eventdata, handles)
% hObject    handle to MaxClusters (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


% --- Executes on selection change in TransformPopupMenu.
function TransformPopupMenu_Callback(hObject, eventdata, handles)
% hObject    handle to TransformPopupMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns TransformPopupMenu contents as cell array
%        contents{get(hObject,'Value')} returns selected item from TransformPopupMenu

% value = get(hObject,'Value');
% 
% if value == 2
%     handles.matrix2classify(:,1) = log(handles.matrix2classify(:,1)); 
% else
%     handles.matrix2classify(:,1) = exp(handles.matrix2classify(:,1));
% end

% --- Executes during object creation, after setting all properties.
function TransformPopupMenu_CreateFcn(hObject, eventdata, handles)
% hObject    handle to TransformPopupMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

function KClasses_Callback(hObject, eventdata, handles)
% hObject    handle to KClasses (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of KClasses as text
%        str2double(get(hObject,'String')) returns contents of KClasses as a double


% --- Executes during object creation, after setting all properties.
function KClasses_CreateFcn(hObject, eventdata, handles)
% hObject    handle to KClasses (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


% --- Executes on selection change in CepstralPopupMenu.
function CepstralPopupMenu_Callback(hObject, eventdata, handles)
% hObject    handle to CepstralPopupMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns CepstralPopupMenu contents as cell array
%        contents{get(hObject,'Value')} returns selected item from CepstralPopupMenu


% --- Executes during object creation, after setting all properties.
function CepstralPopupMenu_CreateFcn(hObject, eventdata, handles)
% hObject    handle to CepstralPopupMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end



% --- Executes on button press in RangeSpecify.
function RangeSpecify_Callback(hObject, eventdata, handles)
% hObject    handle to RangeSpecify (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of RangeSpecify

state = get(hObject,'Value');

if state
    set(handles.KClasses,'Enable','off');
    set(handles.text3,'Visible','on');
    set(handles.MinClusters,'Visible','on');
    set(handles.text4,'Visible','on');
    set(handles.MaxClusters,'Visible','on');
else
    set(handles.KClasses,'Enable','on');
    set(handles.text3,'Visible','off');
    set(handles.MinClusters,'Visible','off');
    set(handles.text4,'Visible','off');
    set(handles.MaxClusters,'Visible','off');
end


% --- Executes on selection change in DistancePopupMenu.
function DistancePopupMenu_Callback(hObject, eventdata, handles)
% hObject    handle to DistancePopupMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns DistancePopupMenu contents as cell array
%        contents{get(hObject,'Value')} returns selected item from DistancePopupMenu

contents = get(hObject,'String');

dstring = contents{get(hObject,'Value')};

switch dstring
    case 'Squared Euclidean'
        handles.distancemeasure = 'sqEuclidean'
    case 'City Block (L1)'
        handles.distancemeasure = 'cityblock'
    case 'Cosine'
        handles.distancemeasure = 'cosine'
    case 'Correlation'
        handles.distancemeasure = 'correlation'
end

guidata(gcbo,handles);

% --- Executes during object creation, after setting all properties.
function DistancePopupMenu_CreateFcn(hObject, eventdata, handles)
% hObject    handle to DistancePopupMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

handles.distancemeasure = 'sqEuclidean';

guidata(gcbo,handles);

% --- Executes on button press in DiagnosticCheckbox.
function DiagnosticCheckbox_Callback(hObject, eventdata, handles)
% hObject    handle to DiagnosticCheckbox (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of DiagnosticCheckbox

function pcaplot(matrix2classify,classification)
% Plots the first two principal components uses different shapes selected
% randomly and different colors selected randomly to show classes.

nclusters = length(unique(classification));

if not(isempty(find(classification == 0)))
    nclusters = nclusters -1;
end

colors = rand(nclusters,3);
mark = mod(1:nclusters,13)+1;

[coef, score] = princomp(matrix2classify);

figure();

for i = 1:length(classification)
    h = line(score(i,1),score(i,2));
    markers = set(h,'Marker');
    if classification(i) == 0 % For segments which are not classified
        set(h,'Marker',markers{4});   
        set(h,'Color',[0 0 0]);
    else
        set(h,'Marker',markers{mark(classification(i))});   
        set(h,'Color',colors(classification(i),:));
    end
end

xlabel('PCA 1');
ylabel('PCA 2');

function classifymatrix = madnormalize(classifymatrix, cols2normalize)
  for i = cols2normalize
    classifymatrix(:,i) = (classifymatrix(:,i) - median(classifymatrix(:,i))) / mad(classifymatrix(:,i));
  end
  
  
% This code is modified from http://phys.columbia.edu/~aylin/clustering

function c=cluster_aylin(p1,p2);
%enter the p1,p2 found from hneighbors.m and get the struct array c. 
%c(i).c will contain the indices of the objects that is in cluster i.
%c(1).c will be the cluster that has the maximum number of objects.
nn=size(p1,2);
p=std(p1);g=p(p2);p=p';
z=zeros(nn,1);zz=zeros(nn,1);
n=zeros(1,nn);
for i=1:nn
    n(i)=max(max(p1(:,p2(:,i))));
end
gg=[1:nn; max(g); n]';n=find(n<1);
[q1,q1]=sort(gg(n,2)');
g=gg(n(q1),1);
t=cputime;
j=1;m=1
	for i=1:length(n)
        ii=g(i);b=p2(:,ii);a=b;aa=a(find(gg(a,2)<1));
        if length([0; unique(z(aa))])<=2;a1=1;a2=0;
            while a1~=a2;a1=length(aa);
                a=unique(p2(:,a));a=a(find(gg(a,2)<1));
                a=unique(a(find(gg(a,2)<=mean(gg(aa,2))+std(gg(aa,2)))));
                a=a(find(ismember(a,aa)==0));
                if ~isempty(a);aa=[aa;a];
                    jj=aa(find(z(aa)));
                    if ~isempty(jj);;
                         u=unique(z(jj));
                         if length(u)==1;
                            zz(aa(find(~z(aa))))=m;z(aa)=u;m=m+1;
                         end
                         break;
                    end
                end;a2=length(aa);
            end;a=aa;
            jj=a(find(z(a)));
            if isempty(jj);
                z(a)=j;j=j+1;
                zz(a)=m;zz(b)=m-.1;zz(ii)=m-.2;m=m+1;
            end
        end
    end
    u=unique(z)
    u=u(find(u));v=length(u);vv=floor(1/v);if vv;vv=' is';else vv='s      are';end
    fprintf([int2str(v) ' cluster' vv ' found in ' num2str(cputime-t) 'sec\n\n'])
	q0=[];for i=1:v;qq=find(z==u(i));q0=[q0 length(qq)];end;
    [q1,q2]=sort(q0);%q2=q2(find(100*q1/nn>1));v=length(q2);
	c=[];for i=1:v;qq=find(z==u(q2(i)));[j,j]=sort(zz(qq));c(v-i+1).c=qq(j);end;
    if isempty(c);fprintf('\tNo cluster was found. \n \tscale the data (step size must be 1)\n');end 
    
    
    function [p1,p2]=hneighbors(e);
	% this function finds the neighbors of each object in 'e' within a unit hypercube
	% and returns the sorted object distances to q.q1 and their identities to q.q2 , 'e' is a 
	% matrix where i th row and j th column represents the j th component of the i th object. 
	s='find(';i='abs(e(:,%d)-e(i,%d))<1&';
    % assuming that the data is given scaled and the characteristic step size is 1, variable s 
	% keeps a script to find the objects that lie within a unit hypercube around the i th object.
    for j=1:size(e,2);
        s=[s sprintf(i,j,j)];
	end;s([end end+1])=');';
     % runs the script s for each of the objects and stores the sorted distances 
     % from the i th object in q(i).q1 and their indentities in q(i).q2
    nn=size(e,1);m=ceil(nn^(1/4));p1=ones(m,nn);p2=kron(ones(m,1),1:nn);
    for i=1:nn;
        j=eval(s);
        [q,qq]=sort(sqrt(sum((e(j,:)-kron(ones(length(j),1),e(i,:)))'.^2)));q=q(find(q<1));mm=length(q);
        qq=j(qq(1:mm))';mn=min([m mm]);
        p1(1:mn,i)=q(1:mn)';
        p2(1:mn,i)=qq(1:mn)';
    end
    
    
function classification = cluster_hierarchical(matrix2classify)
    [p1,p2] = hneighbors(matrix2classify);
    c = cluster_aylin(p1,p2);
    
    nclusters = size(c,2); 
    
    total_classified = 0
    
    nsegments = size(matrix2classify,1);
            
    classification = zeros(nsegments,1);
    
    for i = 1:nclusters
        for j = 1:length(c(i).c)
            classification(c(i).c(j)) = i;
        end
    end
;


% --- Executes on button press in HierarchicalRadioButton.
function HierarchicalRadioButton_Callback(hObject, eventdata, handles)
% hObject    handle to HierarchicalRadioButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of HierarchicalRadioButton

selected = get(hObject,'Value');

if selected
    handles.cluster_method = 'hierarchical';
end

guidata(gcbo,handles);


% --- Executes on button press in KmeansRadioButton.
function KmeansRadioButton_Callback(hObject, eventdata, handles)
% hObject    handle to KmeansRadioButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of KmeansRadioButton

selected = get(hObject,'Value');

if selected
    handles.cluster_method = 'kmeans';
end

guidata(gcbo,handles);




function SegLenWeight_Callback(hObject, eventdata, handles)
% hObject    handle to SegLenWeight (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of SegLenWeight as text
%        str2double(get(hObject,'String')) returns contents of SegLenWeight as a double


% --- Executes during object creation, after setting all properties.
function SegLenWeight_CreateFcn(hObject, eventdata, handles)
% hObject    handle to SegLenWeight (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


