Handling and eliminating multiples entries in MatLab legend - matlab

I currently want to have the legend of graph, however i'm plotting several lines that should be group in only 3 types.
My currently option is to use a dummy plot out of the boundaries, plotting the relevant data and calling the legend just at the end. It works but it is prone to errors. I wanted to update the legend and select just a few of the plots.
I tried to use the leg_handle.String, but then it comes two problems:
It still plot 5 handles instead of 3.
It does not have the proper line style & color.
Any ideas?
Bellow follow the code (with dummy plot commented) and the pictures of the current version giving the error and what i want to look.
clear
figure()
hold on
%using
%dummy plot
% leg_text={'a','b','c'};
% plot(100,100,'-r')
% plot(100,100,'-b')
% plot(100,100,'-k')
for ii=1:20,
plot(1:11,linspace(0,ii,11),'-r')
end
for ii=30:50,
plot(1:11,linspace(0,ii,11),'-b')
end
for ii=70:80,
plot(1:11,linspace(ii,25,11),'-k')
end
Yaxl=[-1 80];
Xaxl=[1 11];
set(gca, 'Visible','on', ...
'Box','on', ...
'Layer','top',...
'Xlim',Xaxl, ...
'Ylim',Yaxl);
%using
% legend(leg_text)
%want to use
leg_hand=legend(gca,'show');
leg_hand.String=leg_hand.String([1 21 42]);
%extra comand will give the things that i wanted above
% leg_hand.String=leg_hand.String([1 2 3]);
What it gives:
What I expect to have:
I have tried this method using [a,b,c,d]=legend, but this give only the a handle that i already using.

This little workaround should do the job:
clear();
figure();
hold on;
h = gobjects(3,1);
for ii = 1:20
h(1) = plot(1:11,linspace(0,ii,11),'-r');
end
for ii = 30:50
h(2) = plot(1:11,linspace(0,ii,11),'-b');
end
for ii = 70:80
h(3) = plot(1:11,linspace(ii,25,11),'-k');
end
set(gca,'Box','on','Layer','top','Visible','on','Xlim',[1 11],'Ylim',[-1 80]);
legend(h,'A','B','C');
hold off;
Actually, what I did is very simple. I created an array of graphical objects of size 3 (one for each iteration) using the gobjects function. Then, inside each iteration, I assigned the last plotted line to its respective array placeholder. Finally, I created the legend using the three graphical objects I previously stored.
Alternatively:
clear();
figure();
hold on;
h1 = gobjects(20,1);
for ii = 1:20
h1(ii) = plot(1:11,linspace(0,ii,11),'-r');
end
h2 = gobjects(21,1);
for ii = 30:50
h2(ii-29) = plot(1:11,linspace(0,ii,11),'-b');
end
h3 = gobjects(11,1);
for ii = 70:80
h3(ii-69) = plot(1:11,linspace(ii,25,11),'-k');
end
set(gca,'Box','on','Layer','top','Visible','on','Xlim',[1 11],'Ylim',[-1 80]);
legend([h1(1) h2(1) h3(1)],'A','B','C');
hold off;
You create an array of graphical objects for storing the plot handlers produced by every iteration. Then you create the legend using the first (basically, any) item of each array of graphical objects.

Related

MATLAB: Blanked Plots when Looping on a Multidimensional Array

My goal output is to have four plots displaying (time, dist_a), (time, speed_a), (time, dist_b), and (time, speed_b) when looping through a multidimensional array. However, I am displaying only 2 blanked plots.
Here is my code:
time = rand(10, 1)
dist_a = rand(10,1)
dist_b = rand(10,1)
speed_a = rand(10,1)
speed_b = rand(10,1)
dist = cat(2, dist_a, dist_b);
speed = cat(2, speed_a, speed_b);
for k = 1:2
figure;
plot(time, dist(k));
plot(time, speed(k));
end
Your problems were two-fold. Firstly, you were only plotting a single point as opposed to a vector, changing dist(k) to dist(:,k) for example fixes this. Secondly, if you want four figures with a loop that executes twice, you need to include another figure command before the second plot. The following should do what you asked for, I also added in some formatting to make the plots looks nicer
for k = 1:2
figure
plot(time, dist(:,k),'o','LineWidth',2);
xlabel('time')
ylabel('distance')
box on
grid on
figure
plot(time, speed(:,k),'o','LineWidth',2);
xlabel('time')
ylabel('speed')
box on
grid on
end
which gives:

Update plot using hold on inside a for loop

I'm combining two plots using this code
plot(x1,y1,'.','MarkerSize',20,'Color','r');
hold on; grid on;
plot(x2,y2,'x','MarkerSize',10,'Color','b');
xlim([-a a]);
ylim([-a a]);
Now I want to change the values of x1,y1 and x2,y2 in order to have more than one point and one cross inside my figure. I tried to use a for loop where I compute new values, but every iteration this code generates another figure - whereas I want just one figure with all the points in it.
I did something like this:
for i=1:1:8
% do things that compute x1,x2,y1,y2
figure; hold on
plot(x1,y1,'.','MarkerSize',20,'Color','r');
hold on; grid on;
plot(x2,y2,'x','MarkerSize',10,'Color','b');
xlim([-a a]);ylim([-a a]);
i=i+1;
end
I also tried to put the hold on just before i=i+1 but still give me a new figure.
There are several things you can do:
The simple solution would be to put the figure command outside the loop:
figure(); hold on;
for ...
plot(x1, ...);
plot(x2, ...);
end
A better solution would be to first compute all values and then plot them:
[x1,y1,x2,y2] = deal(NaN(8,1));
for ind1 = 1:8
% do some computations
x1(ind1) = ...
...
y2(ind1) = ...
end
figure(); plot(x1,y1,'.',x2,y2,'x');
The best solution (in my opinion) would be to update existing plot objects with new data points as they become available:
[x1,y1,x2,y2] = deal(NaN(8,1));
figure(); hP = plot(x1,y1,'.',x2,y2,'x');
for ind1 = 1:8
% do some computations
hP(1).XData(ind1) = <newly-computed x1>
hP(1).YData(ind1) = <newly-computed y1>
hP(2).XData(ind1) = <newly-computed x2>
hP(2).YData(ind1) = <newly-computed y2>
end

Copy of polygon created in matlab figure despite using same figure handle throughout

figure;
poly = fill(sq(:,1), sq(:,2), 'b');
%% create cell "p" consisting of 5600 arrays of same size as sq
for i=1:5600
hold on
set(poly, 'X', p{i}(:,1),...
'Y', p{i}(:,2));
hold off
drawnow;
end
%% again create cell "q" consisting of 8700 arrays of same size as sq
for i=1:8700
hold on
set(poly, 'X', q{i}(:,1),...
'Y', q{i}(:,2));
hold off
drawnow;
end
I create a blue-filled polygon in first line and then move it all over the figure. When i run the above code, first section moves a polygon as controlled by p from initial point x0 to x1. Then i make another cell q in second section of code and use it to move blue-filled polygon again from x1 to x2. But this time a copy of the polygon is created at x1 which moves so that the previous polygon is still at x1 while this new polygon is moving to x2. Why is this happening?
I tried to write what you describe, using a little different code (more efficient), and made up the parts you didn't add. It's working, so if this is what you look for you can either adopt this code or compare it with yours and look for the problem.
My code:
% define some parameters and auxilary function:
sq_size = 3;
makeSq = #(xy) [xy(1) xy(2)
xy(1)+sq_size xy(2)
xy(1)+sq_size xy(2)+sq_size
xy(1) xy(2)+sq_size];
% create the first object:
sq = makeSq([1 1]);
poly = fill(sq(:,1), sq(:,2), 'b');
% setting the limmits for a constant view:
xlim([0 50+sq_size])
ylim([0 50+sq_size])
% first loop:
p = randi(50,10,2); % no need for cell here...
for k = 1:size(p,1)
temp_sq = makeSq(p(k,:));
set(poly, 'X', temp_sq(:,1),'Y',temp_sq(:,2));
drawnow;
pause(0.1)
end
% second loop:
q = randi(50,20,2); % no need for cell here...
set(poly,'FaceColor','g') % change to green to see we are in the second loop
for k = 1:size(q,1)
temp_sq = makeSq(q(k,:));
set(poly,'X',temp_sq(:,1),'Y',temp_sq(:,2));
drawnow;
pause(0.1)
end
The pause is only so you see the animation, it's not really needed.

Get access to the default LineStyleOrder and ColorOrder arrays in MATLAB

Quick "convenience" question for MATLAB users. I am looping over a plot command, passing it different data to plot each time. The data happens to be generated from a function call, which upon each iteration is passed a different parameter value. To plot everything on the same axis I am using the 'hold' function. Unfortunately this doesn't auto cycle through the available ColorOrder and/or LineStyleOrder plot parameters, so every line plotted has the same style on every iteration.
for i=1:nLines
[x_data y_data]=get_xy_data(param1(i),param2(i))
plot(x_data,y_data)
end
Every line plotted will be the default blue line style.
The obvious solution is to generate up front a cell array of the various line styles, and colors as in:
line_styles={'-','--','-*'}; %...etc
colors=colormap(jet(nLines));
then access each of those on every iteration. What I want is access to the default colors which will be generated from ColorOrder, and the default line cycling, which comes from LineStyleOrder. If I try something like:
get(gca,'LineStyleOrder')
This only returns the styles used in that axis (I've only tested this on an axis defined with one of the styles, but point is, it doesn't give me all possible linestyles). Help appreciated, thanks!
EDIT: Let me be more specific in what I am looking for.
figure; hold on;
for i=1:nLines
[xdata, ydata]=get_data(p1(i),p2(i)) % call some function to return x,y data
plot(xdata,ydata) % on i=1, default blue line
% function which tells matlab to get/set the next linestyle, color combination
nextStyle()
end
If this doesn't exist, it wouldn't be too hard to write it, but I thought I'd ask first before reinventing the wheel.
You may be interested in setting the default properties of DefaultAxesLineStyleOrder and DefaultAxesColorOrder.
The plots (style and color) will first loop through the newly defined colors and then changed the line style. In a successive plot loop, using hold all will "hold the graph and the current line color and line style so that subsequent plotting commands do not reset the ColorOrder and LineStyleOrder" (see the matlab doc). Both examples produce identical results.
%default properties (line style and color)
set(0,'DefaultAxesLineStyleOrder',{'--','-',':'})
set(0,'DefaultAxesColorOrder', summer(4))
figure('Color','w');
%example plot 1 (concurrent plots)
subplot(1,2,1);
yvals = [1:50;1:50]
plot(yvals, 'LineWidth', 2)
axis([1 2 0 size(yvals,2)+1 ]);
title('concurrent plot','FontSize',16);
%example plot 2 (iterative plots)
subplot(1,2,2);
for ii = 1:50
plot(yvals(:,ii), 'LineWidth', 2);
hold all;
end
axis([1 2 0 size(yvals,2)+1 ]);
title('successive plot','FontSize',16);
The results are
It looks like #Luis Mendo was not that wrong!
You can use hold all. That automatically sets different colors and linestyles for each plot.
You could set the line style and color directly for each line. Here's an example:
figure
hold on
nLines = 12;
line_styles={'-','--','-.'};
colors= hsv(nLines);
indexColors = 1;
indexLines = 1;
for i=1:nLines
xData = 1:10;
yData = rand(1,10);
h = plot(xData,yData);
ls = line_styles{indexLines};
c = colors(indexColors,:);
set(h,'color',c)
set(h,'LineStyle',ls)
if indexColors < length(colors)
indexColors = indexColors + 1;
else
indexColors = 1;
end
if indexLines < length(line_styles)
indexLines = indexLines + 1;
else
indexLines = 1;
end
end

MATLAB - best way to dynamically update a line handles' XData and YData?

I am collecting data and plotting that data in real time. The data are produced by a motion capture system. I have one class DynamicDataset that is just a wrapper around a 2-column matrix (although it's more nuanced than that) with an event notifier for new data added; another class DynamicPlotter that listens for the data-added event and updates the plot dynamically. Appropriate code snippets:
classdef DynamicDataset < handle
properties
newestData = [];
data = []
end
events
DataAdded
end
methods
function append(obj, val)
obj.data(end+1,:) = val;
obj.newestData = val;
notify(obj, 'DataAdded');
end
end
end
classdef DynamicPlotter < dynamicprops
properties
FH %# figure handle
AH %# axes handle
LH %# array of line handles - may have multiple lines on the plot
dynProps = {} %# cell array of dynamic property names -
%# use to access individual datasets
end
methods
function obj = DynamicPlotter(props) %# props is a cell array of dynamic
%# properties to store information
for i = 1:length(props)
addprop(obj, props{i});
obj.(props{i}) = DynamicDataset;
obj.dynProps = [obj.dynProps props{i}];
addlistener(obj.(props{i}), 'DataAdded', #obj.updatePlot(i));
end
obj.createBlankPlot();
end
function createBlankPlot(obj)
obj.FH = figure;
obj.AH = axes;
hold all;
for i = 1:length(obj.dynProps)
obj.LH(i) = plot(nan); %# only used to produce a line handle
set(obj.LH(i), 'XData', [], 'YData', []);
end
end
function updatePlot(obj, propNum)
X = get(obj.LH(propNum), 'XData');
Y = get(obj.LH(propNum), 'YData');
X(end+1) = obj.(dynProps{propNum}).newestData(1);
Y(end+1) = obj.(dynProps{propNum}).newestData(2);
set(obj.LH(propNum), 'XData', X, 'YData', Y);
end
end
end
Based on the MATLAB Code Profile, the set command in updatePlot() is rather expensive. I am wondering if there is a better way to plot individual points as they come? Ideally I would push the single point into XData and YData and draw that point only, but I don't know if this is possible.
Please note that there may be multiple lineseries objects (i.e., multiple graphs on the same plot); plot() takes an axes handle as an argument, so it wouldn't consider the properties of the previously drawn line handles (or is there a way to make it do so?); I thought of just doing plot(x,y);hold all; but that would give me separate line handles every time, each corresponding to a single point.
It might be that there's no way to make plotting incoming points any faster, but I figured I'd ask.
EDIT: Updated OP with actual code I'm working with, rather than using a generic example that's up for misinterpretation.
The amount of data you're handling in each update, is large (although only a single point is actually changing), making your code O(N^2).
By using a second lineseries to build up a large group of data, you can alternate between adding every point to a short "active" line, and infrequently adding large blocks to the main lineseries. While this doesn't exactly avoid O(N^2), it lets you reduce the constant significantly.
If you do this, remember to overlap the "old" lineseries and "active" lineseries by one point, so that they connect.
Essentially:
function updatePlot(obj, propNum)
X = get(obj.LHactive(propNum), 'XData');
Y = get(obj.LHactive(propNum), 'YData');
X(end+1) = obj.(dynProps{propNum}).newestData(1);
Y(end+1) = obj.(dynProps{propNum}).newestData(2);
if numel(X) > 100
Xold = [get(obj.LH(propNum), 'XData'); X(2:end)];
Yold = [get(obj.LH(propNum), 'YData'); Y(2:end)];
set(obj.LH(propNum), 'XData', Xold, 'YData', Yold);
X = X(end);
Y = Y(end);
end
set(obj.LHactive(propNum), 'XData', X, 'YData', Y);
end
Part of the reason why your code may be taking a long time to run is because you are using a for loop to assign your variables. Depending on what version of Matlab you are using, this will slow your process down significantly. I suggest using vectorization to assign values to your x and y like this:
x = 1:1000;
y = cosd(x);
You can then assign the first points in your data.
xi = x(1);
yi = y(1);
When you plot, assign the XDataSource and YDataSource.
h = plot(xi, yi, 'YDataSource', 'yi', 'XDataSource', 'xi');
Now when you loop through to change the values, use the refreshdata to update the Xdata and Ydata values. Use the drawnow function to update the figure window.
for k = 2:1000,
xi = x(1:k);
yi = y(1:k);
refreshdata(h, 'caller')
drawnow;
end
Your code is slow, because you are replotting all values everytime that you call updatePlot. I would therefore only plot the latest point in updatePlot (This is also the problem that you've stated: Ideally I would push the single point into XData and YData and draw that point only, but I don't know if this is possible.)
add property LH_point_counter
classdef DynamicPlotter < dynamicprops
properties
FH %# figure handle
AH %# axes handle
LH %# cell array of line handles - may have multiple lines on the plot
% counter that counts home many points we have for each dynProps
LH_point_counter = [];
dynProps = {} %# cell array of dynamic property names -
%# use to access individual datasets
end
modify updatePlot
function updatePlot(obj, propNum)
% plot new point
new_x = obj.(dynProps{propNum}).newestData(1);
new_y = obj.(dynProps{propNum}).newestData(2);
new_handle = plot(new_x, new_y);
% add new handle to list of handles of this property
counter_this_prop = obj.LH_point_counter(propNum);
counter_this_prop = counter_this_prop + 1;
obj.LH{propNum}(counter_this_prop) = new_handle;
% save new counter value
obj.LH_point_counter(propNum) = counter_this_prop;
end