MATLAB: Plotting on one axes with a loop: solid line & legend - matlab

I have two distinct problems, but they're posted together because I believe the solutions are related. I'm testing Newton's and secant methods (each of which is implemented with a loop) and plotting the results versus computing time on the same axes to compare them. I want the (discrete) Newton's method results to be connected by a blue line and the secant method results by a red line. These lines, in turn, are annotated by a corresponding legend. This is not happening because each and every point on the plot seems to be considered at individual object because they were individually created. And the legend command brings up two blue asterisks instead a blue one and a red one (I wish I could post my plot here, but I don't have the image privilege yet.)
Here's my abbreviated code:
f = (x) % define function
figure
hold on
%% Newton
tic
while % terminating condition
% [Newtons method]
t = toc;
plot(t,log(abs(f(z)),'b*-')
end
%% Secant
tic
while % terminating condition
% [secant method]
t = toc;
plot(t,log(abs(f(z)),'r*-')
end
legend('Newton''s','Secant')
Needless to day, the '-' in the linespec doesn't do anything because only a point is being plotted, not a line. I know I could make a line plot with each iteration with something like plot([t_old t],[log(abs(f(z_old) log(abs(f(z)]), but that isn't ideal, not least because log(abs(f(z_old))) would have to be reevaluated each time. Besides, that would not solve the problem with the legend.
I think both problems will be solved if I can get MATLAB to understand that I'm trying to create just two objects on the axes, one blue line and one red line. Thank you.

If you don't want to store the x/y data in a vector and then replot the entire vector you could just add to the plotting line using code like this:
hNewton = [];
while % terminating condition
% [Newtons method]
t = toc;
if isempty(hNewton)
hNewton = plot(t,log(abs(f(z))),'b*-'); % First time through plot and save the line handle
else
% On all subsequent passes, just add to the lines X/Y data
set(hNewton,'XData',[get(hNewton,'XData') t]);
set(hNewton,'YData',[get(hNewton,'YData') log(abs(f(z)))]);
end
end
Since there are now only 2 lines, the legend works as expected.
Alternatively, you could put the code to add data to an existing line in a function
function hLineHandle = AddToLine( hLineHandle, xData, yData, lineStyle )
% AddToLine - Add data to a plotted line
if isempty(hLineHandle)
hLineHandle = plot(xData,yData, lineStyle);
else
set(hLineHandle,'XData',[get(hLineHandle,'XData') xData]);
set(hLineHandle,'YData',[get(hLineHandle,'YData') yData]);
end
end
Which makes the code in the main script/function a lot cleaner.
hNewton = [];
while % terminating condition
% [Newtons method]
t = toc;
hNewton = AddToLine(hNewton,t, log(abs(f(z))),'b*-' );
end

You can use line object, for example:
f = (x) % define function
figure
hold on
lHandle1 = line(nan, nan); %# Generate a blank line and return the line handle
lHandle2 = line(nan, nan); %# Generate a blank line and return the line handle
%% Newton
tic
while % terminating condition
% [Newtons method]
t = get(lHandle1, 'XData');
Y1 = get(lHandle1, 'YData');
t = toc;
Y1 = [Y1 log(abs(f(z)];
set(lHandle1, 'XData', t, 'YData', Y1, 'LineWidth', 2 ,'Color' , [0 1 0]);
end
%% Secant
tic
while % terminating condition
% [secant method]
t = get(lHandle2, 'XData');
Y2 = get(lHandle2, 'YData');
t = toc;
Y2 = [Y2 log(abs(f(z)];
set(lHandle2, 'XData', t, 'YData', Y2, 'LineWidth', 2 ,'Color' , [1 0 0]);
end
legend('Newton''s','Secant')

Good example of only showing the relevant parts of your code to ask a question. The others have explained tricks to have the legend behave as you want. I would go for a different solution, by saving your measurements in a vector and doing the plots after the loops. This has 2 advantages: you do not have to do the tricks with the legend, but more importantly, you are not doing a plot inside your loop, which potentially takes a lot of time. I would guess that your timing is dominated by the plotting, so the influence of your algorithm will hardly show up in the results. So change your code to something like this (untested):
f = (x) % define function
% preallocate plenty of space
[t_newton, t_secant, f_newton, f_secant] = deal(nan(1, 1000));
%% Newton
tic;
i = 1;
while % terminating condition
% [Newtons method]
f_newton(i) = current_result;
t_newton(i) = toc;
i = i + 1;
end
%% Secant
tic;
i = 1;
while % terminating condition
% [secant method]
f_secant(i) = current_result;
t_secant(i) = toc;
i = i + 1;
end
% trim NaNs (not really needed, not plotted anyhow)
t_newton = t_newton(isfinite(t_newton));
f_newton = f_newton(isfinite(f_newton));
t_secant = t_secant(isfinite(t_secant));
f_secant = f_secant(isfinite(f_secant));
% do the plot
semilogy(t_newton, abs(f_newton), t_secant, abs(f_secant))
legend('Newton''s','Secant')

Related

How to set the errorbars in a different color from the plot in Matlab?

When making a plot in Matlab using errorbar(...) the color of the error bars is the same as the plot. How do I set them to be in a different color?
I tried looking for a way to do it in here:
https://www.mathworks.com/help/matlab/ref/matlab.graphics.chart.primitive.errorbar-properties.html
https://www.mathworks.com/help/matlab/ref/errorbar.html
But I couldn't find it.
Edit: This question:
Color of errobar different from the graph matlab
doesn't have the answer to what I'm asking. It was asked almost a year and half ago and no solution was given. The one comment there doesn't give a proper solution. It says to draw the plot twice - once with the errorbars (when the plot and the errorbars are at the same color) and a second time just the plot without the errorbars (which will be drawn on top of the first one using hold on). There should be a way to draw the figure once with the errorbars at a different color than the color of the plot - that is what I'm looking for.
Here is a quick and dirty implementation of a function that allows to style the errorbars and the data separatly. However, internally it does the same as the answers to the previously posted question: it uses two plots.
function varargout = mc_errorbar(ax, x,y,e, varargin)
% MC_ERRORBAR errorbar which allows to style errorbar and data separately
%
% Usage:
% mc_errorbar(X,Y,E)
% plots errorbars and a separate line in the current axis.
%
% mc_errorbar(X,Y,E, property, value)
% plots errorbars and a separate line in the current axis and styles
% the plots according to the properties given. All properties that are
% accepted by the errorbar function are allowed, but should be prefixed
% with 'EB'. Note that the LineStyle property will be overriden.
% Properties not prefixed with 'EB' will be passed to the plot
% function, hence all properties allowed by plot are allowed here, too.
%
% mc_errorbar(ax, ___)
% plots in the axes ax instead of the current axes.
%
% [lh] = mc_errorbar(___)
% returns the line handle
%
% [lh, ebh] = mc_errorbar(___)
% returns the line handle and the handle to the errorbar
%
% See also errorbar plot
if ~isa(ax, 'matlab.graphics.axis.Axes')
if nargin > 3
varargin = [{e}, varargin(:)'];
end
e = y;
y = x;
x = ax;
ax = gca();
end
oldnextplot = ax.NextPlot;
ebargs = {};
lineargs = {};
for k = 1:2:numel(varargin)
if strcmp(varargin{k}(1:2),'EB') == 1
ebargs{end+1} = varargin{k}(3:end);
ebargs{end+1} = varargin{k+1};
else
lineargs{end+1} = varargin{k};
lineargs{end+1} = varargin{k+1};
end
end
ebargs{end+1} = 'LineStyle';
ebargs{end+1} = 'none';
eb = errorbar(ax, x, y, e, ebargs{:});
ax.NextPlot = 'add';
line = plot(ax, x,y, lineargs{:});
ax.NextPlot = oldnextplot;
if nargout > 0
varargout{1} = line;
end
if nargout > 1
varargout{2} = eb;
end
end
Example:
mc_errorbar(1:10, (1:10)*2, (1:10)*.5, 'Color','g', 'EBColor', 'k', 'EBLineWidth', 3, 'LineStyle','-', 'LineWidth',8)

How to speed up a very slow animated plot in MATLAB

I'm trying to create an animated plot but my code is very slow, perhaps the method I'm using is too naive. In the below example, I have 4 subplots each with 3 lines, which I update in a 'time' loop.
clc;clear;close all;
state = {'$x-Position$','$x-Velocity$','$y-Position$','$y-Velocity$'};
ylabels = {'$x$','$\dot{x}$','$y$','$\dot{y}$'};
options1 = {'interpreter','latex'};
options2 = {'interpreter','latex','fontsize',20};
maxT = 300;
for pp = 1:4
hh1(pp)=subplot(2,2,pp);
xlabel('$t$',options2{:});
ylabel(ylabels{pp},options2{:});
title(state{pp},options1{:})
xlim([0 maxT])
hold on
end
x = randn(4,300);
z = randn(4,300);
x_est = randn(4,300);
for k = 2:maxT
for p = 1:4
plot(hh1(p),k-1:k,x(p,k-1:k),'b','linewidth',2)
plot(hh1(p),k-1:k,z(p,k-1:k),'m')
plot(hh1(p),k-1:k,x_est(p,k-1:k),':k','linewidth',2)
end
drawnow;
end
As can be seen from the profiler output, the drawnow is killing the time. Is there any way I can be more efficient in creating this animation?
Because you want an animation, there is no alternative to using drawnow to update the frame. However, it's not drawnow in particular which is slowing you down - the profiler can be misleading... drawnow simply updates all of the graphics changes since the last re-draw, which in your case is a dozen new plots!
You'll find that hold is pretty slowing. For instance if you're wiser about your holding, remove the existing hold on and only hold when actually plotting
% ... above code the same but without 'hold on'
for p = 1:4
hold(hh1(p), 'on');
% plots
hold(hh1(p), 'off');
end
This saves ~10% time on my PC (12.3sec down to 11.3sec).
The real speed up comes from removing hold entirely, along with all of the individual plot calls! This method also doesn't touch the line formatting which will help with speed. See a previous question about updating plot data here.
Simply update the plot data instead of adding plots. This gives me a speedup of ~68% (12.3sec down to 4.0sec).
% ... your same setup
% Initialise plot data
x = randn(4,300);
z = randn(4,300);
x_est = randn(4,300);
plts = cell(4,3);
hh1 = cell(4,1);
% Loop over subplots and initialise plot lines
for p = 1:4
hh1{p}=subplot(2,2,p);
xlabel('$t$',options2{:});
ylabel(ylabels{p},options2{:});
title(state{p},options1{:})
xlim([0 maxT])
% Hold on to make 3 plots. Create initial points and set line styles.
% Store the plots in a cell array for later reference.
hold on
plts{p,1} = plot(hh1{p},1:2,x(p,1:2),'b','linewidth',2);
plts{p,2} = plot(hh1{p},1:2,z(p,1:2),'m');
plts{p,3} = plot(hh1{p},1:2,x_est(p,1:2),':k','linewidth',2);
hold off
end
% March through time. No replotting required, just update XData and YData
for k = 2:maxT
for p = 1:4
set(plts{p,1}, 'XData', 1:k, 'YData', x(p,1:k) );
set(plts{p,2}, 'XData', 1:k, 'YData', z(p,1:k) );
set(plts{p,3}, 'XData', 1:k, 'YData', x_est(p,1:k) );
end
drawnow;
end
Now the plotting is pretty optimised. If you want the animation to be even quicker then just plot every 2nd, 3rd, ..., nth timestep instead of every timestep by using for k = 2:n:maxT.

Visualizing matrix values in real time

Suppose I have a 5x5 matrix.
The elements of the matrix change (are refreshed) every second.
I would like to be able to display the matrix (not as a colormap but with the actual values in a grid) in realtime and watch the values in it change as time progresses.
How would I go about doing so in MATLAB?
A combination of clc and disp is the easiest approach (as answered by Tim), here's a "prettier" approach you might fancy, depending on your needs. This is not going to be as quick, but you might find some benefits, such as not having to clear the command window or being able to colour-code and save the figs.
Using dispMatrixInFig (code at the bottom of this answer) you can view the matrix in a figure window (or unique figure windows) at each stage.
Example test code:
fig = figure;
% Loop 10 times, pausing for 1sec each loop, display matrix
for i=1:10
A = rand(5, 5);
dispMatrixInFig(A,fig)
pause(1)
end
Output for one iteration:
Commented function code:
function dispMatrixInFig(A, fig, strstyle, figname)
%% Given a figure "fig" and a matrix "A", the matrix is displayed in the
% figure. If no figure is supplied then a new one is created.
%
% strstyle is optional to specify the string display of each value, for
% details see SPRINTF. Default is 4d.p. Can set to default by passing '' or
% no argument.
%
% figname will appear in the title bar of the figure.
if nargin < 2
fig = figure;
else
clf(fig);
end
if nargin < 3 || strcmp(strstyle, '')
strstyle = '%3.4f';
end
if nargin < 4
figname = '';
end
% Get size of matrix
[m,n] = size(A);
% Turn axes off, set origin to top left
axis off;
axis ij;
set(fig,'DefaultTextFontName','courier', ...
'DefaultTextHorizontalAlignment','left', ...
'DefaultTextVerticalAlignment','bottom', ...
'DefaultTextClipping','on');
fig.Name = figname;
axis([1, m-1, 1, n]);
drawnow
tmp = text(.5,.5,'t');
% height and width of character
ext = get(tmp, 'Extent');
dy = ext(4);
wch = ext(3);
dwc = 2*wch;
dx = 8*wch + dwc;
% set matrix values to fig positions
x = 1;
for i = 1:n
y = 0.5 + dy/2;
for j = 1:m
y = y + 1;
text(x,y,sprintf(strstyle,A(j,i)));
end
x = x + dx;
end
% Tidy up display
axis([1-dwc/2 1+n*dx-dwc/2 1 m+1]);
set(gca, 'YTick', [], 'XTickLabel',[],'Visible','on');
set(gca,'XTick',(1-dwc/2):dx:x);
set(gca,'XGrid','on','GridLineStyle','-');
end
I would have thought you could achieve this with disp:
for i=1:10
A = rand(5, 5);
disp(A);
end
If you mean that you don't want repeated outputs on top of each other in the console, you could include a clc to clear the console before each disp call:
for i=1:10
A = rand(5, 5);
clc;
disp(A);
end
If you want to display your matrix on a figure it is quite easy. Just make a dump matrix and display it. Then use text function to display your matrix on the figure. For example
randMatrix=rand(5);
figure,imagesc(ones(20));axis image;
hold on;text(2,10,num2str(randMatrix))
If you want to do it in a for loop and see the numbers change, try this:
for i=1:100;
randMatrix=rand(5);
figure(1),clf
imagesc(ones(20));axis image;
hold on;text(2,10,num2str(randMatrix));
drawnow;
end

How to delete previous points while plotting?

I am plotting live data: for the plotting, I am using the line function, which improved the plotting performance a lot compared when using the plot function. Still, the plot gets slower with time. I realized that the sample points that I am creating are plotted, but even when they are not visible later the still remain in the plot. Could this cause any performance degradation?
I just want to see the sample points of the current three seconds, if use clf or cla function, just see very small part of the signal, which is not helpful for me. Do you have any suggestions?
%% opening function:
handles.figureHandle=figure;
guidata(hObject, handles);
t=1/200; %sample rate 5ms
%% button function:
if 40< newSamples
figure(handles.figureHandle)
t = max(t) + (1:size(sample,1)) * 1/200;
for x=1:8
subplot(8,8,x);
hold on
line('XDATA',t,'YDATA',sample(:,x),'MarkerSize', 1,'Color','r');
ylim([0 1024]);
xlim([max(t)-1 max(t)+2]);
hold off
end
drawnow ;
end
Update
%% opening function
sample=[];
t=[];
handles.figureHandle
for i=1:8
subplot(2,2,i);
hold on
h=line(t,sample,'MarkerSize', 1,'Color','r');
% ylim([0 1024]);
% xlim([max(t)-1 max(t)+2]);
hold off
end
t=1/200;
%% button function
figure(handles.figureHandle)
t = get(gca, 'XData');
sample = get(gca, 'YData');
t = max(t) + (1:size(sample,1)) * 1/200;
for x=1:8
set(h,'XData',t,'YData',sample(:,x));
end

Slowing Speed with each for loop iteration in matlab

I have written a while loop in Matlab that is supposed to send each value in an array from Matlab to arduino at a specified time interval using a tic toc delay in Matlab and then read values and store them in a variable and graph them.
The output of the while-loop slows down with each successive iteration.
I increased the buffersize which helped it a lot, but it still slows down too much. Is there another way to increase the speed to print the values on time. I have included another tic toc and graph to show the execution speed here is the code:
max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
pause(5);
y = [];
i = 1;
figure(1);
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
t1 = [];
figure(2);
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');
while (i<=length(x))
t2 = tic;
t = tic;
fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
%disp((['<P' num2str(x1(i)) '>']));
y(i) = fscanf(handles.UltraM,'%d');
figure(1);
hold on;
plot(i, y(i), 'b*');
drawnow;
hold off;
while toc(t) < 0.05
continue
end
t1(i) = toc(t2);
figure(2);
hold on;
plot(i,t1(i),'b*');
drawnow;
hold off;
i = i + 1;
end
After a bit of back and forth I think I know what you're trying to achieve and what stands in your way.
I have edited your code to make it slightly more fast and readable. Most of the time the operations take just slightly above 0.05 seconds, and at several time points it can take about 5 milliseconds longer than expected. Your millage may vary of course. Since I do not have an arduino, I cannot know if there is a bottle neck there. You should also try profiling your code using the builtin Matlab profiler (it's very useful) to see what exactly slows your code down.
The main thing I found to slow your code is that you used the plot function to add one point at a time to your figure. Each time you call this function, it creates a new graphics object. After a few hundred of those, things get sluggish. Instead you should simply update the already plotted data and redraw it using drawnow.
In short, the solution is this:
1) Initialize you plot with a single point and save the graphics handle for later use:
p1 = plot(0,0,'b*');
2) Then, inside the loop, once your data arrays have been updated, replace the data in your existing plot with the new arrays.
set(p1, 'XData', 1:i, 'YData', y(1:i));
3) Redraw the plots to reflect the latest update.
drawnow;
drawnow will eventually slow down your code also since it has to redraw increasingly large plots at every iteration. To make things work faster, you might want to refresh your plot after a longer interval. For example, the following will refresh every 10 iterations:
if rem(i,10) == 0
drawnow;
end
Full code below. Let me know if you have any more issues.
max = 80;
min = 40;
amp = (max-min)/2;
offset = amp + min;
btime = 5;
bpm = 12;
spb = 60/bpm;
sapb = spb/.05;
tosd = sapb*bpm*btime;
time1 = btime*60;
x = linspace(0,time1,tosd)';
x1 = amp*sin(x*(2*pi/20)) + offset;
pause(1);
%fprintf(handles.UltraM,(['<P' num2str(offset) '>']))
disp(['<P' num2str(offset) '>']); % replacing with disp (I don't have an arduino)
pause(5);
%y = []; % unnecessary here, preallocated before loop
figure(1);
p1 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Pressure Data');
xlabel('Data Number');
ylabel('Analog Voltage (0-1023)');
%t1 = []; % unnecessary here, preallocated before loop
figure(2);
p2 = plot(0,0,'b*'); % plotting one dot to create an object, data will be overwritten
hold on;
title('Time to execute task');
xlabel('iteration number');
ylabel('time taken');
% preallocate t1 and y arrays for faster operation
t1 = zeros(size(x));
y = zeros(size(x));
i = 1; % moved closer to loop beginning for better readability
while i <= length(x) % parentheses unnecessary in Matlab
t2 = tic;
t = tic;
%fprintf(handles.UltraM,(['<P' num2str(x1(i)) '>']));
disp((['<P' num2str(x1(i)) '>'])); % replacing with disp (I don't have an arduino)
%y(i) = fscanf(handles.UltraM,'%d');
y(i) = randn; % replacing with random number (I don't have an arduino)
%figure(1); % unnecessary
%hold on; % unnecessary
%plot(i, y(i), 'b*');
% replacing the above with a slightly faster version
set(p1, 'XData', 1:i, 'YData', y(1:i));
%drawnow; % first one is annecessary
%hold off; % unnecessary
while toc(t) < 0.05
continue
end
t1(i) = toc(t2);
%figure(2); % unnecessary
%hold on; % unnecessary
%plot(i,t1(i),'b*');
% replacing the above with a slightly faster version
set(p2, 'XData', 1:i, 'YData', t1(1:i));
if rem(i,10) == 0 % refreshing every 10 iterations
drawnow;
end
%hold off; % unnecessary
i = i + 1;
end
ANSWER TO PREVIOUS VERSION OF QUESTION
You can vectorize your loop by replacing it entirely with the following two statements:
% vectorizing num-to-string conversion
y4 = cellstr(strcat('<P',num2str(x1), '>'));
% deleting all spaces
y4 = cellfun(#(u) u(~isspace(u)), y4, 'UniformOutput', false)
This small adjustment makes your program run x4 faster on my PC.
Displaying/printing the results could also be done using the cellfun iterator: cellfun(#disp, y4)