--- a +++ b/code/preprocessing/EEG/fastica/pcamat.m @@ -0,0 +1,358 @@ +function [E, D] = pcamat(vectors, firstEig, lastEig, s_interactive, ... + s_verbose); +%PCAMAT - Calculates the pca for data +% +% [E, D] = pcamat(vectors, firstEig, lastEig, ... +% interactive, verbose); +% +% Calculates the PCA matrices for given data (row) vectors. Returns +% the eigenvector (E) and diagonal eigenvalue (D) matrices containing the +% selected subspaces. Dimensionality reduction is controlled with +% the parameters 'firstEig' and 'lastEig' - but it can also be done +% interactively by setting parameter 'interactive' to 'on' or 'gui'. +% +% ARGUMENTS +% +% vectors Data in row vectors. +% firstEig Index of the largest eigenvalue to keep. +% Default is 1. +% lastEig Index of the smallest eigenvalue to keep. +% Default is equal to dimension of vectors. +% interactive Specify eigenvalues to keep interactively. Note that if +% you set 'interactive' to 'on' or 'gui' then the values +% for 'firstEig' and 'lastEig' will be ignored, but they +% still have to be entered. If the value is 'gui' then the +% same graphical user interface as in FASTICAG will be +% used. Default is 'off'. +% verbose Default is 'on'. +% +% +% EXAMPLE +% [E, D] = pcamat(vectors); +% +% Note +% The eigenvalues and eigenvectors returned by PCAMAT are not sorted. +% +% This function is needed by FASTICA and FASTICAG + +% For historical reasons this version does not sort the eigenvalues or +% the eigen vectors in any ways. Therefore neither does the FASTICA or +% FASTICAG. Generally it seams that the components returned from +% whitening is almost in reversed order. (That means, they usually are, +% but sometime they are not - depends on the EIG-command of matlab.) + +% @(#)$Id$ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Default values: +if nargin < 5, s_verbose = 'on'; end +if nargin < 4, s_interactive = 'off'; end +if nargin < 3, lastEig = size(vectors, 1); end +if nargin < 2, firstEig = 1; end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Check the optional parameters; +switch lower(s_verbose) + case 'on' + b_verbose = 1; + case 'off' + b_verbose = 0; + otherwise + error(sprintf('Illegal value [ %s ] for parameter: ''verbose''\n', s_verbose)); +end + +switch lower(s_interactive) + case 'on' + b_interactive = 1; + case 'off' + b_interactive = 0; + case 'gui' + b_interactive = 2; + otherwise + error(sprintf('Illegal value [ %s ] for parameter: ''interactive''\n', ... + s_interactive)); +end + +oldDimension = size (vectors, 1); +if ~(b_interactive) + if lastEig < 1 | lastEig > oldDimension + error(sprintf('Illegal value [ %d ] for parameter: ''lastEig''\n', lastEig)); + end + if firstEig < 1 | firstEig > lastEig + error(sprintf('Illegal value [ %d ] for parameter: ''firstEig''\n', firstEig)); + end +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Calculate PCA + +% Calculate the covariance matrix. +if b_verbose, fprintf ('Calculating covariance...\n'); end +covarianceMatrix = cov(vectors', 1); + +% Calculate the eigenvalues and eigenvectors of covariance +% matrix. +[E, D] = eig (covarianceMatrix); + +% The rank is determined from the eigenvalues - and not directly by +% using the function rank - because function rank uses svd, which +% in some cases gives a higher dimensionality than what can be used +% with eig later on (eig then gives negative eigenvalues). +rankTolerance = 1e-7; +maxLastEig = sum (diag (D) > rankTolerance); +if maxLastEig == 0, + fprintf (['Eigenvalues of the covariance matrix are' ... + ' all smaller than tolerance [ %g ].\n' ... + 'Please make sure that your data matrix contains' ... + ' nonzero values.\nIf the values are very small,' ... + ' try rescaling the data matrix.\n'], rankTolerance); + error ('Unable to continue, aborting.'); +end + +% Sort the eigenvalues - decending. +eigenvalues = flipud(sort(diag(D))); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Interactive part - command-line +if b_interactive == 1 + + % Show the eigenvalues to the user + hndl_win=figure; + bar(eigenvalues); + title('Eigenvalues'); + + % ask the range from the user... + % ... and keep on asking until the range is valid :-) + areValuesOK=0; + while areValuesOK == 0 + firstEig = input('The index of the largest eigenvalue to keep? (1) '); + lastEig = input(['The index of the smallest eigenvalue to keep? (' ... + int2str(oldDimension) ') ']); + % Check the new values... + % if they are empty then use default values + if isempty(firstEig), firstEig = 1;end + if isempty(lastEig), lastEig = oldDimension;end + % Check that the entered values are within the range + areValuesOK = 1; + if lastEig < 1 | lastEig > oldDimension + fprintf('Illegal number for the last eigenvalue.\n'); + areValuesOK = 0; + end + if firstEig < 1 | firstEig > lastEig + fprintf('Illegal number for the first eigenvalue.\n'); + areValuesOK = 0; + end + end + % close the window + close(hndl_win); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Interactive part - GUI +if b_interactive == 2 + + % Show the eigenvalues to the user + hndl_win = figure('Color',[0.8 0.8 0.8], ... + 'PaperType','a4letter', ... + 'Units', 'normalized', ... + 'Name', 'FastICA: Reduce dimension', ... + 'NumberTitle','off', ... + 'Tag', 'f_eig'); + h_frame = uicontrol('Parent', hndl_win, ... + 'BackgroundColor',[0.701961 0.701961 0.701961], ... + 'Units', 'normalized', ... + 'Position',[0.13 0.05 0.775 0.17], ... + 'Style','frame', ... + 'Tag','f_frame'); + +b = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'BackgroundColor',[0.701961 0.701961 0.701961], ... + 'HorizontalAlignment','left', ... + 'Position',[0.142415 0.0949436 0.712077 0.108507], ... + 'String','Give the indices of the largest and smallest eigenvalues of the covariance matrix to be included in the reduced data.', ... + 'Style','text', ... + 'Tag','StaticText1'); +e_first = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'Callback',[ ... + 'f=round(str2num(get(gcbo, ''String'')));' ... + 'if (f < 1), f=1; end;' ... + 'l=str2num(get(findobj(''Tag'',''e_last''), ''String''));' ... + 'if (f > l), f=l; end;' ... + 'set(gcbo, ''String'', int2str(f));' ... + ], ... + 'BackgroundColor',[1 1 1], ... + 'HorizontalAlignment','right', ... + 'Position',[0.284831 0.0678168 0.12207 0.0542535], ... + 'Style','edit', ... + 'String', '1', ... + 'Tag','e_first'); +b = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'BackgroundColor',[0.701961 0.701961 0.701961], ... + 'HorizontalAlignment','left', ... + 'Position',[0.142415 0.0678168 0.12207 0.0542535], ... + 'String','Range from', ... + 'Style','text', ... + 'Tag','StaticText2'); +e_last = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'Callback',[ ... + 'l=round(str2num(get(gcbo, ''String'')));' ... + 'lmax = get(gcbo, ''UserData'');' ... + 'if (l > lmax), l=lmax; fprintf([''The selected value was too large, or the selected eigenvalues were close to zero\n'']); end;' ... + 'f=str2num(get(findobj(''Tag'',''e_first''), ''String''));' ... + 'if (l < f), l=f; end;' ... + 'set(gcbo, ''String'', int2str(l));' ... + ], ... + 'BackgroundColor',[1 1 1], ... + 'HorizontalAlignment','right', ... + 'Position',[0.467936 0.0678168 0.12207 0.0542535], ... + 'Style','edit', ... + 'String', int2str(maxLastEig), ... + 'UserData', maxLastEig, ... + 'Tag','e_last'); +% in the first version oldDimension was used instead of +% maxLastEig, but since the program would automatically +% drop the eigenvalues afte maxLastEig... +b = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'BackgroundColor',[0.701961 0.701961 0.701961], ... + 'HorizontalAlignment','left', ... + 'Position',[0.427246 0.0678168 0.0406901 0.0542535], ... + 'String','to', ... + 'Style','text', ... + 'Tag','StaticText3'); +b = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'Callback','uiresume(gcbf)', ... + 'Position',[0.630697 0.0678168 0.12207 0.0542535], ... + 'String','OK', ... + 'Tag','Pushbutton1'); +b = uicontrol('Parent',hndl_win, ... + 'Units','normalized', ... + 'Callback',[ ... + 'gui_help(''pcamat'');' ... + ], ... + 'Position',[0.767008 0.0678168 0.12207 0.0542535], ... + 'String','Help', ... + 'Tag','Pushbutton2'); + + h_axes = axes('Position' ,[0.13 0.3 0.775 0.6]); + set(hndl_win, 'currentaxes',h_axes); + bar(eigenvalues); + title('Eigenvalues'); + + uiwait(hndl_win); + firstEig = str2num(get(e_first, 'String')); + lastEig = str2num(get(e_last, 'String')); + + % close the window + close(hndl_win); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% See if the user has reduced the dimension enought + +if lastEig > maxLastEig + lastEig = maxLastEig; + if b_verbose + fprintf('Dimension reduced to %d due to the singularity of covariance matrix\n',... + lastEig-firstEig+1); + end +else + % Reduce the dimensionality of the problem. + if b_verbose + if oldDimension == (lastEig - firstEig + 1) + fprintf ('Dimension not reduced.\n'); + else + fprintf ('Reducing dimension...\n'); + end + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Drop the smaller eigenvalues +if lastEig < oldDimension + lowerLimitValue = (eigenvalues(lastEig) + eigenvalues(lastEig + 1)) / 2; +else + lowerLimitValue = eigenvalues(oldDimension) - 1; +end + +lowerColumns = diag(D) > lowerLimitValue; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Drop the larger eigenvalues +if firstEig > 1 + higherLimitValue = (eigenvalues(firstEig - 1) + eigenvalues(firstEig)) / 2; +else + higherLimitValue = eigenvalues(1) + 1; +end +higherColumns = diag(D) < higherLimitValue; + +% Combine the results from above +selectedColumns = lowerColumns & higherColumns; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% print some info for the user +if b_verbose + fprintf ('Selected [ %d ] dimensions.\n', sum (selectedColumns)); +end +if sum (selectedColumns) ~= (lastEig - firstEig + 1), + error ('Selected a wrong number of dimensions.'); +end + +if b_verbose + fprintf ('Smallest remaining (non-zero) eigenvalue [ %g ]\n', eigenvalues(lastEig)); + fprintf ('Largest remaining (non-zero) eigenvalue [ %g ]\n', eigenvalues(firstEig)); + fprintf ('Sum of removed eigenvalues [ %g ]\n', sum(diag(D) .* ... + (~selectedColumns))); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Select the colums which correspond to the desired range +% of eigenvalues. +E = selcol(E, selectedColumns); +D = selcol(selcol(D, selectedColumns)', selectedColumns); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Some more information +if b_verbose + sumAll=sum(eigenvalues); + sumUsed=sum(diag(D)); + retained = (sumUsed / sumAll) * 100; + fprintf('[ %g ] %% of (non-zero) eigenvalues retained.\n', retained); +end + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function newMatrix = selcol(oldMatrix, maskVector); + +% newMatrix = selcol(oldMatrix, maskVector); +% +% Selects the columns of the matrix that marked by one in the given vector. +% The maskVector is a column vector. + +% 15.3.1998 + +if size(maskVector, 1) ~= size(oldMatrix, 2), + error ('The mask vector and matrix are of uncompatible size.'); +end + +numTaken = 0; + +for i = 1 : size (maskVector, 1), + if maskVector(i, 1) == 1, + takingMask(1, numTaken + 1) = i; + numTaken = numTaken + 1; + end +end + +newMatrix = oldMatrix(:, takingMask); \ No newline at end of file