Is it possible to subplot confusion matrices? - matlab

I'm plotting several confusion matrices using plot_confusion() function and I want to put them in a subplot (2x5 figures), but it does not seem to work.It displays every confusion matrix separately. Are there any restriction for plotting confusion? Thanks!
figure
Subplot(2,1,1);
plotconfusion(targets,outputs,'train');
subplot(2,1,2);
plotconfusion(targets1,outputs1,'test')

You're "not supposed" to do that (the functionality is not included), but you can trick Matlab a little, because at the end of a day it's just an axes object:
%% First Example Data
[x,t] = cancer_dataset;
net = patternnet(10);
net = train(net,x,t);
y = net(x);
%// plot
plotconfusion(t,y)
%// get handle and enable second plöt
cp1 = gcf;
cp1.NextPlot = 'new'
ax1 = findobj(cp1,'Type','Axes')
%% Second Example Data
[x,t] = cancer_dataset;
net = patternnet(5);
net = train(net,2*x,t);
y = net(x);
%// plot
plotconfusion(t,y)
%// get handle and enable third plöt
cp2 = gcf;
cp2.NextPlot = 'new'
ax2 = findobj(cp2,'Type','Axes')
%% combine plots
f1 = figure(42)
f1s1 = subplot(121)
copyobj(allchild(ax1),f1s1)
f1s2 = subplot(122)
copyobj(allchild(ax2),f1s2)
You loose the labels and titles and may need to adjust the axis, but I guess you're able to do that.

Related

Producing a histogram in Matlab with out using Hist

I am using histograms in Matlab to look at the distribution of some data from my experiments. I want to find the mean distribution (mean height of the bars) from a group of tests then produce an average histogram.
By using this code:
data = zeros(26,31);
for i = 1:length(files6)
x = csvread(files6(i).name);
x = x(1:end,:);
time = x(:,1);
variable = x(:,3);
thing(:,1) = x(:,1);
thing(:,2) = x(:,3);
figure()
binCenter = {0:tbinstep:tbinend 0:varbinstep:varbinend};
hist3(thing, 'Ctrs', binCenter, 'CDataMode','auto','FaceColor','interp');
colorbar
[N,C] = hist3(thing, 'Ctrs', binCenter);
data = data + N;
clearvars x time variable
end
avedata = data / i;
I can find the mean of N, which will be the Z value for the plot (histogram) I want, and I have X,Y (which are the same for all tests) from:
x = 0:tbinstep:tbinend;
y = 0:varbinstep:varbinend;
But how do I bring these together to make the graphical out that shows the average height of the bars? I can't use hist3 again as that will just calculate the distribution of avedata.
AT THE RISK OF STARTING AN XY PROBLEM using bar3 has been suggested, but that asks the question "how do I go from 2 vectors and a matrix to 1 matrix bar3 can handle? I.e. how do I plot x(1), y(1), avedata(1,1) and so on for all the data points in avedata?"
TIA
By looking at hist3 source code in matlab r2014b, it has his own plotting implemented inside that prepares data and plot it using surf method. Here is a function that reproduce the same output highly inspired from the hist3 function with your options ('CDataMode','auto','FaceColor','interp'). You can put this in a new file called hist3plot.m:
function [ h ] = hist3plot( N, C )
%HIST3PLOT Summary of this function goes here
% Detailed explanation goes here
xBins = C{1};
yBins = C{2};
% Computing edges and width
nbins = [length(xBins), length(yBins)];
xEdges = [0.5*(3*xBins(1)-xBins(2)), 0.5*(xBins(2:end)+xBins(1:end-1)), 0.5*(3*xBins(end)-xBins(end-1))];
yEdges = [0.5*(3*yBins(1)-yBins(2)), 0.5*(yBins(2:end)+yBins(1:end-1)), 0.5*(3*yBins(end)-yBins(end-1))];
xWidth = xEdges(2:end)-xEdges(1:end-1);
yWidth = yEdges(2:end)-yEdges(1:end-1);
del = .001; % space between bars, relative to bar size
% Build x-coords for the eight corners of each bar.
xx = xEdges;
xx = [xx(1:nbins(1))+del*xWidth; xx(2:nbins(1)+1)-del*xWidth];
xx = [reshape(repmat(xx(:)',2,1),4,nbins(1)); NaN(1,nbins(1))];
xx = [repmat(xx(:),1,4) NaN(5*nbins(1),1)];
xx = repmat(xx,1,nbins(2));
% Build y-coords for the eight corners of each bar.
yy = yEdges;
yy = [yy(1:nbins(2))+del*yWidth; yy(2:nbins(2)+1)-del*yWidth];
yy = [reshape(repmat(yy(:)',2,1),4,nbins(2)); NaN(1,nbins(2))];
yy = [repmat(yy(:),1,4) NaN(5*nbins(2),1)];
yy = repmat(yy',nbins(1),1);
% Build z-coords for the eight corners of each bar.
zz = zeros(5*nbins(1), 5*nbins(2));
zz(5*(1:nbins(1))-3, 5*(1:nbins(2))-3) = N;
zz(5*(1:nbins(1))-3, 5*(1:nbins(2))-2) = N;
zz(5*(1:nbins(1))-2, 5*(1:nbins(2))-3) = N;
zz(5*(1:nbins(1))-2, 5*(1:nbins(2))-2) = N;
% Plot the bars in a light steel blue.
cc = repmat(cat(3,.75,.85,.95), [size(zz) 1]);
% Plot the surface
h = surf(xx, yy, zz, cc, 'CDataMode','auto','FaceColor','interp');
% Setting x-axis and y-axis limits
xlim([yBins(1)-yWidth(1) yBins(end)+yWidth(end)]) % x-axis limit
ylim([xBins(1)-xWidth(1) xBins(end)+xWidth(end)]) % y-axis limit
end
You can then call this function when you want to plot outputs from Matlab's hist3 function. Note that this can handle non uniform positionning of bins:
close all; clear all;
data = rand(10000,2);
xBins = [0,0.1,0.3,0.5,0.6,0.8,1];
yBins = [0,0.1,0.3,0.5,0.6,0.8,1];
figure()
hist3(data, {xBins yBins}, 'CDataMode','auto','FaceColor','interp')
title('Using hist3')
figure()
[N,C] = hist3(data, {xBins yBins});
hist3plot(N, C); % The function is called here
title('Using hist3plot')
Here is a comparison of the two outputs:
So if I understand your question and code correctly, you are plotting the distribution of multiple experiments' data as histograms, then you want to calculate the average shape of all the previous histograms.
I usually avoid giving approaches the asker isn't explicitly asking for, but for this one I must comment that it is a very strange thing to do. I've never heard of calculating the average shape of multiple histograms before. So just in case, you could simply append all your experiment's data into a single variable, and plot a normalized histogram of that using histogram2. This code outputs a relative frequency histogram. (Other normalization methods)
% Append all data in a single matrix
x = []
for i = 1:length(files6)
x = [x; csvread(files6(i).name)];
end
% Plot normalized bivariate histogram, normalized
xEdges = 0:tbinstep:tbinend;
yEdges = 0:varbinstep:varbinend;
histogram2(x(:,1), x(:,3), xEdges, yEdges, 'Normalize', 'Probability')
Now, if you really are looking to draw the average shape of multiple histograms, then yes, use bar3. Since bar3 doesn't accept an (x,y) value argument, you can follow the other answer, or modify the XTickLabel and YTickLabel property to match whatever your bin range is, afterwards.
... % data = yourAverageData;
% Save axis handle to `h`
h = bar3(data);
% Set property of axis
h.XTickLabels = 0:tbinstep:tbinend;
h.YTickLabels = 0:varbinstep:varbinend;

How can I generate CDF from a large dataset in MATLAB?

I have 6 datasets each containing 10000 entries.
I want to plot their CDFs for comparison.
In MATLAB I am using the following code:
figure()
ksdensity(dataset1,'Support','positive','Function','cdf',...
'NumPoints',5)
xlabel('Error')
ylabel('CDF')
But I am not sure, is it the right way or wrong?
How can I do that?
I am getting the following figure.
Update:
This has been made even easier with cdfplot().
% MATLAB R2019a
% Example Data
X = wblrnd(2,3,50000,1);
Y = wblrnd(3,2,50000,1);
Z = wblrnd(2.5,2.5,50000,1);
Data = [X Y Z];
figure, hold on
for k = 1:size(Data,2)
h(k) = cdfplot(Data(:,k));
end
legend('show')
It looks like you've got the result you want except for the legend and markers. If you'd like more control of the plotting features, I'd suggest obtaining the necessary elements to plot from ksdensity using [f,xi] = ksdensity(x) then plotting separately.
% MATLAB R2019a
% Example Data
X = wblrnd(2,3,50000,1);
Y = wblrnd(3,2,50000,1);
Z = wblrnd(2.5,2.5,50000,1);
Data = [X Y Z];
NumPointCDF = 5; % Number of points to estimate CDF with
figure, hold on
for ii = 1:size(Data,2) % for each column of Data
[fii, xii] = ksdensity(Data(:,ii),'Support','positive','Function','cdf',...
'NumPoints',NumPointsCDF);
p(ii) = plot(xii,fii,'LineWidth',1.1,'Marker','.','MarkerSize',12);
end
legend('X','Y','Z')
Alternatively, you could just plot each first,
figure, hold on
for ii = 1:size(Data,2) % for each column of Data
[fii, xii] = ksdensity(Data(:,ii),'Support','positive','Function','cdf',...
'NumPoints',NumPointsCDF);
p(ii) = plot(xii,fii);
end
and then change the properties of each line later with p(1).foo (see here).
For example, one at a time: p(1).Marker = 's' % square
Or all at once:
% Update all properties using the object
for ii = 1:size(Data,2)
p(ii).Marker = '.'; % Adjust specific properties of p(ii) as needed
p(ii).LineWidth = 1.2;
end
Reference:
Graphics Object Properties
Access Property Values

Matlab multiple legends on one plot 2014b

I want to have multiple legends on one plot. This solution works perfectly before 2014b version. I am trying to figure out how to make this elegantly using handles, but so far no success. Any ideas are welcome.
Example in 2013b:
x = 1:50;
y1 = sin(x/2);
y2 = cos(x/2);
f = figure(1);
pl(1) = plot(x,y1,'g');hold on;
pl(2) = plot(x,y2,'r');
h1 = legend('eg1','eg2');
set(h1,'Location','NorthEast')
tmp = copyobj(h1,f);
h2 = legend(pl,'sin','line');
set(h2,'Location','SouthWest')
I do get something using
ax = gca;
tmp = copyobj([h1,ax],f);
but when I set the legend again, previous legend goes under the plot.
Thanks!
Matlab by default only allows one legend per axes, so what you would have to do is create a fake/empty secondary axis in order to get your legend. Mathworks help has a good example of this
Code to produce the below chart is here
x= 0:0.01:2*pi;
y = sin(x);
hl1 = line(x, y,'Color','k','LineStyle','--');
ax1 = gca;
set(ax1,'xlim',[0, 7],'ylim',[-1,
1],'XColor','k','YColor','k');
legend_handle1 = legend(' sin');
ax2 = axes('Position',get(ax1,'Position'),...
'xlim',[0, 7],'ylim',[-1,1],...
'Visible','off','Color','none');
hl2 = line(pi/2, 1,'Color','r','Marker', 'o','Parent',ax2);
hl3 = line(pi, 0,'Color','g','Marker', 'x','Parent',ax2);
legend_handle2 = legend('peak', 'zero');
set(legend_handle2, 'Color', 'none');
Here is a simple alternative approach
t = linspace(0,2*pi,200);
frequencies=1:3;
for w=frequencies;
y = sin(w*t);
plot(t,y)
hold on
end
legend("w = " + num2str(frequencies'));

Speed up creation of impoint objects

I have to create some draggable points on an axes. However, this seems to be a very slow process, on my machine taking a bit more than a second when done like so:
x = rand(100,1);
y = rand(100,1);
tic;
for i = 1:100
h(i) = impoint(gca, x(i), y(i));
end
toc;
Any ideas on speed up would be highly appreciated.
The idea is simply to provide the user with the possibility to correct positions in a figure that have been previously calculated by Matlab, here exemplified by the random numbers.
You can use the the ginput cursor within a while loop to mark all points you want to edit. Afterwards just click outside the axes to leave the loop, move the points and accept with any key.
f = figure(1);
scatter(x,y);
ax = gca;
i = 1;
while 1
[u,v] = ginput(1);
if ~inpolygon(u,v,ax.XLim,ax.YLim); break; end;
[~, ind] = min(hypot(x-u,y-v));
h(i).handle = impoint(gca, x(ind), y(ind));
h(i).index = ind;
i = i + 1;
end
Depending on how you're updating your plot you can gain a general speedup by using functions like clf (clear figure) and cla (clear axes) instead of always opening a new figure window as explained in this answer are may useful.
Alternatively the following is a very rough idea of what I meant in the comments. It throws various errors and I don't have the time to debug it right now. But maybe it helps as a starting point.
1) Conventional plotting of data and activating of datacursormode
x = rand(100,1);
y = rand(100,1);
xlim([0 1]); ylim([0 1])
f = figure(1)
scatter(x,y)
datacursormode on
dcm = datacursormode(f);
set(dcm,'DisplayStyle','datatip','Enable','on','UpdateFcn',#customUpdateFunction)
2) Custom update function evaluating the chosen datatip and creating an impoint
function txt = customUpdateFunction(empt,event_obj)
pos = get(event_obj,'Position');
ax = get(event_obj.Target,'parent');
sc = get(ax,'children');
x = sc.XData;
y = sc.YData;
mask = x == pos(1) & y == pos(2);
x(mask) = NaN;
y(mask) = NaN;
set(sc, 'XData', x, 'YData', y);
set(datacursormode(gcf),'Enable','off')
impoint(ax, pos(1),pos(2));
delete(findall(ax,'Type','hggroup','HandleVisibility','off'));
txt = {};
It works for the, if you'd just want to move one point. Reactivating the datacursormode and setting a second point fails:
Maybe you can find the error.

MATLAB - hold on for subplot between different figures (Solved, but may have issues on Version R2012a)

I'm new to MATLAB, and I've been searching around for what I'm trying to do, but the results don't fit quite well.
I'm graphing plots of variations of transfer functions, the code I've done is below:
omega = 3;
K = omega * omega;
for zeta = 0.1:0.1:2
sys = tf(K,[1 2*zeta*omega omega]);
figure();
subplot(1,2,1);
step(sys);
title('Step response');
[num,den] = tfdata(sys, 'v');
disp(den);
r = roots(den);
subplot(1,2,2);
%hold (subplot(1,2,2), 'on');
plot(real(r), imag(r), 'o');
title('Pole Locations in Complex Plane');
end
Each time the loop runs, it will create a new figure. The first subplot should be unique for every figure, but the second subplot should plot the accumulation of all points (roots of denominator of all transfer functions) from figures before it. I tried to use hold (subplot(1,2,2), 'on'); to keep the second subplot, but it didn't work. My thought is that because the subplots are different figures, hold on cannot be used.
How can I solve this problem? Any help will be great.
A solution is to use 'Tag' in your subplot. I am using your code to edit:
omega = 3;
K = omega * omega;
for zeta = 0.1:0.1:2
sys = tf(K,[1 2*zeta*omega omega]);
figure();
sb = subplot(1,2,1);
set(sb, 'Tag', 'daddy') % Something to tag with - memorable
step(sys);
title('Step response');
[num,den] = tfdata(sys, 'v');
disp(den);
r = roots(den);
sb = subplot(1,2,2);
set(sb, 'Tag', 'child')
sb = findobj('Tag','child'); % Use MATLAB methods to find your tagged obj
set(sb,'NextPlot','add'); % set 'NextPlot' property to 'add'
plot(real(r), imag(r), 'o');
title('Pole Locations in Complex Plane');
end
DOes this work for you? btw. This is also in MATLAB central. You should use that too.