I want to control the legends in MATLAB - matlab

Ask for help, how to make legend in matlab not display text, only display color?
As shown in the figure, i want to remove the text between each dashline but remain the legends, moreover, the legend with the box also needs to be preserved.
And I want the above figure to become like this.
%This is my code
%%
t = 0:0.01:1;
y1 = sin(t);
y2 = cos(t);
y3 = sin(t)+cos(t);
h = plot(t,y1,t,y2,t,y3);
legend('y1','y2','y3')
ah=axes('position',get(gca,'position'),'visible','off');
h1 = legend(ah,h([1 3]),'y1','y3');
h1.ItemTokenSize = [10,80];
set(h1,'FontSize',10,'position',[0.6,0.2,0.01,0.01],'units','normalized');
set(h1,'box','off')

The solution is a bit hacky, but basically what you want to do is make 2 plots, and hide one of them, having each of the plots a different legend.
t = 0:0.01:1;
y1 = sin(t);
y2 = cos(t);
y3 = sin(t)+cos(t);
% plot twice
h = plot(t,y1,t,y2,t,y3);
hold on
% reset colors
set(gca,'ColorOrderIndex',1)
h2 = plot(t,y1,t,y2,t,y3);
% make first legend
legend(h,'y1','y2','y3')
% hide the latest axes
ah=axes('position',get(gca,'position'),'visible','off');
% add new legend in hidden second axis/plot
l1 = legend(ah,h2([1 3]),' ',' ');
% these next lines are not part of the solution, just your demo
l1.ItemTokenSize = [10,80];
set(l1,'FontSize',10,'position',[0.6,0.2,0.01,0.01],'units','normalized');
set(l1,'box','off')

Related

Matlab: patch area between two curves which depend on the curves values

I'm trying to fill an area between two curves with respect to a function which depends on the values of the curves.
Here is the code of what I've managed to do so far
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
N=[n_vec,fliplr(n_vec)];
X=[x_vec,fliplr(y_vec)];
figure(1)
subplot(2,1,1)
hold on
plot(n_vec,x_vec,n_vec,y_vec)
hp = patch(N,X,'b')
plot([n_vec(i) n_vec(i)],[x_vec(i),y_vec(i)],'linewidth',5)
xlabel('n'); ylabel('x')
subplot(2,1,2)
xx = linspace(y_vec(i),x_vec(i),100);
plot(xx,cc(xx,y_vec(i),x_vec(i)))
xlabel('x'); ylabel('c(x)')
This code produces the following graph
The color code which I've added represent the color coding that each line (along the y axis at a point on the x axis) from the area between the two curves should be.
Overall, the entire area should be filled with a gradient color which depends on the values of the curves.
I've assisted the following previous questions but could not resolve a solution
MATLAB fill area between lines
Patch circle by a color gradient
Filling between two curves, according to a colormap given by a function MATLAB
NOTE: there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
The surf plot method
The same as the scatter plot method, i.e. generate a point grid.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
Generate a logical array indicating whether the points are inside the polygon, but no need to extract the points:
in = inpolygon(px, py, N, X);
Generate Z. The value of Z indicates the color to use for the surface plot. Hence, it is generated using the your function cc.
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
Set Z values for points outside the area of interest to NaN so MATLAB won't plot them.
pz(~in) = nan;
Generate a bounded colourmap (delete if you want to use full colour range)
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
Finally, plot.
figure;
colormap(jet)
surf(px,py,pz,'edgecolor','none');
view(2) % x-y view
Feel free to turn the image arround to see how it looks like in the Z-dimention - beautiful :)
Full code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
% generate z
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
pz(~in) = nan;
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
% plot
figure;
colormap(c)
surf(px,py,pz,'edgecolor','none');
view(2)
You can use imagesc and meshgrids. See comments in the code to understand what's going on.
Downsample your data
% your initial upper and lower boundaries
n_vec_long = linspace(2,10,1000000);
f_ub_vec_long = linspace(2, 10, length(n_vec_long));
f_lb_vec_long = abs(sin(n_vec_long));
% downsample
n_vec = linspace(n_vec_long(1), n_vec_long(end), 1000); % for example, only 1000 points
% get upper and lower boundary values for n_vec
f_ub_vec = interp1(n_vec_long, f_ub_vec_long, n_vec);
f_lb_vec = interp1(n_vec_long, f_lb_vec_long, n_vec);
% x_vec for the color function
x_vec = 0:0.01:10;
Plot the data
% create a 2D matrix with N and X position
[N, X] = meshgrid(n_vec, x_vec);
% evaluate the upper and lower boundary functions at n_vec
% can be any function at n you want (not tested for crossing boundaries though...)
f_ub_vec = linspace(2, 10, length(n_vec));
f_lb_vec = abs(sin(n_vec));
% make these row vectors into matrices, to create a boolean mask
F_UB = repmat(f_ub_vec, [size(N, 1) 1]);
F_LB = repmat(f_lb_vec, [size(N, 1) 1]);
% create a mask based on the upper and lower boundary functions
mask = true(size(N));
mask(X > F_UB | X < F_LB) = false;
% create data matrix
Z = NaN(size(N));
% create function that evaluates the color profile for each defined value
% in the vectors with the lower and upper bounds
zc = #(X, ub, lb) 1 ./ (1 + (exp(-X) ./ (exp(-ub) - exp(-lb))));
CData = zc(X, f_lb_vec, f_ub_vec); % create the c(x) at all X
% put the CData in Z, but only between the lower and upper bound.
Z(mask) = CData(mask);
% normalize Z along 1st dim
Z = normalize(Z, 1, 'range'); % get all values between 0 and 1 for colorbar
% draw a figure!
figure(1); clf;
ax = axes; % create some axes
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
xlabel('n')
ylabel('x')
This already looks kinda like what you want, but let's get rid of the blue area outside the boundaries. This can be done by creating an 'alpha mask', i.e. set the alpha value for all pixels outside the previously defined mask to 0:
figure(2); clf;
ax = axes; % create some axes
hold on;
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
% set a colormap
colormap(flip(hsv(100)))
% set alpha for points outside mask
Calpha = ones(size(N));
Calpha(~mask) = 0;
sc.AlphaData = Calpha;
% plot the other lines
plot(n_vec, f_ub_vec, 'k', n_vec, f_lb_vec, 'k' ,'linewidth', 1)
% set axis limits
xlim([min(n_vec), max(n_vec)])
ylim([min(x_vec), max(x_vec)])
there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
It is difficult to achieve this using patch.
However, you may use scatter plots to "fill" the area with coloured dots. Alternatively, and probably better, use surf plot and generate z coordinates using your cc function (See my seperate solution).
The scatter plot method
First, make a grid of points (resolution 500*500) inside the rectangular space bounding the two curves.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
figure;
scatter(px(:), py(:), 1, 'r');
The not-interesting figure of the point grid:
Next, extract the points inside the polygon defined by the two curves.
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
hold on;
scatter(px, py, 1, 'k');
Black points are inside the area:
Finally, create color and plot the nice looking gradient colour figure.
% create color for the points
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s'); % use size 16, filled square markers.
Note that you may need a fairly dense grid of points to make sure the white background won't show up. You may also change the point size to a bigger value (won't impact performance).
Of cause, you may use patch to replace scatter but you will need to work out the vertices and face ids, then you may patch each faces separately with patch('Faces',F,'Vertices',V). Using patch this way may impact performance.
Complete code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate point grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
% generate color
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s');

Is it possible to subplot confusion matrices?

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.

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'));

Overlay Bar Plots with Line Plot in matlab with 2 y-axis by Creating 2nd axis

I know there is a lot of similar questions on Stack-Overflow but I didn't find the one that I am specifically interested so I am going to ask again.
I am creating a graph where a line plot overlays with a stacked bar plot. I am NOT using plotyy. I am following the instruction provided here by creating a second axis system: http://www.mathworks.com/help/matlab/ref/plotyy.html
Basically, I plot my first data set, get the location of current axis, create a second axis at the same location, move y-axis to the right, x-axis to the top, and plot my second/third data set based on the new axis.
My data set is basically:
x
y1 (axis 1),y2,y3 (axis 2)
While I was able to plot y1,y2,y3 at two axis using all line style, I cannot get it to work with y2 and y3 being bar style. The second axis is somehow stuck with the first axis, instead moving to top-right. Also the first data set line just disappears.
Another small question I also have is how to remove the x-axis for the second axis (since they are essentially the same). I searched online and they said to set xtick to []. But I am getting error: Invalid or deleted object with command
set(ax1,'YTick',[])
Thank you very much.
As pointed out I don't have code uploaded, here you go ;)
% this script predicts
% user prompt
prompt = {'Stock Name:','Cost per share($):','Current Value ($):','Holdings (shares):','Est. High ($)','Tolerance ($):'};
user_input = inputdlg(prompt);
% process user input
if isempty(user_input)
stockname = 'APPLE.INC';
x0 = 125.82;
xn = 129.91;
N0 = 80;
xt = 135;
tol = 20;
else
[stockname,x0,xn,N0,xt,tol] = user_input{:};
x0 = str2num(x0);
xn = str2num(xn);
xt = str2num(xt);
N0 = str2num(N0);
tol = str2num(tol);
end
% calculate sale-rebuy threshold
xt = linspace(x0-tol,xt+tol,10);
[x0,xn,y,N0,Ny] = sale_rebuy(x0,xn,xt,N0);
profit_rebuy = Ny.*(xt-y);
profit_nosale = N0*(xt-x0);
% plotting
figure
line(xt,y,'Color','r','LineStyle','--');
ax1=gca;
set(ax1,'XColor','r');
set(ax1,'YColor','r');
ax1_pos = get(ax1,'Position');
ax2 = axes('Position',ax1_pos,...
'XAxisLocation','top',...
'YAxisLocation','right',...
'Color','none');
profit = [profit_rebuy;profit_nosale]';
%bar(ax2,xt,profit,'stacked');
line(xt,profit_rebuy,'Parent',ax2,'Color','k','LineStyle',':');
title(stockname);
xlabel(ax1,'final price');
xlabel(ax2,'final price');
ylabel(ax1,'rebuy price');
ylabel(ax2,'profit');
% This is the function
function [x0,xn,y,N0,Ny] = sale_rebuy(x0,xn,xt,N0)
y = (xn.*xt)./(xt-x0+xn);
Ny = xn.*N0./y;
x0 = x0;
xn = xn;
N0 = N0;
end

Nicer errorbars when multiple data is shown

So I need to plot some errobar plots in a figure. Specifically I need 4 errorbar plots in each figure, the problem is that the figure gets a bit unreadable when several data is plotted.
Example:
clear all
close all
clc
x = 0:pi/10:pi;
y = sin(x);
y2=cos(x);
y3=atan(x);
e = std(y)*ones(size(x));
e2 = std(y2)*ones(size(x));
e3 = std(y3)*ones(size(x));
figure
hold on
errorbar(x,y,e)
errorbar(x,y2,e2)
errorbar(x,y3,e3)
My idea to solve the problem is to fill the area that the corners of the errorbars delimit with the same color of the plot and low alpha, so the overlapping of the areas is visible.
The problem is that the only way I can imagine of doing this is to create a mesh in the area delimited by the errorbar corners and then fill them with patch. This is indeed possible, but quite annoying, as a plot will not have a convex hull, therefore I will need to iteratively go creating the triangles one by one. So the question is : Is there a more elegant way of doing this?
Additionally, I am open to suggestions of a better way of visualizing this data, if anyone has.
Approach 1
Plot the graphs normally, and then plot the errorbars manually using patches. The data for the patches (coordinates and color) is taken from the plotted graphs, and the alpha of the patch can be set to any desired value.
clear all
close all
clc
error_alpha = .4;
error_width_factor = .01;
x = 0:pi/10:pi;
y = sin(x);
y2 = cos(x);
y3 = atan(x);
e = std(y)*ones(size(x));
e2 = std(y2)*ones(size(x));
e3 = std(y3)*ones(size(x));
ee = [e; e2; e3];
figure
hold on
hp(1) = plot(x,y);
hp(2) = plot(x,y2);
hp(3) = plot(x,y3);
w = diff(xlim)*error_width_factor;
for m = 1:numel(hp)
for n = 1:numel(hp(m).XData)
patch(hp(m).XData(n)+[-1 1 1 -1]*w, hp(m).YData(n)+[-1 -1 1 1]*ee(m,n), 'w',...
'FaceColor', hp(m).Color, 'FaceAlpha', error_alpha, 'EdgeColor', 'none');
end
end
Approach 2
Similar as before, but use narrower patches and plot them with a graph-dependent horizontal shift (as suggested by #Patrik). Applying an alpha value helps keep the figure lighter.
The code is a modified version of that of approach 1. The example shown here contains 101 data values, and is still rather visible.
clear all
close all
clc
error_alpha = .4;
error_width_factor = .003;
x = 0:pi/50:pi;
y = sin(x);
y2 = cos(x);
y3 = atan(x);
e = std(y)*ones(size(x));
e2 = std(y2)*ones(size(x));
e3 = std(y3)*ones(size(x));
ee = [e; e2; e3];
figure
hold on
hp(1) = plot(x,y);
hp(2) = plot(x,y2);
hp(3) = plot(x,y3);
w = diff(xlim)*error_width_factor;
m0 = (numel(hp)+1)/2;
for m = 1:numel(hp)
for n = 1:numel(hp(m).XData)
patch(hp(m).XData(n)+[-1 1 1 -1]*w+w*(m-m0),...
hp(m).YData(n)+[-1 -1 1 1]*ee(m,n), 'w', 'FaceColor', hp(m).Color, ...
'FaceAlpha', error_alpha, 'EdgeColor', 'none');
end
end