Hierarchically grouped boxplot - matlab

I would like to draw a boxplot with two sets of data to compare. I am willing to use Hierarchically grouped boxplot. I could just plot one set of my data using this function. I was wondering how I can use this function to plot two sets of data together. I drew the second set of data in red one by hand to show what I am trying to plot!
My problem is that I can't put two sets of data on one graph with hold on.

Well, this is not precisely what you asked for, and does not use the function hierarchicalBoxplot, but it may be a workaround. I demonstrate it using MATLAB example data:
load carsmall
% cleaning the data a little
Origin = categorical(cellstr(Origin));
MPG(Origin=='Italy') = [];
Origin(Origin=='Italy') = [];
% this part is just for readability:
data1 = MPG;
groups1 = Origin;
data2 = MPG*3;
groups2 = Origin;
% And we start:
% =============
% we need a wider figure, with a white background:
figure('Color',[1 1 1],'Position',[178 457 1114 521])
main_ax = axes; % create a tmporary axes
% we get the measurements of the ploting area:
pos = main_ax.Position;
% and divide it to our data:
group_number = 6;
width = pos(3)/group_number; % the width of each group
% the bottom left corner of each group:
corner = linspace(pos(1),pos(3)+pos(1),group_number+1);
clf % clear the area!
% Now we plot everything in a loop:
for k = 1:group_number
% create a different axes for each group:
ax = axes;
boxplot(ax,data1,groups1); % plot the first set
hold on
boxplot(ax,data2,groups2) % plot the second set
% set the ylim to include all data:
ax.YLim = [min([data1; data2])-5 max([data1; data2])+10];
ax.XTickLabelRotation = 90; % rotate xlables if needed
box off
if k == 1
ylabel('Miles per Gallon (MPG)') % only for the most right axes
else
ax.YTick = [];
end
xlabel(['Group ' num2str(k)])
ax.Position = [corner(k) 0.2 width 0.7];
end
% and finally we place the title:
main_ax = axes('Position',[corner(1) 0.11 width*group_number 0.815]);
title('Miles per Gallon by Vehicle Origin')
axis off
% and this will color the data:
f = gcf;
colors = [1 0 0;0 0 1]; % red and blue
for g = 2:numel(f.Children)
for k = 1:numel(f.Children(g).Children(1).Children)
f.Children(g).Children(1).Children(k).Color = colors(1,:);
f.Children(g).Children(1).Children(k).MarkerEdgeColor = colors(1,:);
f.Children(g).Children(2).Children(k).Color = colors(2,:);
f.Children(g).Children(2).Children(k).MarkerEdgeColor = colors(2,:);
end
end
All this procedure gives:
It will probably need some final tweaks, but it's somthing to start from ;)
Edit
For a side by side view, you can plot all groups together, and just move the x-ticks:
% Making some data:
% MAKE SURE YOU UNDERSTAND HOW THE DATA IS ARRANGED WITHIN THE GRAPH
years = 6; % try to change this number
groups = 5; % try to change this number
data1 = rand(100,years);
data2 = rand(100,years)+0.3;
groups1 = randi(groups,100,1)*2-1; % groups 1 3 5 7 9
groups2 = randi(groups,100,1)*2; % groups 2 4 6 8 10
legendEntries = {'A' 'B'};
colors = [1 0 0;0 0 1]; % red and blue
% And we start:
% =============
% we need a wider figure, with a white background:
figure('Color',[1 1 1],'Position',[178 457 1400 521])
main_ax = axes; % create a temporary axes
% we get the measurements of the plotting area:
pos = main_ax.Position;
% and divide it to our data:
width = pos(3)/years; % the width of each group
% the bottom left corner of each group:
corner = linspace(pos(1),pos(3)+pos(1),years+1);
clf % clear the area!
% Now we plot everything in a loop:
for k = 1:years
% create a different axes for each group:
ax = axes;
boxplot(ax,[data1(:,k); data2(:,k)],[groups1; groups2]);
ax.XTick = 1.5:2:(groups*2-0.5); % to "combine" the groups in pairs
ax.XTickLabel = {'a','b','c','v','f'};
% set the ylim to include all data:
ax.YLim = [min([data1(:); data2(:)]) max([data1(:); data2(:)])];
box off
if k == 1
ylabel('Miles per Gallon (MPG)') % only for the most right axes
else
ax.YTick = [];
end
xlabel(num2str(2000+k)) % the labels for the years
ax.Position = [corner(k) 0.11 width 0.8];
% this will color the data:
for g = 1:2:numel(ax.Children.Children)-1
ax.Children.Children(g).Color = colors(1,:);
ax.Children.Children(g).MarkerEdgeColor = colors(1,:);
ax.Children.Children(g+1).Color = colors(2,:);
ax.Children.Children(g+1).MarkerEdgeColor = colors(2,:);
end
if k == years
% you can try to change here the index to 1:2 and see if you like it:
leg = legend(ax.Children.Children(20:21),legendEntries);
leg.Position(1) = 0.92;
end
end
% and finally we place the title:
main_ax = axes('Position',[corner(1) 0.11 width*years 0.815]);
title('Miles per Gallon by Vehicle Origin')
axis off
And we get the crowded plot:

Related

How to show the legend of zero values as a line in the legend?

I have below the following example, which shows the bars for the following vectors:
% % For Phi/Psi = +/-10
CoverageArea_mean_10 = [84.4735,21.1779,6.4247,2.1416];
CoverageArea_min_10 = [98.5128,21.1779,6.9007,2.1416];
CoverageArea_max_10 = [70.1963,19.0363,5.9488,2.1416];
% For Phi/Psi = +/-40
CoverageArea_mean_40 = [0,4.5211,2.3795,0];
CoverageArea_min_40 = [92.5640,21.1779,6.9007,2.1416];
CoverageArea_max_40 = [0,0.4759,0.2380,0];
as shown below
The problem is there are some zero values, and I need them to be represented as a line on the legend. How can I fix it?
The expected results:
%% Plotting the coverage area
x = [15,30,45,60];
figure
COVERAGE = [CoverageArea_min_10;CoverageArea_mean_10;CoverageArea_max_10];
COVERAGEAREA = [COVERAGE(:,1)';COVERAGE(:,2)';COVERAGE(:,3)';COVERAGE(:,4)'];
bar(x,COVERAGEAREA);
xlabel( 'Semi-angle at half power, \Phi_1_/_2 (°)' );
ylabel('Coverage area (m²)');
BarNames = {'min','mean','max'};
legend(BarNames,'Location','best');
grid on;
figure
COVERAGE1 = [CoverageArea_min_40;CoverageArea_mean_40;CoverageArea_max_40];
COVERAGEAREA1 = [COVERAGE1(:,1)';COVERAGE1(:,2)';COVERAGE1(:,3)';COVERAGE1(:,4)'];
bar(x,COVERAGEAREA1);
xlabel( 'Semi-angle at half power, \Phi_1_/_2 (°)' );
ylabel('Coverage area (m²)');
BarNames = {'min','mean','max'};
legend(BarNames,'Location','best');
grid on;
close all; clear all;
% Create figure
figure1 = figure('WindowState','maximized');
% Create axes
axes1 = axes('Parent',figure1);
hold(axes1,'on');
% Create multiple lines using matrix input to bar
% % For Phi/Psi = +/-10
CoverageArea_mean_10 = [84.4735,21.1779,6.4247,2.1416];
CoverageArea_min_10 = [98.5128,21.1779,6.9007,2.1416];
CoverageArea_max_10 = [70.1963,19.0363,5.9488,2.1416];
% For Phi/Psi = +/-40
CoverageArea_mean_40 = [0,4.5211,2.3795,0];
CoverageArea_min_40 = [92.5640,21.1779,6.9007,2.1416];
CoverageArea_max_40 = [0,0.4759,0.2380,0];
%% Plotting the coverage area
x = [15,30,45,60];
COVERAGE = [CoverageArea_min_10;CoverageArea_mean_10;CoverageArea_max_10];
COVERAGE1 = [CoverageArea_min_40;CoverageArea_mean_40;CoverageArea_max_40];
COVERAGEAREA1 = [COVERAGE1(:,1)';COVERAGE1(:,2)';COVERAGE1(:,3)';COVERAGE1(:,4)'];
COVERAGEAREA = [COVERAGE(:,1)';COVERAGE(:,2)';COVERAGE(:,3)';COVERAGE(:,4)'];
bar1 = bar(x,COVERAGEAREA1,'Parent',axes1);
set(bar1(3),'DisplayName','min');
set(bar1(2),'DisplayName','mean');
set(bar1(1),'DisplayName','max');
% Create ylabel
ylabel('Coverage area $(m^2)$','Interpreter','latex');
% Create xlabel
xlabel('$\phi_{1/2}$','Interpreter','latex');
box(axes1,'on');
grid(axes1,'on');
hold(axes1,'off');
% Set the remaining axes properties
set(axes1,'XTick',[15 30 45 60]);
% Create legend
legend1 = legend(axes1,'show');
set(legend1,'Interpreter','latex','AutoUpdate','off','Location','best');
% Create rectangle
annotation(figure1,'rectangle',...
[0.854166666666666 0.861889927310488 0.0223958333333336 0.0290758047767393],...
'Color',[1 1 1],...
'FaceColor',[1 1 1]);
% Create line
annotation(figure1,'line',[0.8578125 0.873958333333333],...
[0.881619937694704 0.881619937694704]);
% Create line
annotation(figure1,'line',[0.858333333333333 0.8734375],...
[0.867082035306334 0.867082035306334]);

Single boxplot for multiple group comparison

Here is the sample code that i used to compare two groups with random mean and standard deviation. However, i want to plot both groups in a single box in the box plot as shown in the attached figure where x-axis is group 1 and y-axis is group 2. I could not find any code doing this. Can some one please help me with this?
clc
clear
x=[rand(1,10) rand(1,10) rand(1,10) rand(1,10) rand(1,10) rand(1,10)];
n=10 ; xx=([1:6])'; % example
r=repmat(xx,1,n)';
g=r(:)';
positions = [1 2 3 4 5 6 ];
h=boxplot(x,g, 'positions', positions);
set(h,'linewidth',2)
set(gca,'xtick',[mean(positions(1:2)) mean(positions(3:4)) mean(positions(5:6)) ])
set(gca,'xticklabel',{'exp1','exp2','exp3'},'Fontsize',28)
color = ['c', 'y', 'c', 'y','c', 'y'];
h = findobj(gca,'Tag','Box');
for j=1:length(h)
patch(get(h(j),'XData'),get(h(j),'YData'),color(j),'FaceAlpha',.5);
end
now i want yellow and blue for exp1 in one box as shown below.. similarly for exp2 and exp3 so on.. so 3 boxes in one boxplot..Ideally this should work for any number of experiments.
For a single two-sided boxplot, we can use the 'Orientation' property, and overlay 2 boxplots one above the other:
x = [1 2 3 4 5 6 7 1 2 3 4 5 6 7];
group = [1,1,1,1,1,1,1,2,2,2,2,2,2,2];
% we need the precntiles of the groups so the boxes will overlap.
% on each boxplot we set the width to the other boxplot hight:
p1 = prctile(x(group==1),[25 75]);
p2 = prctile(x(group==2),[25 75]);
ax = axes;
% first group is vertical:
boxplot(x(group==2),'Positions',mean(x(group==1)),...
'Orientation','vertical','Widths',p1(2)-p1(1),'Colors','r');
lims1 = axis;
hold on
% secound group is horizontal:
boxplot(x(group==1),'Positions',mean(x(group==2)),...
'Orientation','horizontal','Widths',p2(2)-p2(1),'Colors','k');
% the values of the axis are no longer relevant, since they have two
% different meanings, depend on the group. So we hide them.
ax.XAxis.Visible = 'off';
ax.YAxis.Visible = 'off';
hold off
lims2 = axis;
% because each axis represent to different things, we make sure we see
% everything:
axis([max(lims1(1),lims2(1)),...
min(lims1(2),lims2(2)),...
min(lims1(3),lims2(3)),...
max(lims1(4),lims2(4))])
To create multiple two-sided box-plots you need to use an axes for each experiment:
x = rand(10,6);
nsp = floor(size(x,2)/2); % the number of subplots
meanx = mean(x);
% we need the precntiles of the groups so the boxes will overlap.
% on each boxplot we set the width to the other boxplot hight:
width = range(prctile(x,[25; 75]));
main_ax = axes; % create a tmporary axes
% we get the measurements of the ploting area:
pos = main_ax.Position;
% and divide it to our data:
axwidth = pos(3)/nsp; % the width of each group
% the bottom left corner of each group:
corner = linspace(pos(1),pos(3)+pos(1),nsp+1);
clf % clear the area!
% now we plot each pair of boxplot on a different subplot:
for k = 1:2:size(x,2)
ax = axes('Position',[corner((k+1)/2) pos(2) axwidth pos(4)]);
hold on
% first group is vertical:
boxplot(x(:,k),'Positions',meanx(k+1),...
'Orientation','vertical','Widths',width(k+1),'Colors','r');
% secound group is horizontal:
boxplot(x(:,k+1),'Positions',meanx(k),...
'Orientation','horizontal','Widths',width(k),'Colors','k');
% the values of the y-axis are no longer relevant, since they have two
% different meanings, depend on the group. So we hide them.
ax.YAxis.Visible = 'off';
% we use the x-axis to label the pairs of boxplots:
ax.XAxis.TickLabels = ['Exp ' num2str((k+1)/2)];
% because each axis represent to different things, we make sure we see
% everything:
minx = min(min(x(:,k:k+1)))*0.1;
maxx = max(max(x(:,k:k+1)))*1.1;
axis ([minx maxx minx maxx])
hold off
box off
% set the locations to the exact same place:
bx = findobj(ax,'tag','Box'); % get the boxes
posXdif = bx(2).XData(1)-bx(1).XData(1); % get the horizontal difference
posYdif = bx(2).YData(1)-bx(1).YData(1); % get the vertical difference
bx2Xdata = get(ax.Children(2).Children,{'XData'}); % get all X-data of box 2
bx2Ydata = get(ax.Children(2).Children,{'YData'}); % get all Y-data of box 2
% substruct horizontal difference X-data:
set(ax.Children(2).Children,{'XData'},...
cellfun(#(x) x-posXdif,bx2Xdata,'UniformOutput',false))
% substruct vertical difference Y-data:
set(ax.Children(2).Children,{'YData'},...
cellfun(#(y) y-posYdif,bx2Ydata,'UniformOutput',false))
end

How to plot a line on the top of a grouped bar chart?

I have a grouped bar chart and I want to compare the values .i.e , I mean I want to visualize it using lines. I tried the following code and output is also follows
Y=rand(5,5)
str = {'A'; 'B'; 'C'; 'D'; 'E';};
bar_widh=0.2;
h = bar(Y,bar_widh);
hold on;plot(Y,'b');
set(gca, 'XTickLabel',str, 'XTick',1:numel(str))
grid on
l = cell(1,5);
l{1}='P'; l{2}='Q'; l{3}='R'; l{4}='S'; l{5}='T';
legend(h,l);
I got the following output:
I want to visualize smallest quantity /larger quantity of the bar.In some cases larger value is bad. Can you help me to plot the color of the line same as the bar
I got output as follows
You can try this:
Y=rand(5,5);
str = {'A'; 'B'; 'C'; 'D'; 'E';};
bar_widh=0.2;
figure; hold on;grid on
h = bar(Y,bar_widh);
% to highlight the minimum of each group,
% copy data into a new matrix
Y_ = Y;
% find the minimum values and make the rest zeors
Y_(Y_~=repmat(min(Y_,[],1),size(Y,1),1)) = 0;
% then plot with so sort of highlighting
h2 = bar(Y_,0.5);
pause(0.1) % pause to allow bars to be drawn
% now go through each group of bars and plot the line
for i = 1:numel(h)
x = h(i).XData + h(i).XOffset; % find the x coordinates where the bars are plotted
ax = plot(x,Y(:,i)); % plot the line
% set color of the bars the same as the line
h(i).FaceColor = ax.Color;
h2(i).FaceColor = ax.Color;
end
set(gca, 'XTickLabel',str, 'XTick',1:numel(str))
legend('P','Q','R','S','T');
h(i).XData
is the center coordinates of the ith group of bars.
For example, in your case:
h(1).XData = [ 1 2 3 4 5 ]; % group P
h(2).XData = [ 1 2 3 4 5 ]; % group Q
...
h(5).XData = [ 1 2 3 4 5 ]; % group T
h(i).XOffset
is the offset value of each bar in the group from its corresponding centre coordinate.
For example, in your case:
h(1).XOffset = -0.3077; % group P
h(2).XOffset = -0.1538; % group Q
...
h(5).XOffset = 0.3077; % group T
Without highlighting the minimum values
Minimum values highlighted

MATLAB legend does not work for plotting 2 circles

close all; clc; clear all;
A0 = 1.5; % meters
lambda = 100 % meters
k = (2*pi)/lambda;
T = 3600 % Period in seconds
ome = 2*pi/T; % omega
x = 0; z = 0;
t = linspace(0,7200,100); % 2 periods, 100 data
zz=0;
for z = 0:20:20;
zz = zz+1;
% multiplied by 100, unit in cm/s
u= 100.*ome*A0*exp(-k*z)*sin(k*x - ome*t);
w = 100.*-ome*A0*exp(-k*z)*cos(k*x - ome*t);
uu(zz,:) = u; % size(uu) 2 100
ww(zz,:) = w; % size(ww) 2 100
end
figure(1)
color = -0.8;
for zz = 1:2
color = color + 0.8;
for i=1:3:49; % plot circle for one period
plot([uu(zz,i) uu(zz,i+3)],[ww(zz,i) ww(zz,i+3)], 'color',([color+0.2 0 0]), 'linewidth', 2)
hold on
end
end
title('Plot of lines from (0,0) to (u(i), v(i). Radius or amplitude in cm/s')
axis equal;
grid on;
legend('radius at surface','radius at depth 20')%
This script plots 2 circles: the small one is red, another is black. But the legend is not consistence with these circles.
this is because you create many line objects in your axes (each loop iteration when you do plot) and the legend function addresses these line objects. sp line1 and line2 are still part of the polygon you draw.
I'll answer your question, but know that your code is sub-optimal and that it is not the best way to draw 2 circles or polygons.
so with minimal change to your code this is what you can do:
....
figure(1)
color = -0.8;
for zz = 1:2
color = color + 0.8;
for i=1:3:49; % plot circle for one period
h(zz)=plot([uu(zz,i) uu(zz,i+3)],[ww(zz,i) ww(zz,i+3)], 'color',([color+0.2 0 0]), 'linewidth', 2)
hold on
end
end
title('Plot of lines from (0,0) to (u(i), v(i). Radius or amplitude in cm/s')
axis equal;
grid on;
legend([h(1) h(2)],{'radius at surface','radius at depth 20'});

Regarding visualization of movement of the data points in training of the Self-Organizing Map (SOM) using Simulink

I have implemented the Self-Organizing Map(SOM) algorithm in MATLAB. Suppose each of the data points are represented in 2-dimensional space. The problem is that I want to visualize the movement of each of the data points in the training phase i.e. I want to see how the points are moving and eventually forming clusters as the algorithm is in progress say at every fix duration. I believe that this can be done through Simulation in MATLAB,but I don't know how to incorporate my MATLAB code for visualization?
I developed a code example to visualize clustering data with multiple dimensions using all possible data projection in 2-D. It may not be the best idea for visualization (there are techniques developed for this, as SOM itself may be used for this need), specially for a higher dimension numbers, but when the number of possible projections (n-1)! is not that high it is a quite good visualizer.
Cluster Algorithm 
Since I needed access to the code so that I could save the cluster means and cluster labels for each iteration, I used a fast kmeans algorithm available at FEX by Mo Chen, but I had to adapt it so I could have this access. The adapted code is the following:
function [label,m] = litekmeans(X, k)
% Perform k-means clustering.
% X: d x n data matrix
% k: number of seeds
% Written by Michael Chen (sth4nth#gmail.com).
n = size(X,2);
last = 0;
iter = 1;
label{iter} = ceil(k*rand(1,n)); % random initialization
checkLabel = label{iter};
m = {};
while any(checkLabel ~= last)
[u,~,checkLabel] = unique(checkLabel); % remove empty clusters
k = length(u);
E = sparse(1:n,checkLabel,1,n,k,n); % transform label into indicator matrix
curM = X*(E*spdiags(1./sum(E,1)',0,k,k)); % compute m of each cluster
m{iter} = curM;
last = checkLabel';
[~,checkLabel] = max(bsxfun(#minus,curM'*X,dot(curM,curM,1)'/2),[],1); % assign samples to the nearest centers
iter = iter + 1;
label{iter} = checkLabel;
end
% Get last clusters centers
m{iter} = curM;
% If to remove empty clusters:
%for k=1:iter
% [~,~,label{k}] = unique(label{k});
%end
Gif Creation
I also used #Amro's Matlab video tutorial for the gif creation.
Distinguishable Colors
I used this great FEX by Tim Holy for making the cluster colors easier to distinguish.
Resulting code
My resulting code is as follows. I had some issues because the number of clusters would change for each iteration which would cause scatter plot update to delete all cluster centers without giving any errors. Since I didn't noticed that, I was trying to workaround the scatter function with any obscure method that I could find the web (btw, I found a really nice scatter plot alternative here), but fortunately I got what was happening going back to this today. Here is the code I did for it, you may feel free to use it, adapt it, but please keep my reference if you use it.
function varargout=kmeans_test(data,nClusters,plotOpts,dimLabels,...
bigXDim,bigYDim,gifName)
%
% [label,m,figH,handles]=kmeans_test(data,nClusters,plotOpts,...
% dimLabels,bigXDim,bigYDim,gifName)
% Demonstrate kmeans algorithm iterative progress. Inputs are:
%
% -> data (rand(5,100)): the data to use.
%
% -> nClusters (7): number of clusters to use.
%
% -> plotOpts: struct holding the following fields:
%
% o leftBase: the percentage distance from the left
%
% o rightBase: the percentage distance from the right
%
% o bottomBase: the percentage distance from the bottom
%
% o topBase: the percentage distance from the top
%
% o FontSize: FontSize for axes labels.
%
% o widthUsableArea: Total width occupied by axes
%
% o heigthUsableArea: Total heigth occupied by axes
%
% -> bigXDim (1): the big subplot x dimension
%
% -> bigYDim (2): the big subplot y dimension
%
% -> dimLabels: If you want to specify dimensions labels
%
% -> gifName: gif file name to save
%
% Outputs are:
%
% -> label: Sample cluster center number for each iteration
%
% -> m: cluster center mean for each iteration
%
% -> figH: figure handle
%
% -> handles: axes handles
%
%
% - Creation Date: Fri, 13 Sep 2013
% - Last Modified: Mon, 16 Sep 2013
% - Author(s):
% - W.S.Freund <wsfreund_at_gmail_dot_com>
%
% TODO List (?):
%
% - Use input parser
% - Adapt it to be able to cluster any algorithm function.
% - Use arrows indicating cluster centers movement before moving them.
% - Drag and drop small axes to big axes.
%
% Pre-start
if nargin < 7
gifName = 'kmeansClusterization.gif';
if nargin < 6
bigYDim = 2;
if nargin < 5
bigXDim = 1;
if nargin < 4
nDim = size(data,1);
maxDigits = numel(num2str(nDim));
dimLabels = mat2cell(sprintf(['Dim %0' num2str(maxDigits) 'd'],...
1:nDim),1,zeros(1,nDim)+4+maxDigits);
if nargin < 3
plotOpts = struct('leftBase',.05,'rightBase',.02,...
'bottomBase',.05,'topBase',.02,'FontSize',10,...
'widthUsableArea',.87,'heigthUsableArea',.87);
if nargin < 2
nClusters = 7;
if nargin < 1
center1 = [1; 0; 0; 0; 0];
center2 = [0; 1; 0; 0; 0];
center3 = [0; 0; 1; 0; 0];
center4 = [0; 0; 0; 1; 0];
center5 = [0; 0; 0; 0; 1];
center6 = [0; 0; 0; 0; 1.5];
center7 = [0; 0; 0; 1.5; 1];
data = [...
bsxfun(#plus,center1,.5*rand(5,20)) ...
bsxfun(#plus,center2,.5*rand(5,20)) ...
bsxfun(#plus,center3,.5*rand(5,20)) ...
bsxfun(#plus,center4,.5*rand(5,20)) ...
bsxfun(#plus,center5,.5*rand(5,20)) ...
bsxfun(#plus,center6,.2*rand(5,20)) ...
bsxfun(#plus,center7,.2*rand(5,20)) ...
];
end
end
end
end
end
end
end
% NOTE of advice: It seems that Matlab does not test while on
% refreshdata if the dimension of the inputs are equivalent for the
% XData, YData and CData while using scatter. Because of this I wasted
% a lot of time trying to debug what was the problem, trying many
% workaround since my cluster centers would disappear for no reason.
% Draw axes:
nDim = size(data,1);
figH = figure;
set(figH,'Units', 'normalized', 'Position',...
[0, 0, 1, 1],'Color','w','Name',...
'k-means example','NumberTitle','Off',...
'MenuBar','none','Toolbar','figure',...
'Renderer','zbuffer');
% Create dintinguishable colors matrix:
colorMatrix = distinguishable_colors(nClusters);
% Create axes, deploy them on handles matrix more or less how they
% will be positioned:
[handles,horSpace,vertSpace] = ...
createAxesGrid(5,5,plotOpts,dimLabels);
% Add main axes
bigSubSize = ceil(nDim/2);
bigSubVec(bigSubSize^2) = 0;
for k = 0:nDim-bigSubSize
bigSubVec(k*bigSubSize+1:(k+1)*bigSubSize) = ...
... %(nDim-bigSubSize+k)*nDim+1:(nDim-bigSubSize+k)*nDim+(nDim-bigSubSize+1);
bigSubSize+nDim*k:nDim*(k+1);
end
handles(bigSubSize,bigSubSize) = subplot(nDim,nDim,bigSubVec,...
'FontSize',plotOpts.FontSize,'box','on');
bigSubplotH = handles(bigSubSize,bigSubSize);
horSpace(bigSubSize,bigSubSize) = bigSubSize;
vertSpace(bigSubSize,bigSubSize) = bigSubSize;
set(bigSubplotH,'NextPlot','add',...
'FontSize',plotOpts.FontSize,'box','on',...
'XAxisLocation','top','YAxisLocation','right');
% Squeeze axes through space to optimize space usage and improve
% visualization capability:
[leftPos,botPos,subplotWidth,subplotHeight]=setCustomPlotArea(...
handles,plotOpts,horSpace,vertSpace);
pColorAxes = axes('Position',[leftPos(end) botPos(end) ...
subplotWidth subplotHeight],'Parent',figH);
pcolor([1:nClusters+1;1:nClusters+1])
% image(reshape(colorMatrix,[1 size(colorMatrix)])); % Used image to
% check if the upcoming buggy behaviour would be fixed. I was not
% lucky, though...
colormap(pColorAxes,colorMatrix);
% Change XTick positions to its center:
set(pColorAxes,'XTick',.5:1:nClusters+.5);
set(pColorAxes,'YTick',[]);
% Change its label to cluster number:
set(pColorAxes,'XTickLabel',[nClusters 1:nClusters-1]); % FIXME At
% least on my matlab I have to use this buggy way to set XTickLabel.
% Am I doing something wrong? Since I dunno why this is caused, I just
% change the code so that it looks the way it should look, but this is
% quite strange...
xlabel(pColorAxes,'Clusters Colors','FontSize',plotOpts.FontSize);
% Now iterate throw data and get cluster information:
[label,m]=litekmeans(data,nClusters);
nIters = numel(m)-1;
scatterColors = colorMatrix(label{1},:);
annH = annotation('textbox',[leftPos(1),botPos(1) subplotWidth ...
subplotHeight],'String',sprintf('Start Conditions'),'EdgeColor',...
'none','FontSize',18);
% Creates dimData_%d variables for first iteration:
for curDim=1:nDim
curDimVarName = genvarname(sprintf('dimData_%d',curDim));
eval([curDimVarName,'= m{1}(curDim,:);']);
end
% clusterColors will hold the colors for the total number of clusters
% on each iteration:
clusterColors = colorMatrix;
% Draw cluster information for first iteration:
for curColumn=1:nDim
for curLine=curColumn+1:nDim
% Big subplot data:
if curColumn == bigXDim && curLine == bigYDim
curAxes = handles(bigSubSize,bigSubSize);
curScatter = scatter(curAxes,data(curColumn,:),...
data(curLine,:),16,'filled');
set(curScatter,'CDataSource','scatterColors');
% Draw cluster centers
curColumnVarName = genvarname(sprintf('dimData_%d',curColumn));
curLineVarName = genvarname(sprintf('dimData_%d',curLine));
eval(['curScatter=scatter(curAxes,' curColumnVarName ',' ...
curLineVarName ',100,colorMatrix,''^'',''filled'');']);
set(curScatter,'XDataSource',curColumnVarName,'YDataSource',...
curLineVarName,'CDataSource','clusterColors')
end
% Small subplots data:
curAxes = handles(curLine,curColumn);
% Draw data:
curScatter = scatter(curAxes,data(curColumn,:),...
data(curLine,:),16,'filled');
set(curScatter,'CDataSource','scatterColors');
% Draw cluster centers
curColumnVarName = genvarname(sprintf('dimData_%d',curColumn));
curLineVarName = genvarname(sprintf('dimData_%d',curLine));
eval(['curScatter=scatter(curAxes,' curColumnVarName ',' ...
curLineVarName ',100,colorMatrix,''^'',''filled'');']);
set(curScatter,'XDataSource',curColumnVarName,'YDataSource',...
curLineVarName,'CDataSource','clusterColors');
if curLine==nDim
xlabel(curAxes,dimLabels{curColumn});
set(curAxes,'XTick',xlim(curAxes));
end
if curColumn==1
ylabel(curAxes,dimLabels{curLine});
set(curAxes,'YTick',ylim(curAxes));
end
end
end
refreshdata(figH,'caller');
% Preallocate gif frame. From Amro's tutorial here:
% https://stackoverflow.com/a/11054155/1162884
f = getframe(figH);
[f,map] = rgb2ind(f.cdata, 256, 'nodither');
mov = repmat(f, [1 1 1 nIters+4]);
% Add one frame at start conditions:
curFrame = 1;
% Add three frames without movement at start conditions
f = getframe(figH);
mov(:,:,1,curFrame) = rgb2ind(f.cdata, map, 'nodither');
for curIter = 1:nIters
curFrame = curFrame+1;
% Change label text
set(annH,'String',sprintf('Iteration %d',curIter));
% Update cluster point colors
scatterColors = colorMatrix(label{curIter+1},:);
% Update cluster centers:
for curDim=1:nDim
curDimVarName = genvarname(sprintf('dimData_%d',curDim));
eval([curDimVarName,'= m{curIter+1}(curDim,:);']);
end
% Update cluster colors:
nClusterIter = size(m{curIter+1},2);
clusterColors = colorMatrix(1:nClusterIter,:);
% Update graphics:
refreshdata(figH,'caller');
% Update cluster colors:
if nClusterIter~=size(m{curIter},2) % If number of cluster
% of current iteration differs from previous iteration (or start
% conditions in case we are at first iteration) we redraw colors:
pcolor([1:nClusterIter+1;1:nClusterIter+1])
% image(reshape(colorMatrix,[1 size(colorMatrix)])); % Used image to
% check if the upcomming buggy behaviour would be fixed. I was not
% lucky, though...
colormap(pColorAxes,clusterColors);
% Change XTick positions to its center:
set(pColorAxes,'XTick',.5:1:nClusterIter+.5);
set(pColorAxes,'YTick',[]);
% Change its label to cluster number:
set(pColorAxes,'XTickLabel',[nClusterIter 1:nClusterIter-1]);
xlabel(pColorAxes,'Clusters Colors','FontSize',plotOpts.FontSize);
end
f = getframe(figH);
mov(:,:,1,curFrame) = rgb2ind(f.cdata, map, 'nodither');
end
set(annH,'String','Convergence Conditions');
for curFrame = nIters+1:nIters+3
% Add three frames without movement at start conditions
f = getframe(figH);
mov(:,:,1,curFrame) = rgb2ind(f.cdata, map, 'nodither');
end
imwrite(mov, map, gifName, 'DelayTime',.5, 'LoopCount',inf)
varargout = cell(1,nargout);
if nargout > 0
varargout{1} = label;
if nargout > 1
varargout{2} = m;
if nargout > 2
varargout{3} = figH;
if nargout > 3
varargout{4} = handles;
end
end
end
end
end
function [leftPos,botPos,subplotWidth,subplotHeight] = ...
setCustomPlotArea(handles,plotOpts,horSpace,vertSpace)
%
% -> handles: axes handles
%
% -> plotOpts: struct holding the following fields:
%
% o leftBase: the percentage distance from the left
%
% o rightBase: the percentage distance from the right
%
% o bottomBase: the percentage distance from the bottom
%
% o topBase: the percentage distance from the top
%
% o widthUsableArea: Total width occupied by axes
%
% o heigthUsableArea: Total heigth occupied by axes
%
% -> horSpace: the axes units size (integers only) that current axes
% should occupy in the horizontal (considering that other occupied
% axes handles are empty)
%
% -> vertSpace: the axes units size (integers only) that current axes
% should occupy in the vertical (considering that other occupied
% axes handles are empty)
%
nHorSubPlot = size(handles,1);
nVertSubPlot = size(handles,2);
if nargin < 4
horSpace(nHorSubPlot,nVertSubPlot) = 0;
horSpace = horSpace+1;
if nargin < 3
vertSpace(nHorSubPlot,nVertSubPlot) = 0;
vertSpace = vertSpace+1;
end
end
subplotWidth = plotOpts.widthUsableArea/nHorSubPlot;
subplotHeight = plotOpts.heigthUsableArea/nVertSubPlot;
totalWidth = (1-plotOpts.rightBase) - plotOpts.leftBase;
totalHeight = (1-plotOpts.topBase) - plotOpts.bottomBase;
gapHeigthSpace = (totalHeight - ...
plotOpts.heigthUsableArea)/(nVertSubPlot);
gapWidthSpace = (totalWidth - ...
plotOpts.widthUsableArea)/(nHorSubPlot);
botPos(nVertSubPlot) = plotOpts.bottomBase + gapWidthSpace/2;
leftPos(1) = plotOpts.leftBase + gapHeigthSpace/2;
botPos(nVertSubPlot-1:-1:1) = botPos(nVertSubPlot) + (subplotHeight +...
gapHeigthSpace)*(1:nVertSubPlot-1);
leftPos(2:nHorSubPlot) = leftPos(1) + (subplotWidth +...
gapWidthSpace)*(1:nHorSubPlot-1);
for curLine=1:nHorSubPlot
for curColumn=1:nVertSubPlot
if handles(curLine,curColumn)
set(handles(curLine,curColumn),'Position',[leftPos(curColumn)...
botPos(curLine) horSpace(curLine,curColumn)*subplotWidth ...
vertSpace(curLine,curColumn)*subplotHeight]);
end
end
end
end
function [handles,horSpace,vertSpace] = ...
createAxesGrid(nLines,nColumns,plotOpts,dimLabels)
handles = zeros(nLines,nColumns);
% Those hold the axes size units:
horSpace(nLines,nColumns) = 0;
vertSpace(nLines,nColumns) = 0;
for curColumn=1:nColumns
for curLine=curColumn+1:nLines
handles(curLine,curColumn) = subplot(nLines,...
nColumns,curColumn+(curLine-1)*nColumns);
horSpace(curLine,curColumn) = 1;
vertSpace(curLine,curColumn) = 1;
curAxes = handles(curLine,curColumn);
if feature('UseHG2')
colormap(handle(curAxes),colorMatrix);
end
set(curAxes,'NextPlot','add',...
'FontSize',plotOpts.FontSize,'box','on');
if curLine==nLines
xlabel(curAxes,dimLabels{curColumn});
else
set(curAxes,'XTick',[]);
end
if curColumn==1
ylabel(curAxes,dimLabels{curLine});
else
set(curAxes,'YTick',[]);
end
end
end
end
Example
Here is an example using 5 dimensions, using the code:
center1 = [1; 0; 0; 0; 0];
center2 = [0; 1; 0; 0; 0];
center3 = [0; 0; 1; 0; 0];
center4 = [0; 0; 0; 1; 0];
center5 = [0; 0; 0; 0; 1];
center6 = [0; 0; 0; 0; 1.5];
center7 = [0; 0; 0; 1.5; 1];
data = [...
bsxfun(#plus,center1,.5*rand(5,20)) ...
bsxfun(#plus,center2,.5*rand(5,20)) ...
bsxfun(#plus,center3,.5*rand(5,20)) ...
bsxfun(#plus,center4,.5*rand(5,20)) ...
bsxfun(#plus,center5,.5*rand(5,20)) ...
bsxfun(#plus,center6,.2*rand(5,20)) ...
bsxfun(#plus,center7,.2*rand(5,20)) ...
];
[label,m,figH,handles]=kmeans_test(data,20);