MATLAB: Plot line color - matlab

I am trying to run the following function so that I can plot some points connected by a line, but the lines are not displaying in the color I want (white). I tried other colors as well, but the lines still won't show. I don't understand why. Can someone help?
function draw_constellations
figure
hold on
axis([0,100,0,100])
grid off
set(gcf,'color','k')
set(gca,'color','k')
axis square
while 1
[x,y,button] = ginput(1);
switch button
case 1 % left mouse
p = plot(x,y,'w-*');
case {'q','Q'} % on keyboard
break;
end
end

It is because x and y you used to plot are scalers. To plot lines, you will need to store all the x and y you get from ginput in vectors and plot the vectors:
[x,y,button] = ginput(1);
switch button
case 1 % left mouse
xs = [xs x];
ys = [ys y];
p = plot(xs,ys,'w-*');
% more code to go
end
However, if xs and ys are plotted everytime a new point is entered, you will have lines overlapping. To avoid this, we only plot the first point and update the p.XData and p.YData for new points:
if isempty(p)
p = plot(x,y,'w-*');
else
p.XData = xs;
p.YData = ys;
Full code:
figure
hold on
axis([0,100,0,100])
grid off
set(gcf,'color','k')
set(gca,'color','k')
axis square
xs = [];
ys = [];
p = [];
while 1
[x,y,button] = ginput(1);
switch button
case 1 % left mouse
xs = [xs x];
ys = [ys y];
if isempty(p)
p = plot(x,y,'w-*');
else
p.XData = xs;
p.YData = ys;
end
case {'q','Q'} % on keyboard
break;
end
end
Result:

You can use
plot(x,y,'*','color','blue') % plots in blue.
plot(x,y,'*','color',[.5 .4 .7]) % plots the RGB value [.5 .4 .7].
If you want lots of color names, you could use the rgb function to return the RGB values of just about any color. For example,
plot(x,y,'*','color',rgb('blood red'))
Further demonstration on how to change the default color order that you get when you plot lines without specifying the color:
Ever wonder how it plots blue first, then dark green, then red, then cyan, etc.? Ever want to change the default order so that it plots curves with the color order you want instead of the default color order, and without having to specify the color in every single call to plot()? If so, run the attached demo.
% Unless you specify the 'Color' property when you plot,
% plots are plotted according to the 'ColorOrder' property of the axes.
% This demo shows how you can change the default color order of plots.
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables.
workspace; % Make sure the workspace panel is showing.
fontSize = 18;
% Make 20 plots, with 25 data points in each plot.
numberOfDataSets = 20;
x = 1:25;
y = rand(numberOfDataSets, length(x));
% These y would all be on top of each other.
% Separate the plots vertically.
offsets = repmat((1:numberOfDataSets)', [1, length(x)]);
y = y + offsets;
% Get the initial set of default plot colors.
initialColorOrder = get(gca,'ColorOrder') % Initial
% See what the colors look like when plotted:
subplot(2, 1, 1);
plot(x,y, 'LineWidth', 3);
grid on;
caption = sprintf('%d plots with the Initial Default Color Order (Note the repeating colors)', numberOfDataSets);
title(caption, 'FontSize', fontSize);
xlabel('X', 'FontSize', fontSize);
ylabel('Y', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]); % Maximize figure.
% Give a name to the title bar.
set(gcf,'name','Image Analysis Demo','numbertitle','off')
choice = menu('Which ColorOrder do you want?', 'jet', 'random', 'hsv', 'hot', 'cool', 'spring', 'summer',...
'autumn', 'winter', 'lines', 'gray', 'bone', 'copper', 'pink');
% Make a new axes:
subplot(2, 1, 2);
% Create a new colormap that will define the new default color order property.
switch choice
case 1
newDefaultColors = jet(numberOfDataSets);
case 2
newDefaultColors = rand(numberOfDataSets, 3);
case 3
newDefaultColors = hsv(numberOfDataSets);
case 4
newDefaultColors = hot(numberOfDataSets);
case 5
newDefaultColors = cool(numberOfDataSets);
case 6
newDefaultColors = spring(numberOfDataSets);
case 7
newDefaultColors = summer(numberOfDataSets);
case 8
newDefaultColors = autumn(numberOfDataSets);
case 9
newDefaultColors = winter(numberOfDataSets);
case 10
newDefaultColors = lines(numberOfDataSets);
case 11
newDefaultColors = gray(numberOfDataSets);
case 12
newDefaultColors = bone(numberOfDataSets);
case 13
newDefaultColors = copper(numberOfDataSets);
otherwise
newDefaultColors = pink(numberOfDataSets);
end
% Note: You can build your own custom order if you want,
% just make up a array with numberOfDataSets rows and 3 columns
% where each element is in the range 0-1.
% Apply the new default colors to the current axes.
set(gca, 'ColorOrder', newDefaultColors, 'NextPlot', 'replacechildren');
% Now get the new set of default plot colors.
% Verify it changed by printing out the new default color set to the commandwindow.
newColorOrder = get(gca,'ColorOrder')
% Now plot the datasets with the changed default colors.
plot(x,y, 'LineWidth', 3);
grid on;
caption = sprintf('%d plots with the New Default Color Order', numberOfDataSets);
title(caption, 'FontSize', fontSize);
xlabel('X', 'FontSize', fontSize);
ylabel('Y', 'FontSize', fontSize);
msgbox('Done with ColorOrder demo!');
Special thanks/credit goes to this.

Related

Consulting waterfall chart matlab

I was trying to create "consulting" waterfall chart in matlab, and I am having a really difficult time in creating it. I was expecting actually that there would be a built in way of doing that.
Given this data:
x = [5, 2, -5, 8, 2, 12];
total = [1, 0, 0 ,0 ,0, 1];
I want to make a waterfall chart.
Basically, the vector x has the values for the chart and the vector total indicates whether the corresponding column is a total column or not.
So the first column, is a 5 and is a total column. The second column is a two and it is not (so it adds up). The third column is minus five so it subtracts, and so on and so forth until the last column which is a total again. Below how the figure would look like.
1) How to get this figure?
2) How to color increases, decreases and totals with different colors?
3) How to include the connecting lines?
Method 1
Here's one possible solution using MATLAB's bar function.
Assumptions:
The total columns are always the first and last columns.
The basic idea is to use the 'Baseline' property of a Bar object, which allows a particular bar to start from a specific value. For example, bar([1,3,5], 'BaseValue', 2) produces 3 bars that start from the value 2: the first going down by 1 unit, the second going up by 1 unit, and the last going up by 3 units.
From testing on R2019b, unfortunately it appears that all Bar objects on an Axes must share the same BaseValue. Thus, for each Bar object to have its own Baseline value, each of them must be on a separate Axes object. We can workaround this by overlaying a bunch of Axes (one for each Bar) on top of each other, making all but one of them transparent. This way all bars will be visible.
Anyways, here's the function. The inputs are
ax (optional): a handle to an existing Axes object. You may want to do this if you have other things plotted already, or if you want to manually set various properties of an Axes.
y: a vector of all the incremental values. Note: the final value is NOT required, i.e. to reproduce the plot in the question, use y=[5, 2, -5, 8, 2];
The function outputs the handles to each Bar object created. You may want this to further change the EdgeColor of the Bars.
function h = wfall(ax, y)
if nargin == 1
y = ax;
ax = gca;
end
if ~strcmp(ax.NextPlot, 'add')
fprintf('hold on not set for current axes. Overriding.\n');
hold(ax, 'on');
end
y = y(:); % column vector
n = length(y);
cumy = cumsum(y);
set(ax, 'XLim', [0, n+1]+0.5, 'YLim', [min(min(cumy), 0), max(max(cumy), 0)]);
% colors:
% decrease - red - code as -1
% total - black - code as 0
% increase - blue - code as 1
set(ax, 'CLim', [-1, 1], 'ColorMap', [1 0 0; 0 0 0; 0 0 1]);
% copy a bunch of axes
for i = 1:n
ax(i+1) = copyobj(ax(1), ax(1).Parent);
end
% Make all subsequent axes invisible
% Make sure all axes will always be the same size by linking properties
set(ax(2:end), 'Color', 'none', 'XColor', 'none', 'YColor', 'none');
linkprop(ax, {'XLim', 'YLim', 'Position', 'DataAspectRatio'});
% define from/to of each bar (except 1st and last)
from = cumy(1:n-1);
to = cumy(2:n);
% color of each bar (except 1st and last)
c = double(y>0) - double(y<0);
c(1) = [];
% first total bar
h = bar(ax(1), 1, from(1), 'CData', 0, 'BaseValue', 0);
% 2nd to 2nd last bars
for i = 1:n-1
h(end+1) = bar(ax(i+1), i+1, to(i), 'CData', c(i), 'BaseValue', from(i), 'ShowBaseLine', 'off');
end
% last total bar
h(end+1) = bar(ax(1), n+1, cumy(n), 'CData', 0);
% setting FaceColor flat makes the Bars use the CData property
set(h, 'FaceColor', 'flat')
Run the code as follows to produce the following plot.
close all;
ax = gca;
h = wfall(ax, y(1:end-1));
Method 2
Here's another solution if you prefer not to stack Axes objects on top of each other.
In this case, we make an additional assumption:
The cumulative value is never negative (this would apply, for example, the cash in my pocket)
Simply, each bar we draw can be considered as one colored bar (either blue/red) that is partially covered by a shorter white bar.
function h = wfall2(ax, y)
if nargin == 1
y = ax;
ax = gca;
end
if ~strcmp(ax.NextPlot, 'add')
fprintf('hold on not set for current axes. Overriding.\n');
hold(ax, 'on');
end
y = y(:); % column vector
n = length(y);
cumy = cumsum(y);
from = cumy(1:n-1);
to = cumy(2:n);
% color values:
% 1 - blue (increase)
% 0 - white
% -1 - red (decrease)
c = double(y>0) - double(y<0);
c(1) = [];
upper = max(cumy(1:n-1), cumy(2:n));
lower = min(cumy(1:n-1), cumy(2:n));
h(1) = bar(ax, 2:n, upper, 'FaceColor', 'flat', 'CData', c);
h(2) = bar(ax, 2:n, lower, 'FaceColor', 'w');
h(3) = bar(ax, 1, cumy(1), 'FaceColor', 'k');
h(4) = bar(ax, n+1, cumy(n), 'FaceColor', 'k');
set(h, 'EdgeColor', 'none')
set(ax, 'CLim', [-1, 1], 'ColorMap', [1 0 0; 0 0 0; 0 0 1]);
Run the function as follows:
close all;
ax = gca;
h = wfall2(ax, y(1:end-1));
The resulting plot:
The result, however, is a bit ugly by my personal standards, since the white bar will partially cover the x-axis. You can fix this, however, by setting the lower YLim to a negative value, i.e. set(ax, 'YLim', [-0.5 inf])

How to use togglebutton to turn labels in scatterplot on/off

I tried to create a scatter plot with labels for each point:
Now I would like to give the user of the code the possibility to turn the labels on and off.
So far my code looks like this:
x = rand(1,100); y = rand(1,100); pointsize = 30;
idx = repmat([1 : 10], 1, 10) % I used class memberships here
figure(1)
MDSnorm = scatter(x, y, pointsize, idx, 'filled');
dx = 0.015; dy = 0.015; % displacement so the text does not overlay the data points
T = text(x + dx, y +dy, labels);
colormap( jet ); % in my code I use a specific colormap
Button = uicontrol('Parent',figure(2),'Style','toggle','String',...
'labels','Units','normalized','Position',[0.8025 0.82 0.1 0.1],'Visible','on',...
'callback',{#pb_call, MDSnorm, ???});
At the end of my script I then tried to define the pb_call function. I tried out several different versions, they all failed.
I have a rough idea what I need to do. Something like:
function [] = pb_call( ??? )
if get(Button, 'Value')
T --> invisible % ???
else
T --> visible % ???
end
end
How can I modify the above to turn labels on or off as desired?
Here is a working example:
x = rand(1,9); y = rand(1,9); pointsize = 30;
idx = repmat(1 : 3, 1, 3); % I used class memberships here
labels = {'a', 'b', 'c', 'd', 'e', 'f', 'h', 'i', 'j'};
h_fig = figure(1); %Create a figure, an keep the handle to the figure.
MDSnorm = scatter(x, y, pointsize, idx, 'filled');
dx = 0.015; dy = 0.015; % displacement so the text does not overlay the data points
T = text(x + dx, y +dy, labels);
colormap( jet ); % in my code I use a specific colormap
%Add a button to the same figure (the figure with the labels).
Button = uicontrol('Parent',h_fig,'Style','toggle','String',...
'labels','Units','normalized','Position',[0.8025 0.82 0.1 0.1],'Visible','on',...
'callback',#pb_call);
function pb_call(src, event)
%Callback function (executed when button is pressed).
h_fig = src.Parent; %Get handle to the figure (the figure is the parent of the button).
h_axes = findobj(h_fig, 'Type', 'Axes'); %Handle to the axes (the axes is a children of the figure).
h_text = findobj(h_axes, 'Type', 'Text'); %Handle to all Text labels (the axes is the parent of the text labels).
%Decide to turn on or off, according to the visibility of the first text label.
if isequal(h_text(1).Visible, 'on')
set(h_text, 'Visible', 'off'); %Set all labels visibility to off
else
set(h_text, 'Visible', 'on'); %Set all labels visibility to on
end
end
Explanations are in the comments.

How to fix the legend of this boxplot in matlab?

The legend of this boxplot comes with the same color! How can I fix this? How can I move the x-axis lable a little bit lower ?Thanks for your help.
close all
clc;clear;
f=figure;
Temp_O=[-0.234115422389688;-0.153751688636750;3.03158128172032;-0.746185319551222;0.491616009046725;1.17490826218458;0.495331079652895;0.757394580248284;1.28467417069223;0.710444835069366;-0.979521722186138;-0.216850422633648;0.0596632891728577;-0.525362330358090;0.681608181821661;-0.995216710339821;-0.706416688978551;-0.147700048468633;-0.145946504735073;0.355209739265580;1.25860455564176;0.970569089382961;3.99404165520844;0.433235373567272;1.37023527554759;1.45032207715449;2.00968917969203;0.840884198707613;2.08558564237223;2.05435556980046;-15.5517060656394;3.18991806590028;1.28277879106186;2.15931490153483;3.19647581545030;2.97877640768595;0.0857405478541730;-1.59362648933500;-2.18109410889313;0.751077088333943;0.795072796032814;4.18896005388773;-0.591461781602054;-0.229818549439720];
position_O = 5:5:25;
position_O=position_O';
g = [ones(10,1); 2*ones(10,1); 3*ones(10,1) ;4*ones(10,1);5*ones(4,1)];
box_O = boxplot(Temp_O,g,'colors','b','positions',position_O,'width',0.8);
h=findobj(gca,'tag','Outliers');
delete(h)
set(gca,'XTickLabel',{' '})
hold on
Temp_S=[-0.234069549668875;-0.0803021151079149;0.166729084507040;-0.991371043478263;0.320651878289472;0.118699258741257;-0.190944834558825;0.540367970198674;1.02556298920863;0.112849364285713;-0.395341229166667;0.382362326388889;-1.40591456976744;0.247202120000001;-1.33262568333333;-1.27793610544218;0.0400995141843974;-1.32333150653595;-1.84221947163121;0.407607340136054;0.264276120300749;-0.337747273809525;1.03841878571429;-1.41048786507936;0.901727821428570;-1.03012908482143;2.69786876785714;-0.691010535714286;1.66913088345865;0.684260974489794;-10.3923539047619;1.04994314285714;2.13557031632653;3.87736348701299;7.38705700000000;0.0451628482142860;-3.69094742857143;-1.14071104081633;-3.15830153968254;-4.41399970408163;6.09908001655629;0.0267684861111112;-2.67854298170732;0.925146217948717;];
position_S = 6.8:5:26.8;
position_S=position_S';
box_S = boxplot(Temp_S,g,'colors','r','positions',position_S,'width',0.8);
h=findobj(gca,'tag','Outliers');
delete(h)
legend(findobj(gca,'Tag','Box'),'Group1','Group2')
set(gca,'XTickLabel',{' '}) ;
hold off
text('Position',[5,-11],'String','S')
text('Position',[10,-11],'String','M')
text('Position',[15,-11],'String','L')
text('Position',[20,-11],'String','V')
text('Position',[25,-11],'String','C')
xlabel('Types','FontSize',10);
% set(get(gca, 'XLabel'), 'Position', [0 .2 0]); %
ylim([-10.5 7.8]);
The issue is that you're only displaying the legend for the first two boxes (yours has a total of 10 boxes) and both of these are red. The first 5 boxes that are found are red and the last 5 are blue. Instead you could use the first and last box.
%// Create the box plot
box_S = boxplot(Temp_S, g, 'colors', 'r', 'positions', position_S, 'width', 0.8);
%// Get all of the box plot objects
boxes = findobj(gca, 'Tag', 'Box');
legend(boxes([end 1]), 'Group1', 'Group2')
You could do this more robustly though with the following:
boxes = findobj(gca, 'Tag', 'box');
%// Sort by xposition
[~, ind] = sort(cellfun(#mean, get(boxes, 'XData')));
%// Apply legends to one red and one blue one.
legend(boxes(ind(1:2)), 'Group1', 'Group2');
And to move the xlabel a little lower, you can simply adjust it's Position property.
yrange = diff(get(gca, 'YLim'));
XL = get(gca, 'XLabel');
original = get(XL, 'Position');
%// Add an extra 1% padding
set(XL, 'Position', original - [0 0.01*yrange 0])
Here is a small example (I only kept the necessary stuff):
x1 = randn(44,1);
x2 = randn(44,1);
pos1 = (5:5:25)';
pos2 = (6.8:5:26.8)';
g = repelem([1 2 3 4 5], [10 10 10 10 4]);
h1 = boxplot(x1, g, 'Colors','b', 'Positions',pos1, 'Width',0.8);
hold on
h2 = boxplot(x2, g, 'Colors','r', 'Positions',pos2, 'Width',0.8);
hold off
legend([h1(5,1),h2(5,1)], {'Group1','Group2'})
To quote help boxplot:
% H = BOXPLOT(...) returns the handle H to the lines in the box plot.
% H has one column per box, consisting of the handles for the various
% parts of the box. For the traditional plotstyle, the rows correspond
% to: upper whisker, lower whisker, upper adjacent value, lower adjacent
% value, box, median, and outliers. For the compact plotstyle, the rows
% correspond to: whiskers, box, median outer, median inner, and outliers.
% If median comparison intervals are indicated with markers, H will have
% two more rows for notch lo and notch hi. If medianstyle or boxstyle
% have been set explicitly, the meaning of the rows will adjust
% accordingly.

Add non-existent entry to legend

I want to add an entry manually to a MATLAB legend. This legend can be pre-existent and contain other graphed elements' entries, but not necessarily.
I make a scatter plot, but instead of using e.g. scatter(x,y), I plot it using
for n = 1:numel(x)
text(x(n),y(n),num2str(n), ...
'HorizontalAlignment','center','color',[1 0 0])
end
This results in a scatter plot of numbers one through the number of elements in x (and y, because they are of the same size). I want to add a legend entry for these numbers.
I tried to add or edit the legend with
[h,icons,plots,s] = legend(___)
as described on the legend documentation page. I can't figure out how I can add a legend entry, without having to plot something (such as an actual scatter plot or regular plot). I want the usual line or marker symbol in the legend to be a number or character such as 'n', indicating the numbers in the graph. Is this possible and how would one achieve this?
EDIT by Erik
My answer goes below zelanix's answer, because mine is based on it.
Original answer
A fairly workable solution may be as follows:
x = rand(10, 1);
y = rand(10, 1);
figure;
text(x,y,num2str(transpose(1:numel(x))),'HorizontalAlignment','center')
% Create dummy legend entries, with white symbols.
hold on;
plot(0, 0, 'o', 'color', [1 1 1], 'visible', 'off');
plot(0, 0, 'o', 'color', [1 1 1], 'visible', 'off');
hold off;
% Create legend with placeholder entries.
[h_leg, icons] = legend('foo', 'bar');
% Create new (invisible) axes on top of the legend so that we can draw
% text on top.
ax2 = axes('position', get(h_leg, 'position'));
set(ax2, 'Color', 'none', 'Box', 'off')
set(ax2, 'xtick', [], 'ytick', []);
% Draw the numbers on the legend, positioned as per the original markers.
text(get(icons(4), 'XData'), get(icons(4), 'YData'), '1', 'HorizontalAlignment', 'center')
text(get(icons(6), 'XData'), get(icons(6), 'YData'), '2', 'HorizontalAlignment', 'center')
axes(ax1);
Output:
The trick to this is that the new axes are created in exactly the same place as the legend, and the coordinates of the elements of the icons are in normalised coordinates which can now be used inside the new axes directly. Of course you are now free to use whatever font size / colour / whatever you need.
The disadvantage is that this should only be called after your legend has been populated and positioned. Moving the legend, or adding entries will not update the custom markers.
Erik's answer
Based on zelanix's answer above. It is a work-in-progress answer, I am trying to make a quite flexible function of this. Currently, it's just a script that you'd need to adapt to your situation.
% plot some lines and some text numbers
f = figure;
plot([0 1],[0 1],[0 1],[1 0])
x = rand(25,1);
y = rand(25,1);
for n = 1:numel(x)
text(x(n),y(n),num2str(n), ...
'HorizontalAlignment','center','color',[1 0 0])
end
hold on
% scatter(x,y) % used to test the number positions
scatter(x,y,'Visible','off') % moves the legend location to best position
% create the dummy legend using some dummy plots
plot(0,0,'o','Visible','off')
[l,i] = legend('some line','some other line','some numbers','location','best');
l.Visible = 'off';
% create empty axes to mimick legend
oa = gca; % the original current axes handle
a = axes;
axis manual
a.Box = 'on';
a.XTick = [];
a.YTick = [];
% copy the legend's properties and contents to the new axes
a.Units = l.Units; % just in case
a.Position = l.Position;
i = copyobj(i,a);
% replace the marker with a red 'n'
s = findobj(i,'string','some numbers');
% m = findobj(i(i~=s),'-property','YData','marker','o');
m = findobj(i(i~=s),'-property','YData');
sy = s.Position(2);
if numel(m)>1
dy = abs(m(1).YData - sy);
for k = 2:numel(m)
h = m(k);
dy2 = abs(h.YData - sy);
if dy2<dy
kbest = k;
dy = dy2;
end
end
m = m(kbest);
end
m.Visible = 'off';
mx = m.XData;
text(mx,sy,'n','HorizontalAlignment','center','color',[1 0 0])
% reset current axes to main axes
f.CurrentAxes = oa;
The result:

matlab bar colormap returns the same color for all bars

I have a problem with using bar and colormap.
I have a csv file like this which contains completion time for six tasks:
34,22,103,22,171,26
24,20,41,28,78,28
37,19,60,23,141,24
...
and I create a bar chart with of the means, and add the std variation errorbar.
res = csvread('sorting_results.csv');
figure();
y = mean(res)';
e = std(res);
hold on;
bar(y);
errorbar(y,e,'.r');
title('Sorting completion time');
ylabel('Completion time (seconds)');
xlabel('Task No.');
hold off;
colormap(summer(size(y,2)));
Why is the output like this? Why do the bars have the same color? And how do I put legends to the six bars?
A piece of code that does the magic. It doesn't use the canonical technique mentioned by #am304, as you will have a hard time setting up the legend with it. Here, for each one of the 6 input values, we plot a full 6 bars: one bar with the value and the remaining five set to zero.
x = rand(1,6); %create data
x_diag = diag(x); %zero matrix with diagonal filled with x
cmap = summer(6); %define colors to use (summer colomap)
figure('color','w','Render','Zbuffer'); %create figure
%bar plot for each x value
for ind_data = 1:length(x)
h_bar = bar( x_diag(ind_data, :)); %bar plot
set( get(h_bar,'children'), 'FaceVertexCData', cmap(ind_data,:) ) ; %color
hold on;
end
colormap('summer');
%legend-type info
hleg = legend( ('a':'f')' );
set(hleg, 'box', 'off');
%xticks info
set(gca, 'XTickLabel', ('a':'f')' );
%plot errors
e = ones(1,6) * 0.05;
errorbar(x, e,'.r');
set(gca, 'FontSize', 14, 'YLim', [ 0 (max(x) + max(e) + 0.1) ]);
See Coloring 2-D Bars According to Height in the MATLAB documentation. Only the first colour of the colormap is used to colour the faces, you need a bit of hack (as per code on that doc page) to do what you want.