How to make all the legend appear in a plot? - matlab

Im using this code to plot X vector vs all the columns of the table on example.txt
C = readtable('example.txt', 'Delimiter',',');
X = 5:5:430;
%%
for i=1:numel(X)
plot(X,C{:,i})
hold on
grid on
end
LegendString = cell(1,numel(X));
hold all
for k = 1:numel(X)
LegendString{k} = sprintf('%i',X(k));
end
legend(LegendString)
Here you can download the example.txt file to run the code:
https://la.mathworks.com/matlabcentral/answers/383371-how-to-make-all-the-legend-appear-in-a-plot
Why the plot is only showing me 50 legends and not all the 86 the code is asking?

It's not a Matlab bug, but an intentional behavior: legends are currently limited to 50 elements (you may have noticed that your plot legend contains 50 items in fact, instead of 86). This limit has probably been introduced in order to avoid the creation of excessively big legends, which:
are hard to read (especially if they overflow the plot window);
are computationally expensive to draw;
occupy a huge volume of memory.
Traces of this hardcoded limit can be found in the legend script itself (in order to view it, run open legend in the Command Window). For legends created with explicit labels passed as argument, the limit is probably applied internally somewhere in the class that handles the graphic components of the legend. For legends created without a defined set of labels, the subfunction set_children_and_strings applies this limit:
if auto_children && length(ch) > 50,
% only automatically add first 50 to cut down on huge lists
ch = ch(1:50);
end
matlab.graphics.illustration.internal.generateDisplayNames(ch);
In order to overcome this limit, you have two alternatives:
select just a few elements to display in the legend;
use a workaround by creating one legend for each group of 50 items.
EDIT
C = readtable('data.txt','Delimiter',',');
X = 5:5:430;
X_len = numel(X);
X_seq = 1:numel(X);
figure();
hold on;
grid on;
h = gobjects(X_len,1);
for i = X_seq
h(i) = plot(X,C{:,i});
end
legend(h(2:2:X_len),sprintfc('%d',10:10:430));

Related

how to decrease the legend width in matlab

I am using the matlab to plot some project figures, see the blow figure. Now I am trying to cut the legend width so that the line won't look so wide. I tried these command as suggest by Benoit_11:
[~,icons,~,~] = legend(leg,'location','northwest');
hline = icons(2);
linedata = get(hline,'xdata');
newdata = [linedata(1)+0.2 linedata(2)];
set(hline,'xdata',newdata,'linewidth',1)
I am using the for loop to plot these figures because I have multiple figures to analysis at the same time. Now I can change the length of the legend line right now. But I got another problem: if I have different length of legend text, even if I set the same starting point and end point, I will get different length for the line in the end (you can see that from the figures). I tried to modify icon(1) but always got the error. Any suggestions?
There are 2 things you are not doing right with your code (aside the fact that you use size as the handles to the legend...that's risky because size is a built-in function):
1) Calling legend with only 1 argument returns a handle to the legend object and getting its position actually gives you the position of the box enclosing the legend, i.e. the text + the line.
2) Using this line:
p(3) = p(3) - 0.06;
does modify the position, however you would need to set the new position of the legend with something like the following for the changes to be effective:
set(HandleToLegend,'Position',p)
To come back to your question, the trick is to assign many outputs during the call to legend; you can then modify specific elements of the legend object.
Actually we only need 1 of the 4 output arguments, called icons in the docs so I'll stick with the notation. Then, we can get the XData property of the line and modify it as we want. The XData is actually a 2-element vector:
[StartingPoint EndingPoint]
so changing one or the other (or both) will change the length of the line displayed in the legend box.
Here is the whole code with comments; I changed the length and linewidth of the line in the 2nd plot to highlight the changes.
clear
clc
close all
x = 1:10;
y = rand(1,10);
figure;
%// Default case
subplot(1,2,1)
plot(x,y);
legend('First plot','Location','NorthWest');
title('Before','FontSize',18);
%// With modifications
subplot(1,2,2)
plot(x,y);
title('After','FontSize',18);
%//========================
%// Change the legend here
%//========================
%// The "icons" output is what you want
[~,icons,~,~] = legend('First plot','Location','NorthWest');
%// icons(1) is the text of the current element in the legend Here its 'First plot'
i_1 = get(icons(1)); %// access the properties with this command.
%// icons(2) is the line associated with that text. Here the blue line.
i_2 = get(icons(2));
%// Mhh I don't know what icons(3) represents haha sorry about that.
i_3 = get(icons(3));
%// Get the actual line
hline = icons(2);
%// Fetch its XData property
LineData = get(hline,'XData')
%// Play with those 2 elements to see the output change.
NewData = [LineData(1)+.2 LineData(2)-.01];
%// Apply the changes
set(hline,'XData',NewData,'LineWidth',3)
Which gives the following:
You need to set the value of the Position property, you can just change the vector p. p does not affect the plot at all, it's just a vector of numbers. You have to modify it, then apply it back to the plot, using
set(size,'Position',p)
There does seem to be a minimum width of the legend however.

Plot a cell into a time-changing curve

I have got a cell, which is like this : Data={[2,3],[5,6],[1,4],[6,7]...}
The number in every square brackets represent x and y of a point respectively. There will be a new coordinate into the cell in every loop of my algorithm.
I want to plot these points into a time-changing curve, which will tell me the trajectory of the point.
As a beginner of MATLAB, I have no idea of this stage. Thanks for your help.
Here is some sample code to get you started. It uses some basic Matlab functionalities that you will hopefully find useful as you continue using it. I added come data points to you cell array for illustrative purposes.
The syntax to access elements into the cell array might seem weird but is important. Look here for details about cell array indexing.
In order to give nice colors to the points, I generated an array based on the jet colormap built-in in Matlab. Basically issuing the command
Colors = jet(N)
create a N x 3 matrix in which every row is a 3-element color ranging from blue to red. That way you can see which points were detected before other (i.e. blue before red). Of course you can change that to anything you want (look here if you're interested).
So here is the code. If something is unclear please ask for clarifications.
clear
clc
%// Get data
Data = {[2,3],[5,6],[1,4],[6,7],[8,1],[5,2],[7,7]};
%// Set up a matrix to color the points. Here I used a jet colormap
%// available from MATLAB but that could be anything.
Colors = jet(numel(Data));
figure;
%// Use "hold all" to prevent the content of the figure to be overwritten
%// at every iterations.
hold all
for k = 1:numel(Data)
%// Note the syntax used to access the content of the cell array.
scatter(Data{k}(1),Data{k}(2),60,Colors(k,:),'filled');
%// Trace a line to link consecutive points
if k > 1
line([Data{k-1}(1) Data{k}(1)],[Data{k-1}(2) Data{k}(2)],'LineStyle','--','Color','k');
end
end
%// Set up axis limits
axis([0 10 0 11])
%// Add labels to axis and add a title.
xlabel('X coordinates','FontSize',16)
ylabel('Y coordinates','FontSize',16)
title('This is a very boring title','FontSize',18)
Which outputs the following:
This would be easier to achieve if all of your data was stored in a n by 2 (or 2 by n) matrix. In this case, each row would be a new entry. For example:
Data=[2,3;
5,6;
1,4;
6,7];
plot(Data(:, 1), Data(:, 2))
Would plot your points. Fortunately, Matlab is able to handle matrices which grow on every iteration, though it is not recommended.
If you really wanted to work with cells, there are a couple of ways you could do it. Firstly, you could assign the elements to a matrix and repeat the above method:
NumPoints = numel(Data);
DataMat = zeros(NumPoints, 2);
for I = 1:NumPoints % Data is a cell here
DataMat(I, :) = cell2mat(Data(I));
end
You could alternatively plot the elements straight from the cell, though this would limit your plot options.
NumPoints = numel(Data);
hold on
for I = 1:NumPoints
point = cell2mat(Data(I));
plot(point(1), point(2))
end
hold off
With regards to your time changing curve, if you find that Matlab starts to slow down after it stores lots of points, it is possible to limit your viewing window in time with clever indexing. For example:
index = 1;
SamplingRate = 10; % How many times per second are we taking a sample (Hertz)?
WindowTime = 10; % How far into the past do we want to store points (seconds)?
NumPoints = SamplingRate * WindowTime
Data = zeros(NumPoints, 2);
while running
% Your code goes here
Data(index, :) = NewData;
index = index + 1;
index = mod(index-1, NumPoints)+1;
plot(Data(:, 1), Data(:, 2))
drawnow
end
Will store your data in a Matrix of fixed size, meaning Matlab won't slow down.

Figure legend is being cut off MATLAB

I'm trying to plot 81 variables into one plot in MATLAB and I need a legend with 81 respective labels. I've managed to separate the legend into several lines such that it would fit the figure better but still, being this large, the legend apears cut off:
is there any way to solve this problem, so that all the legend appears in the figure?
Thank you in advance
In my experience, the legend is not manageable once the number of plots becomes so large.
You could try and hack this out by hand by trying to make a compact legend my manually moving the legend lines and text boxes around individually--but I fear this way will ultimately make you unhappy. If you want, you could start playing with the legend pieces by writing something like:
hl = legend(uniqueHandles, myNames);
hlc = get(hl, 'children');
This may not be possible and MATLAB will resist you. Instead, consider that a legend with 81 different colors becomes very hard to manage and you can't really tell the colors apart anyway. Instead of trying to get them all to fit I recommend grouping them into different colors and styles and then only showing 3-7 different sets like this:
This could be further improved by customizing the data tip so that when you click on a line it gives you more detail.
I put together some basic code to show how you might do this. It has some shortcomings. Regardless, in this example I plot everything at once then go back and group the colors together. I then pick out a representative line from each grouping and use it as an example in the legend. You could also plot everything, color it correctly, and then plot a new line in the existing axis with a value nan and use THOSE in the legend.
(edit) I also noted that you have grouped the colors already for some of them, so this should flow naturally from the work you've already done. If nothing else, you could grab the handles from every third entry and then only have 27 items in your legend... but best if you can reduce it further.
The example to create the above image is as follows:
%% Setup the problem:
nd = 81;
nx = 256;
data = zeros(nx,nd);
t = linspace(0, 2*pi, nx);
%% We divide the data into 3 groups and set a color for each. This calculation then notes what group each line belongs to:
myCategory = ceil((1:nd)/27);
myColor = myColors(myCategory)';
myColors = ['r', 'b', 'g'];
myNames = {'1 to 27', '28 to 54', '55 to 81'};
%% Make a simple set of data:
randn('seed', 1982);
dt = 0;
for ind = 1:nd
dt = dt+randn/5;
data(:, ind) = sin(t+dt) + dt*cos(t);
end
%% Plot all at once:
figure(33);
clf;
h = plot(data);
%% find the unique entries and choose one representative line for the legend.
%% We then use the handles of these lines to create the legend:
lastColor = -1;
uniqueHandles = [];
for ind = 1:nd
set(h(ind), 'color', myColor(ind));
if lastColor ~= myColor(ind)
lastColor = myColor(ind);
uniqueHandles(end+1) = h(ind);
end
end
% make the legend:
legend(uniqueHandles, myNames);

Plot data with MATLAB biplot with more than 1 color

I have 3 groups of data that had PCA performed on them as one group. I want to highlight each variable group with a different color. Prior to this I overlaid 3 biplots. This gives different colors but creates a distortion in the data as each biplot function skews the data. This caused the groups to all be skewed by different amounts, making the plot not a correct representation.
How do I take a PCA scores matrix (30x3) and split it so the first 10x3 is one color, the next 10x3 is another and the third 10x3 is another, without the data being skewed?
"Skewing" is happening because biplot is renormalizing the scores so the farthest score is distance 1 . axis equal isn't going to fix this. You should use scatter3 instead of biplot
data = rand(30,3);
group = scores(1:10,:)
scatter3(group(:,1), group(:,2), group(:,3), '.b')
hold all
group = scores(11:20,:)
scatter3(group(:,1), group(:,2), group(:,3), '.r')
group = scores(21:30,:)
scatter3(group(:,1), group(:,2), group(:,3), '.g')
hold off
title('Data')
xlabel('X')
ylabel('Y')
zlabel('Z')
Or modify your code's scatter3 lines so that the markers are different colors. The parameter after 'marker' tells what symbol and what symbol and color to plot. E.g. '.r' is a red dot. See Linespec for marker and color parameters.
scatter3(plotdataholder(1:14,1),plotdataholder(1:14,2),plotdataholder(1:14,3),35,[1 0 0],'marker', '.b');
hold on;
scatter3(plotdataholder(15:28,1),plotdataholder(15:28,2),plotdataholder(15:28,3),35,[0 0 1],'marker', '.r') ;
scatter3(plotdataholder(29:42,1),plotdataholder(29:42,2),plotdataholder(29:42,3),35,[0 1 0],'marker', '.g');
This is the method I used to plot biplot data with different colors. The lines of code prior to plot are taken from the biplot.m file. The way biplot manipulates data is kept intact and stops skewing of data when using overlaid biplots.
This coding is not the most efficient, one can see parts that can be cut. I wanted to keep the code intact so one can see how biplot works in it's entirety.
%%%%%%%%%%%%%%%%%%%%%
xxx = coeff(:,1:3);
yyy= score(:,1:3);
**%Taken from biplot.m; This is alter the data the same way biplot alters data - having the %data fit on grid axes no larger than 1.**
[n,d2] = size(yyy);
[p,d] = size(xxx); %7 by 3
[dum,maxind] = max(abs(xxx),[],1);
colsign = sign(xxx(maxind + (0:p:(d-1)*p)));
xxx = xxx .* repmat(colsign, p, 1);
yyy= (yyy ./ max(abs(yyy(:)))) .* repmat(colsign, 42, 1);
nans = NaN(n,1);
ptx = [yyy(:,1) nans]';
pty = [yyy(:,2) nans]';
ptz = [yyy(:,3) nans]';
**%I grouped the pt matrices for my benefit**
plotdataholder(:,1) = ptx(1,:);
plotdataholder(:,2) = pty(1,:);
plotdataholder(:,3) = ptz(1,:);
**%my original score matrix is 42x3 - wanted each 14x3 to be a different color**
scatter3(plotdataholder(1:14,1),plotdataholder(1:14,2),plotdataholder(1:14,3),35,[1 0 0],'marker', '.');
hold on;
scatter3(plotdataholder(15:28,1),plotdataholder(15:28,2),plotdataholder(15:28,3),35,[0 0 1],'marker', '.') ;
scatter3(plotdataholder(29:42,1),plotdataholder(29:42,2),plotdataholder(29:42,3),35,[0 1 0],'marker', '.');
xlabel('Principal Component 1');
ylabel('Principal Component 2');
zlabel('Principal Component 3');
I am not sure if it will help, but try axis equal after you have overlaid the plots.

Assigning figure size to a figure with a given handle (MATLAB)

is there a way to assign the outerposition property of a figure to a figure with a given handle?
For example, if I wanted to define a figure as say figure 1, I would use:
figure(1)
imagesc(Arrayname) % I.e. any array
I can also change the properties of a figure using the code:
figure('Name', 'Name of figure','NumberTitle','off','OuterPosition',[scrsz(1) scrsz(2) 700 700]);
Is there a propertyname I can use to assign the outerposition property to the figure assigned as figure 1?
The reason I am asking this is because I am using a command called save2word (from the MATLAB file exchange) to save some plots from a function I have made to a word file, and I want to limit the number of figures I have open as it does this.
The rest of the code I have is:
plottedloops = [1, 5:5:100]; % Specifies which loops I want to save
GetGeometry = getappdata(0, 'GeometryAtEachLoop') % Obtains a 4D array containing geometry information at each loop
NumSections = size(GetGeometry,4); %Defined by the fourth dimension of the 4D array
for j = 1:NumSections
for i = 1:plottedloops
P = GetGeometry(:,:,i,j);
TitleSize = 14;
Fsize = 8;
% Save Geometry
scrsz = get(0,'ScreenSize'); %left, bottom, width height
figure('Name', 'Geometry at each loop','NumberTitle','off','OuterPosition',[scrsz(1) scrsz(2) 700 700]); This specifies the figure name, dims etc., but also means multiple figures are opened as the command runs.
% I have tried this, but it doesn't work:
% figure(0, 'OuterPosition',[scrsz(1) scrsz(2) 700 700]);
imagesc(P), title('Geometry','FontSize', TitleSize), axis([0 100 0 100]);
text(20,110,['Loop:',num2str(i)], 'FontSize', TitleSize); % Show loop in figure
text(70,110,['Section:',num2str(j)], 'FontSize', TitleSize);% Show Section number in figure
save2word('Geometry at each loop'); % Saves figure to a word file
end
end
Thanks
If you capture the figure handle when you create the figure
figH = figure;
You can assign properties any time you want
set(figH,'OuterPosition',[scrsz(1),scrsz(2),700,700]);
You can also gather the figure handles inside a vector, and then set all sizes at once.
If you cannot capture the figure handle for some reason, you can use findall to look for a figure with a specific name, or gcf to get the handle of the current (last selected/opened) figure.
Here are a few suggestions/corrections:
Your second for loop should look like this:
for i = plottedloops
This is because plottedloops is already an array, and you want i to take on each sequential value in the array for each pass through the loop. For example, a common form for a for loop is:
for i = 1:someScalarValue
Where the term 1:someScalarValue creates an array for you.
It looks like you are wanting to plot something in a figure window, then save it with save2word, then plot something else, then save that, etc. Therefore, I suggest creating your figure window outside your for loops, and simply replotting the window contents within the loop. If you move these two lines outside your loops:
scrsz = get(0,'ScreenSize'); %left, bottom, width height
figure('Name', 'Geometry at each loop','NumberTitle','off',...
'OuterPosition',[scrsz(1) scrsz(2) 700 700]);
Then you should only have one figure at a time open.