Why is subplot much faster than figure? - performance

I'm building a data analysis platform in MATLAB. One of the system's features need to create many plots. At any given time only one plot is available and the user can traverse to the next/previous upon request (the emphasis here is that there is no need for multiple windows to be open).
Initially I used the figure command each time a new plot was shown, but I noticed that, as the user traverse to the next plot, this command took a bit longer than I wanted. Degrading usability. So I tried using subplot instead and it worked much faster.
Seeing this behavior I ran a little experiment, timing both. The first time figure runs it takes about 0.3 seconds and subplot takes 0.1 seconds. The mean run time for figure is 0.06 seconds with standard deviation of 0.05, while subplot take only 0.002 with standard deviation of 0.001. It seems that subplot is an order of magnitude faster.
The question is: In situation when only one window will be available at any given time, is there any reason to use figure?
Is there any value lost in using `subplot' in general?
(similar consideration can be made even if you can either only once).

The call of subplot does nothing else than creating a new axes object with some convenient positioning options wrapped around.
Axes objects are always children of figure objects, so if there is no figure window open, subplot will open one. This action takes a little time. So instead of opening a new figure window for every new plot, it's faster to just create a new axes object by using subplot, as you determined correctly. To save some memory you can clear the previous plot by clf as suggested by Daniel.
As I understood, you don't want to Create axes in tiled positions, rather you just want to create one axes object. So it would be even faster to use the axes command directly. subplot is actually overkill.
If all your plots have the same axes limits and labels, even clf is not necessary. Use cla (clear axes) to delete the previous plot, but keep labels, limits and grid.
Example:
%// plot #1
plot( x1, y2 );
xlim( [0,100] ); ylim( [0,100] );
xlabel( 'x' );
ylabel( 'y' );
%// clear plot #1, keep all settings of axes
%// plot #2
plot( x2, y2 );
...

Use figure once to create a figure and clf to clear it's content before repainting.

Related

How to Plot Several 3D Trajectories in Julia (using a for-loop, presumably?) -- also, how to animate over time?

I am trying to plot a set of 3D trajectories in a single plot in Julia. By 3-D trajectories I mean: different sets of 3-D coordinates over time. These trajectories are stored in a multidimensional array called positions, where the dimensions respectively correspond to the Trajectory ID, X-Y-Z coordinate and Time. For example, positions[75,2,1:100] refers to the Y (2nd) coordinate of the 75th Trajectory, across the first 100 timesteps of the trajectory.
I am trying to figure out why the following code doesn't work:
using Plots
plotlyjs()
time_indices = 1:100
ax= scatter3d(positions[1,1,time_indices],positions[1,2,time_indices],positions[1,3,time_indices],label="Trajectory 1 for times 1 to 100")
for n in 2:size(positions,1)
scatter3d!(ax, positions[n,1,time_indices], positions[n,2,time_indices],positions[n,3,time_indices],label="Trajectory $n for times 1 to 100")
end
When I run that code, I don't see anything in the Plots window (I'm using Atom), although I don't get any errors / it appears to run successfully. Any thoughts on what I'm doing wrong? Should I use a different backend? It doesn't work on either gr() or plotlyjs() (those are the only ones I know of, based on tutorials I've completed).
Follow up question: once I can successfully plot such 3-D trajectories in a single static plot, I am wondering how you would go about animating them over time (using #gif or #animate macros, presumably)? I am asking this here, because I wasn't able to understand the documentation / tutorial on 3D animations, unfortunately. Googling / other sources have also not helped :(
It looks like you're just missing a display(ax) at the end, after the loop.
Edit: to animate, try
anim = #animate for n in 1:size(positions,1)
scatter3d(positions[n,1,time_indices], positions[n,2,time_indices],positions[n,3,time_indices],label="Trajectory $n for times 1 to 100")
end
gif(anim, "some_file_name.gif", fps=15)
(or if you want the prior trajectories to show up as well in later frames of the gif, then replace the scatter3d with scatter3d!)
I can't test the above without having your positions, but here is another example that I have just tested on Julia 1.5.0beta with Plots and the GR backend:
using Plots; gr();
anim = #animate for i=1:100
plot(sin.(range(0,i/10*pi,length=1000)), label="", ylims=(-1,1))
end
gif(anim, "anim_fps15.gif", fps=15)
How to do the second bit (the animation - see details in comments on the accepted answer):
gr()
anim = #animate for t in time_indices
all_positions_t = positions[:,:,t] # positions of all trajectories at time t
scatter3d(all_positions_t[:,1], all_positions_t[:,2],all_positions_t[:,3],label="")
end
gif(anim, "some_file_name.gif", fps=15)

How to reduce 10k data points and show them on smaller display? Arduino

I‘m working on a visual data logger for my DMM, it writes every measurement to RS232 inteface. There I connect a Teensy 3.6 and collect the data points.
For each point I have the timestamp and the measured value. I will collect 10.000 readings.
I want to display the measured data on a display (800x480) in two ways. First as a rolling graph, that scrolls from right to left and shows the last minute or so. This is working fine.
Second, I want to display all collected measurements in total (max. 10k points). So I have to shrink or compress the data, but I want to preserve the shape of the curve.
To give you an idea how it should look like, please watch the video from Dave on EEV at YT (https://youtu.be/SObqPuUozNo) and skip to 41:20. There you see how another DMM is shrinking the incomming data and displays it. At about 1:01:05 10k measurements are shown on the display area of only 400px wide.
Question is, how is this done?
I’ve heard about Douglas-Pucker algorithm, but have no idea if this is the right way and how to use it on the Arduino/ Teensy platform.
Any help is very welcome, thank you....
I cannot just display all data points, because I‘m using an FT81x as display controller, and this can take only up to 2000 drawing commands per frame. And it takes more time.
Anyway, I solved the problem using the simple way.
I create bins and calculate the min and max values in this bin. Then simply draw a line between these points. Works fine!
BTW, I‘m the TO :-)
For cases where you got many more samples than pixels in x axis instead of LineTo like graph use vertical lines graph instead...
So depending on the number of samples per rendered time frame and x resolution you should compute ymin and ymax for eaxch x and render vertical line ...
something like:
xs=800;
for (x0=x,i=sample_left,y0=y1=sample[i],i<sample_right;i++)
{
x = (i-sample_left)*xs/(sample_right-sample_left);
y = sample[i]; // here add y scaling and offset
if (x0!=x) { line(x0,y0,x0,y1); x0=x; y0=y; y1=y; }
if (y0>y) y0=y;
if (y1<y) y1=y;
}
where sample[] are your stored values , sample_left,sample_right is the range to render and xs is graph x resolution. To speed up you can pre-compute the y0,y1 for each x and render that (recompute only on range or samples change) ... So as you can see you will use just xs line commands which shoul dbe fast enough. The x linear interpolation can be done without multiplication nor division if you rewrite it to integer DDA style ...
These QAs might interest you:
plotting real time Data on (qwt )Oscillocope
i don't really understand FFT and sample rates
[note]
After a second look The deleted answer is with the same approach as this (got deleted by review probably before the edit which transformed it from not an answer (comment) to the correct answer) so I voted for undelete even if it is considerably lower quality than mine but was posted sooner.

To make all peaks clearly visible in Matlab

I finally solved my problem here with lennon310.
I have a picture of thousands of thin peaks in Time-Frequency picture.
I cannot see all the same time in one picture.
Depending on the physical width of my time window, some windows appear and some come visible.
Pictures of my data which I plot by imagesc
All pictures are from the same data points T, F, B.
How can you plot all peaks at once in a picture in Matlab?
You need to resize the image using resampling to prevent the aliasing effect (that craigim described as unavoidable).
For example, the MATLAB imresize function can perform anti-aliasing. Don't use the "nearest" resize method, that's what you have now.
Extension to #BenVoigt's answer
My try
B = abs(B);
F1 = filter2(B,T); % you need a different filter kernel because resolution is lower
T = filter2(B,F);
F = F1;
image1 = imagesc(B);
display1 = imresize(image1, [600 600], 'bilinear');
imshow(T*t, F*fs, display1);
where are some problems.
I get again picture where the resolution is not enough
2nd Extension to BenVoigt's answer
My suggestion for one kernel filter is with convolution of relative random error
data(find(data ~= 0)) = sin(pi .* data(find(data ~= 0))) ./ (pi*data(find(data ~= 0)));
data(find(data == 0)) = 1; % removing lastly the discontinuity
data1 = data + 0.0000001 * mean(abs(data(:))) * randn(size(data));
data = conv(data, data1);
Is this what BenVoigt means by the kernel filter for distribution?
This gives results like
where the resolution is still a problem.
The central peaks tend to multiply easily if I resize the window.
I had old code active in the above picture but it did not change the result.
The above code is still not enough for the kernel filter of the display.
Probably, some filter functions has to be applied to the time and frequency axis separately still, something like:
F1 = filter2(B,T); % you need a different kernel filter because resolution is lower
T = filter2(B,F);
F = F1;
These filters mess up the values on the both axis.
I need to understand them better to fix this.
But first to understand if they are the right way to go.
The figure has be resized still.
The size of the data was 5001x1 double and those of F and T 13635x1 double.
So I think I should resize lastly after setting axis, labels and title by
imresize(image, [13635 13635], 'bilinear');
since the distirbution is bilinear.
3rd Extension to BenVoigt's answer
I plot the picture now by
imagesc([0 numel(T)], [0 numel(F)], B);
I have a big aliasing problem in my pictures.
Probably, something like this should be to manipulate the Time-Frequency Representation
T = filter2(B,t); % you need a different filter kernel because resolution is lower
F = filter2(B,fs);
4th extension to BenVoigt's answer and comment
I remove the filters and addition of random relative errors.
I set the size of T,F,B, and run
imagesc([0 numel(T)], [0 numel(F)], B, [0 numel(B)])
I get still with significant aliasing but different picture
This isn't being flippant, but I think the only way is to get a wider monitor with a higher pixel density. MATLAB and your video card can only show so many pixels on the screen, and must decide which to show and which to leave out. The data is still there, it just isn't getting displayed. Since you have a lot of very narrow lines, some of them are going to get skipped when decisions are made as to which pixels to light up. Changing the time window size changes which lines get aliased away and which ones get lucky enough to show up on the screen.
My suggestions are, in no particular order: convolute your lines with a Gaussian along the time axis to broaden them, thus increasing the likelihood that part of the peak will appear on the screen. Print them out on a 600 dpi printer and see if they appear. Make several plots, each zooming in on a separate time window.
3rd Extension to BenVoigt's answer
My attempt to tell imagesc how big to make the image, instead of letting to use the monitor size, so it won't away data.
imagesc(T*t, F*fs, B, [50 50]);
% imagesc(T*t, F*fs, B');
% This must be last
imresize(image, 'bilinear', [64 30],);
I am not sure where I should specify the amount of pixels in x-axis and y-axis, either in the command imagesc or imresize.
What is the correct way of resizing the image here?

Matlab GUI roipoly on multiple axes

If I've multiple axes on my Matlab GUI window, and each one of them has a different image.
How can I manage to do roipoly function on each of them ?
This solution should be without recalling imshow then roiploy after it for each one of them since:
All images are show on axes using a loop that if I processed axes one after one the GUI would appear so ugly.
Re-doing imshow will cost time as images are very large and they are processed each time before being shown, so it'll be a ver bad solution to waste time in a repeated processing.
Use impoly instead of roipoly:
polyH = impoly( hAxes );
position = wait( polyH );
Please see Matlab's doc on impoly for more functionality.

Animate trajectory using Octave

I have a set of (x,y) coordinates that describe the trajectory of an object. I'd like to animate this trajectory using GNU Octave.
The data set is quite large so I won't be able to redraw the entire plot at every iteration if I want the animation to be smooth. What functions are there that would allow me to "update" a plot rather than redraw it?
Also, I have another set of (vx,vy) points, which describe the speed of the object. I'd like my animated trajectory to take speed into account. What function should I use to have the program sleep for a couple of milliseconds as to make the trajectory animate at the same speed as the object?
(I already know Octave has functions such as comet, but I need to write my own animator.)
Edit: Here's what I have up until now. I expected this to run too fast and require me to use pause, but it's still pretty slow (x and y have 10001 elements).
bounds = [min(x) max(x) min(y) max(y)];
axis(bounds);
hold on
for k = 2 : length(x)
plot(x(k-1:k), y(k-1:k));
drawnow("expose");
end
hold off
You can use the set command to change just the XData and YData data for a certain plot object h:
h = plot(my_xdata(0),my_ydata(0))
for i_=1:length(my_xdata)
set(h, 'YData', my_ydata(i_))
set(h, 'XData', my_xdata(i_))
pause(sqrt(vx(i_)^2+vy(i_)^2))
end
The pause(x) command pauses for x seconds, which can be less than 1.
I think you are looking for the "hold" command. holding the plot keeps all previous data on the plot and the new data is added on top.

Resources