How to set the custom marker in MATLAB figure legend - matlab

I have an image as follows.
In this images you can see 3 lines, these are actually 6 lines, one line in invisible just for showing markers and other line is smoothened version of original data points. Now the issue is that how do I get the markers in legend. In this case, you can see the figure legend consists of just line, not the markers. My code is as follows.
clc; clear all ;
colour_green = [12 195 82] ./ 255;
colour_lightgreen = [94 250 81] ./ 255;
colour_lightblue = [8 180 238] ./ 255;
colour_darkblue = [1 17 181] ./ 255;
colour_peach = [251 111 66] ./ 255;
figure('DefaultAxesFontSize',30);
set(0,'DefaultAxesFontName',' Times ');
hold on
time_window = xlsread('pattern_data.xlsx', 'effect_of_count', 'A2:A12');
count1plus = xlsread('pattern_data.xlsx', 'effect_of_count', 'B2:B12');
count10plus = xlsread('pattern_data.xlsx', 'effect_of_count', 'C2:C12');
count1to5 = xlsread('pattern_data.xlsx', 'effect_of_count', 'D2:D12');
x1 = 50:1:99;
% x1 = .01:.01:.5;
x2 = interp1(time_window,count1plus,x1, 'pchip') ;
x3 = interp1(time_window,count10plus,x1, 'pchip') ;
x4 = interp1(time_window,count1to5,x1, 'pchip') ;
% count 1+
plot(x1,x2,'b--','DisplayName', 'Count_{A} = 1: and Count_{B} = 1:','LineWidth',3)
plot(time_window,count1plus,'bs', 'HandleVisibility','off','LineWidth',5)
% count 1:5
plot(x1,x4,'-','DisplayName', 'Count_{A} = 1: and Count_{B} = 5:','LineWidth',3 , 'Color', colour_green)
plot(time_window,count1to5,'^', 'HandleVisibility','off','LineWidth',5 , 'Color', colour_green)
% count 10+
plot(x1,x3,'r--','DisplayName', 'Count_{A} = 1: and Count_{B} = 10:','LineWidth',3)
plot(time_window,count10plus,'ro', 'HandleVisibility','off','LineWidth',5)
hold off
xlabel('Th_{B} ')
ylabel('L (milliseconds)')
legend('Location','north')
legend show
set(gcf, 'PaperUnits', 'normalized');
set(gcf, 'PaperPosition', [0 0 1 1]);
set(gcf,'PaperOrientation','l');
print -dpng graphs/p1_effect_of_count_and_selB;
Please help. What I want is to have a marker(circle, square etc.) in the respective legend.
Edit# 1
The solution provided here did not solve my problem as it did not illustrates how to add a marker. An animation shows how to customize the position of a marker, but here I want to add a new marker not re-position the original existing one.

As explained in this answer to a very similar question, since R2014b onward the legend object is kind of opaque and cannot be easily changed. However, that answer also shows that there is a syntax to the legend function with four output arguments, which creates the legend in a different way such that it can be modified. The answer also shows how to modify the legend. We'll follow their lead.
The documentation says about this syntax:
This syntax is not recommended. It creates a legend that does not support some functionality, such as adding a legend title. Also, the legend does not automatically update when you add or remove data series from the axes.
But in this case these issues don't bother us, so we'll continue along.
In the case of the graph in the question, we'll replace
legend('Location','north')
with
[lgd,icons,plots,txt] = legend('Location','north');
Now, icons contains handles to the objects that form the legend:
>> icons
icons =
9×1 graphics array:
Text (Count_{A} = 1: and Count_{B} = 1:)
Text (Count_{A} = 1: and Count_{B} = 5:)
Text (Count_{A} = 1: and Count_{B} = 10:)
Line (Count_{A} = 1: and Count_{B} = 1:)
Line (Count_{A} = 1: and Count_{B} = 1:)
Line (Count_{A} = 1: and Count_{B} = 5:)
Line (Count_{A} = 1: and Count_{B} = 5:)
Line (Count_{A} = 1: and Count_{B} = 10:)
Line (Count_{A} = 1: and Count_{B} = 10:)
The display even helpfully shows which elements belong to which item. The first three are text objects, the last 6 are line objects. These line objects are what we need to modify.
Why are there two line objects for each of the items shown in the legend? That is because there is one line object (the first one) that is the rendered line (it has two data points):
>> icons(4)
ans =
Line (Count_{A} = 1: and Count_{B} = 1:) with properties:
Color: [0 0 1]
LineStyle: '--'
LineWidth: 3
Marker: 'none'
MarkerSize: 6
MarkerFaceColor: 'none'
XData: [0.0108 0.0919]
YData: [0.8246 0.8246]
ZData: [1×0 double]
and the other that is the marker (currently not visible, it has one data point):
>> icons(5)
ans =
Line (Count_{A} = 1: and Count_{B} = 1:) with properties:
Color: [0 0 1]
LineStyle: 'none'
LineWidth: 3
Marker: 'o'
MarkerSize: 6
MarkerFaceColor: 'none'
XData: 0.0514
YData: 0.8246
ZData: [1×0 double]
So, what we need to do is set these marker objects:
icons(5).Marker = 's';
icons(7).Marker = '^';
icons(9).Marker = 'o';
Now the plot looks like you wanted.
I hope that the description above is clear enough that you can now change the legend in other ways too.

Related

Categorical histogram labels

I've created a categorical histogram :
h = histogram( categorical( emotions{startIndex:endIndex,:} ) )
h.Categories
ans =
1×5 cell array
{'ANGER'} {'CONTEMPT'} {'DISGUST'} {'JOY'} {'SADNESS'}
>> h.Values
ans =
164 26 18 191 1
But there doesn't seem to be a way to display labels (i.e.h.Values) on the histogram bars.
Following this post, I tried this:
text(h.Values, h.Categories, num2str(h.Values'), 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
but I just get:
Error using text First two or three arguments must be numeric doubles.
but the problem is my x values are never going to be numeric, they're categorical. How to fix it?
For a reproducible example, this would do:
emotions = { 'JOY','JOY','JOY','JOY','JOY','JOY','JOY','JOY','JOY','JOY','ANGER','ANGER','CONTEMPT','CONTEMPT','CONTEMPT','JOY','ANGER','ANGER','ANGER','ANGER'}
emotions = emotions.';
t = cell2table(emotions)
histogram(categorical(emotions))
You have interchanged the first two input arguments of text and h.Categories is not numeric double which it needs to be (as the error message is suggesting you).
So the solution is:
emotions = {'JOY','JOY','JOY','JOY','JOY','JOY','JOY','JOY','JOY', ...
'JOY','ANGER','ANGER','CONTEMPT','CONTEMPT','CONTEMPT','JOY', ...
'ANGER','ANGER','ANGER','ANGER'};
% emotions = emotions.'; %No need of this line
% t = cell2table(emotions); %No need of this line
h = histogram(categorical(emotions));
text(1:numel(h.Values), h.Values, num2str(h.Values.'), ...
'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom');
Result:

PCA with colorbar

I have this data of which I want to make a principal component analysis.
In particular for each data point I want to associate a color.
This is my code:
for ii=1:size(SBF_ens,1)
SBF(ii) = SBF_ens(ii, max(find(SBF_ens(ii,:)~=0)) ); %value at the moment of the measurement
end
%matrix of data
toPCA =[
wind_trend_72h_ens-nanmean(wind_trend_72h_ens);
wind_trend_24h_ens-nanmean(wind_trend_24h_ens);
wind_trend_12to18h_ens-nanmean(wind_trend_12to18h_ens);
wind_trend_0to12h_ens-nanmean(wind_trend_0to12h_ens);
wind_trend_last6h_ens-nanmean(wind_trend_last6h_ens);
Mwind12h_ens-nanmean(Mwind12h_ens);
Mwind24h_ens-nanmean(Mwind24h_ens);
SBF-nanmean(SBF)]';
variables = { 'wt72h','wt24h','wt12to18h','wt0to12h','wtLast6h','Mw12h', 'Mw24h', 'SBF'}; %labels
%PCA algorithm
C = corr(toPCA,toPCA);
w = 1./var(toPCA);
[wcoeff,score,latent,tsquared,explained] = pca(toPCA,'VariableWeights',w);
coefforth = diag(sqrt(w))*wcoeff;
metric=decstd_ens; %metric for colorbar
hbi=biplot(coefforth(:,1:2),'scores',score(:,1:2),'varlabels',...
variables,'ObsLabels', num2str([1:length(toPCA)]'),...
'markersize', 15);
%plotting
cm = autumn;
colormap(cm);
for ii = length(hbi)-length(toPCA):length(hbi)
userdata = get(hbi(ii), 'UserData');
if ~isempty(userdata)
indCol = ceil( size(cm,1) * abs(metric(userdata))/max(abs(metric)) );%maps decstd between 0 and 1 and find the colormap index
if indCol==0 %just avoid 0
indCol=1;
end
col = cm(indCol,:); %color corresponding to the index
set(hbi(ii), 'Color', col); %modify the dot's color
end
end
for ii = 1:length(hbi)-length(toPCA)-1 %dots corresponding to the original dimensions are in black
set(hbi(ii), 'Color', 'k');
end
c=colorbar;
ylabel(c,'decstd') %is this true?
xlabel(['1^{st} PCA component ', num2str(explained(1)), ' of variance explained'])
ylabel(['2^{nd} PCA component ', num2str(explained(2)), ' of variance explained'])
The resulting figure is the following:
Everything is fine except for the colorbar range. In fact decstd has values between 0 and 2. Actually I do not understand at all what the values on the colorbar are.
Does anyone understand it?
Is it possible to rethrive the data in the colorbar? So to understand what they are?
size(autumn)
shows you that the default length of the autumn colourmap (actually of all the colourmaps) is 64. When you call colorbar, by default it will use tick labels from 1 to n where n is the length of your colourmap, in this case 64.
If you want the mapping of the colourbar ticklabels to match the mapping that you used to get your data to fit between 1 and 64 (i.e. this line of yours indCol = ceil( size(cm,1) * abs(metric(userdata))/max(abs(metric)) );), then you will need to set that yourself like this
numTicks = 6;
cAxisTicks = linspace(min(metric), max(metric), numTicks); % or whatever the correct limits are for your data
caxis([min(metric), max(metric)]);
set(c,'YTick', cAxisTicks );

Add legend to graph created iteratively

I am creating a graph iteratively from various data sets, and I try to add legend to it, but it fails.
colors = {'g','b','m','k','c'};
subplot(2,3,iii);
hold all;
for jjj=1:length(aggregation_methods),
aggregate = all_aggregate{jjj};
std_agg = all_std_agg{jjj};
plot(coverages, aggregate, colors{jjj});
h1 = errorbar(coverages,aggregate,std_agg,colors{jjj});
set(h1,'linestyle','none')
end
plot(coverages, regular, 'r');
h1 = errorbar(coverages,regular,std_reg,'r');
set(h1,'linestyle','none')
title(datasets{iii});
xlabel('coverage');
ylabel('MSE');
legend('enhanced with clustering(k=4)','enhanced random split', 'regular we');
The graph seems to be generated just fine, it's just the legend that is failing.
I expect the colors of the legend to be (top-down): green, blue, red.
P.S. I also tried (and it didn't work):
legend({'enhanced with clustering(k=4)','enhanced random split', 'regular we'});
EDIT:
Inspired by this thread, I changed the code to use the 'DisplayName' property for each plot:
colors = {'g','b','m','k','c'};
names = {'enhanced with clustering(k=4)','enhanced random split', 'regular we'};
subplot(2,3,iii);
hold all;
for jjj=1:length(aggregation_methods),
aggregate = all_aggregate{jjj};
std_agg = all_std_agg{jjj};
plot(coverages, aggregate, colors{jjj}, 'DisplayName', names{jjj});
h1 = errorbar(coverages,aggregate,std_agg,colors{jjj});
set(h1,'linestyle','none')
end
%plot(coverages,aggregate,'g',coverages ,regular,'r');
%h1 = errorbar(coverages,aggregate,std_agg,'g');
%set(h1,'linestyle','none')
plot(coverages, regular, 'r', 'DisplayName', names{length(aggregation_methods) + 1});
h1 = errorbar(coverages,regular,std_reg,'r');
set(h1,'linestyle','none')
%axis([0 1 0 max(mean(rc{iii}))]);
title(datasets{iii});
xlabel('coverage');
ylabel('MSE');
legend('show');
However, it yields the following legend, but I'd like to get rid of these 'dataX' strings.
a=[
1 2 3
4 5 6
7 8 9
];
clist={'g','b','m'};
for i=1:3
pt(i)=plot(a(i,:),'Color',clist{i});
hold on;
end
b=[5 6 7];
pt(4)=plot(b,'Color','r');
legend([pt(1:2) pt(4)],{'line1','line2','line4'});

can I draw a line with different color by matlab in kml file format

I need to write line in kml file format with malab, this line is multiple color according to signal level or multiple line discontinuity?
by using 'kmlwriteline' it possible to write a line with one color, but I need to draw thie line with different color according to signal level or between to 3 color red,green,yellow
thank you .
There are 2 ways to do that. First one is, you can get the function here or kmlwrite()function and can use its color specification.
Second one is you can generate your one colour line and can open the kml file with Google Earth application then you can change the colour of line by right clicking on lines. The second one is manual way.
Edit: I see your point. You must create geoshape objects to perform your task briefly. If you want to segment the current line you must use NaN value.
Now try to code below:
% % // import your excel file
lat = xlsread('hossain1.xlsx','data','A2:A445');
lon = xlsread('hossain1.xlsx','data','B2:B445');
Rx_Level = xlsread('hossain1.xlsx','data','C2:C445');
% Initializing some stuff
Rx = [-60 -70 -80 -90 -100];
color = ['g', 'y', 'r', 'k']; % // I adjusted the colour wrt your uploaded pic.
width = 12 : -3 : 3; % // In the pic, Rx levels have a different witdh lines.
% // Existing Geographic shape object
lines = geoshape();
len = length(lat);
for u = 2 : length(Rx)
indexWrtRxLevel = ( Rx_Level >= Rx(u) ) & ( Rx_Level < Rx(u-1) );
lines(u-1).Latitude = zeros(1, len);
lines(u-1).Longitude = zeros(1, len);
indexOfZeros = find(indexWrtRxLevel == 0);
indexOfOnes = find(indexWrtRxLevel == 1);
lines(u-1).Latitude( indexOfZeros ) = NaN;
lines(u-1).Longitude( indexOfZeros ) = NaN;
lines(u-1).Latitude( indexOfOnes ) = lat( indexOfOnes );
lines(u-1).Longitude( indexOfOnes ) = lon( indexOfOnes );
lines(u-1).Name = ['>= ', num2str(Rx(u)), ' dBm'];
lines(u-1).Color = color(u-1);
lines(u-1).Width = width(u-1);
end
filename = 'Rx Levels';
kmlwrite(filename, lines, ...
'Name', lines.Name, ...
'Color', lines.Color, ...
'Width', lines.Width);
What I got is:
I hope that would be useful.

Most efficient way of drawing grouped boxplot matlab

I have 3 vectors: Y=rand(1000,1), X=Y-rand(1000,1) and ACTid=randi(6,1000,1).
I'd like to create boxplots by groups of Y and X corresponding to their group value 1:6 (from ACTid).
This is rather ad-hoc and looks nasty
for ii=
dummyY(ii)={Y(ACTid==ii)};
dummyX(ii)={X(ACTid==ii)}
end
Now I have the data in a cell but can't work out how to group it in a boxplot. Any thoughts?
I've found aboxplot function that looks like this but I don't want that, I'd like the builtin boxplot function because i'm converting it to matlab2tikz and this one doesn't do it well.
EDIT
Thanks to Oleg: we now have a grouped boxplot... but the labels are all skew-whiff.
xylabel = repmat({'Bleh','Blah'},1000,1); % need a legend instead, but doesn't appear possible
boxplot([Y(:,end); cfu], {repmat(ACTid,2,1), xylabel(:)} ,'factorgap',10,'color','rk')
set(gca,'xtick',1.5:3.2:50)
set(gca,'xticklabel',{'Direct care','Housekeeping','Mealtimes','Medication','Miscellaneous','Personal care'})
>> ylabel('Raw CFU counts (Y)')
How to add a legend?
I had the same problem with grouping data in a box plot. A further constraint of mine was that different groups have different amounts of data points. Based on a tutorial I found, this seems to be a nice solution I wanted to share with you:
x = [1,2,3,4,5,1,2,3,4,6];
group = [1,1,2,2,2,3,3,3,4,4];
positions = [1 1.25 2 2.25];
boxplot(x,group, 'positions', positions);
set(gca,'xtick',[mean(positions(1:2)) mean(positions(3:4)) ])
set(gca,'xticklabel',{'Direct care','Housekeeping'})
color = ['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
c = get(gca, 'Children');
hleg1 = legend(c(1:2), 'Feature1', 'Feature2' );
Here is a link to the tutorial.
A two-line approach (although if you want to retain two-line xlables and center those in the first line, it's gonna be hackish):
Y = rand(1000,1);
X = Y-rand(1000,1);
ACTid = randi(6,1000,1);
xylabel = repmat('xy',1000,1);
boxplot([X; Y], {repmat(ACTid,2,1), xylabel(:)} ,'factorgap',10)
The result:
EDIT
To center labels...
% Retrieve handles to text labels
h = allchild(findall(gca,'type','hggroup'));
% Delete x, y labels
throw = findobj(h,'string','x','-or','string','y');
h = setdiff(h,throw);
delete(throw);
% Center labels
mylbl = {'this','is','a','pain','in...','guess!'};
hlbl = findall(h,'type','text');
pos = cell2mat(get(hlbl,'pos'));
% New centered position for first intra-group label
newPos = num2cell([mean(reshape(pos(:,1),2,[]))' pos(1:2:end,2:end)],2);
set(hlbl(1:2:end),{'pos'},newPos,{'string'},mylbl')
% delete second intra-group label
delete(hlbl(2:2:end))
Exporting as .png will cause problems...