MATLAB how to plot a discrete function without vertical lines, only levels - matlab

I have something like this:
t = [-1 0 1 2 3 4 5];
ft= [ 0 0 0 0 1 1 1];
I want to plot only horizontal levels high\low without vertical lines:

If you don't mind the vertical lines, it's really simple to just use the stairs(x,t) function. Otherwise you can create your own function that deals with pairs of points to generate lines and plot them all individually using hold on.
function stairs2(x,y)
hold on;
for i=1:length(x)-1
plot(x(i:i+1),[y(i) y(i)]);
end
hold off;
end
Then just call stairs2(x,t) as per your example above, and set appropriate zoom/axes.
Alternately, this is a different way that only uses ONE call to plot:
function stairs2(x,y)
for i=1:length(x)-1
A(:,i) = [x(i) x(i+1)];
B(:,i) = [y(i) y(i)];
end
plot(A,B,'b');
end

Related

A question about drawing a surface plot in MATLAB

I wrote a matlab code for topology optimization. The code showing the results in a surface plot is:(a very simple example)
XYZG=[2.5 0 0;2.5 0 2.5;2.5 0 5;0 -2.5 0;0 -2.5 2.5;0 -2.5 5;-2.5 0 0;-2.5 0 2.5;-2.5 0 5];
ELNOD=[1 4 5 2;2 5 6 3;4 7 8 5;5 8 9 6];
nelement=4;
Dens=[0 1 1 0];
for element=1:nelement
for i=1:4
X(i)=XYZG(ELNOD(element,i),1);
Y(i)=XYZG(ELNOD(element,i),2);
Z(i)=XYZG(ELNOD(element,i),3);
end
XX=[X(1) X(2);X(4) X(3)];
YY=[Y(1) Y(2);Y(4) Y(3)];
ZZ=[Z(1) Z(2);Z(4) Z(3)];
tick=-Dens(element)*[1 1;1 1];
figure(1)
hold on
surf(XX,YY,ZZ,tick);
colormap gray;
end
This code is too slow. If I have for example 10,000 elements it takes a long time for drawing plot.
So I would appreciate any help on how to make this faster.
1] Minor improvement:
The code you gave as a sample can be simplified to avoid temporary assignment. Consider the following:
%% Sample data
XYZG=[2.5 0 0;2.5 0 2.5;2.5 0 5;0 -2.5 0;0 -2.5 2.5;0 -2.5 5;-2.5 0 0;-2.5 0 2.5;-2.5 0 5];
ELNOD=[1 4 5 2;2 5 6 3;4 7 8 5;5 8 9 6];
nelement=4;
Dens=[0 1 1 0];
%% pre-initialisation just to set the size (size=[2,2])
XX = zeros(2) ;
YY = XX ;
ZZ = XX ;
figure(1)
hold on
colormap gray;
for element=1:nelement
% Base vector combining (unused now)
% idx=1:4;
% X = XYZG( ELNOD(element,idx),1 ).';
% Y = XYZG( ELNOD(element,idx),2 ).';
% Z = XYZG( ELNOD(element,idx),3 ).';
% The block above is commented because we do not need these intermediate
% vectors to build the base matrices XX, YY and ZZ.
% This can be done directly using linear indexing:
idxOrder = [1 4 2 3] ;
XX(1:4) = XYZG( ELNOD(element,idxOrder) , 1 ) ;
YY(1:4) = XYZG( ELNOD(element,idxOrder) , 2 ) ;
ZZ(1:4) = XYZG( ELNOD(element,idxOrder) , 3 ) ;
tick=-Dens(element)*[1 1;1 1];
surf(XX,YY,ZZ,tick) ;
end
This should run marginally faster. Thanks to the avoidance of a few temporary arrays. We are now building each patch coordinates more directly. Also the function calls which only need to be used once have been taken out of the loop (something to generally look for if your loop is taking too long).
Now this is not going to be satisfying anyway. The real bottleneck in your structure is not so much the calculations/indexing in each loop iteration, it is the growing number of graphic handles that the system has to maintain. Every iteration of your loop create a surface object. These objects require memory to keep their coordinates, but also to maintain the large number of internal properties. Once you multiply these objects, your system will start to slow down. Some systems may not even be able to create 10,000 surface graphic objects on the same figure, and if they can it's going to be sluggishly painful (you know these situations where you click on the screen and wait ~25s to notice any reaction ...).
2] Major improvement:
One way to limit the number of graphic object would be to combine all of these cordinates in order to create a single graphic object. Of course we'd then have to color each face according to your rule.
Fortunately, I noticed that your base coordinates are actually organised perfectly to inject directly as a patch in Matlab. SO no need to demultiplex/remultiplex the data in and out, we can directly create and color a global patch:
% create a Black and White colormap
cmap = [1 1 1;
0 0 0] ;
figure
% generate a patch with all the 'faces','vertices' and 'color' data :-)
hp = patch('Faces',ELNOD, 'Vertices',XYZG, 'FaceVertexCData',Dens(:) ) ;
% last refinement to have the same appearance than in former code
shading flat
colormap(cmap)
hp.EdgeColor = 'k' ; % <= this needs to be executed AFTER "shading flat"
Voila !! No need any loop or any calculations. You already had all the data needed in the first place ;-)
I'd encourage you to read the documentation for patch, especially the way to use the property FaceVertexCData

In MATLAB: How can XData and YData be updated with a changing number of lines?

I am looking for a way to add vertical dividing lines separating consecutive days in a dynamically updating plot of animal migration data (location vs. time). Part of the challenge is that the number of these dividers changes as the plot domain expands to display more temporal data: As the number of days in the plot increases from 3 to 5, for example, the number of dividers increases by 2.
A minimal code example, written in MATLAB, is shown below:
xcols = [1; 1];
ycols = [0; 1];
figure(4)
clf
h.divs = plot(xcols,ycols,':k');
xlabel('time')
ylabel('location')
for ii=2:6
xcols(:,end+1) = [ii; ii];
ycols(:,end+1) = [0; 1];
% set(h.divs, 'XData', xcols, 'YData', ycols);
% set(h.divs, {'XData'}, num2cell(xcols',2), {'YData'}, num2cell(ycols',2));
drawnow
pause(1)
end
The problem centers on the two lines that have been commented out. If I comment in the first of these to try to update XData and YData with each new set of dividers (given as the 2 x DividerCount matrices xcols and ycols), then I receive an error that these inputs "must be a vector of numeric type". If I instead comment in the second line as way of using cell arrays to get around this (per this Stack Overflow post and this MATLAB Newsgroup post), then the code returns an error that "cell array handle dimension must match handle vector length" as soon as the number of dividers changes.
Hacky solutions are certainly possible. For example, the dividers can be plotted as a single line of horizontal and vertical segments, where the horizontal segments are placed above and below the y-axis limits of the plot. Or a fixed number of dividers can be used, with some of the dividers plotted outside the x-axis limits of the plot. The question is whether there is a non-hacky approach – one that can plot a potentially changing number of lines of identical style in the same figure with each pass of the loop.
Assume we have at the beginning
xcols = [1:3; 1:3];
ycols = [0 0 0; 1 1 1];
then h.divs = plot(xcols,ycols,':k'); will create 3 line objects.
Using
set(h.divs,{'XData'},num2cell(xcols',2),{'YData'},num2cell(ycols',2));
with size(xcols, 2)>3 will fail, because this assumes there are more than 3 graphics objects to set values for (but numel(h.divs) is still 3).
So do we have to create a new plot for every divider?
No!, because if we insert NaNs into our data, we can introduce gaps into lines.
As a first iteration, we could use this:
xcols = [1 1];
ycols = [0 1];
figure(4)
clf
h.divs = plot(xcols,ycols,':k');
for ii=2:6
xcols(end+(1:3)) = [nan ii ii];
ycols(end+(1:3)) = [nan 0 1];
set(h.divs, 'XData', xcols, 'YData', ycols);
drawnow
pause(1)
end
Starting with one divider which we plot, we then grow the data vectors (one-dimensional!) with a new value pair separated from the old data by a nan.
This of course grows the data vectors in the loop, which is not so great. Instead, if we know how many dividers there will be, we can preallocate with NaNs and only fill in new data in the loop:
n_div = 6;
xcols = nan(3 * n_div, 1);
ycols = nan(3 * n_div, 1);
figure(4)
clf
h.divs = plot(xcols,ycols,':k');
for ii=1:6
xcols((ii - 1)*3 + (1:2)) = [ii ii];
ycols((ii - 1)*3 + (1:2)) = [0 1];
set(h.divs, 'XData', xcols, 'YData', ycols);
drawnow
pause(1)
end
This has also the benefit that we can assign XData and YData separately, as in the new handle syntax:
h.divs.XData = xcols;
h.divs.YData = cols;
(or even better: h.divs.XData((ii - 1)*3 + (1:2)) = [ii ii];), because the lengths don't change.
You can start with divider definition:
divider.x=[];
divider.y=[];
divider.counter=0;
h.div=line('xdata',divider.x,'ydata',divider.y,'linestyle',':','color','k');
Which will draw no line, but handle will be set.
Then, perhaps in some loop, you can call:
divider.counter=divider.counter+1; % increase the counter
divider.x=[divider.x,nan,divider.counter*[1 1]]; % append X coords of new divider line
divider.y=[divider.y,nan,divider.counter*[0 1]]; % append Y coords of new divider line
set(h.div,'xdata',divider.x,'ydata',divider.y) % update dividers
drawnow % update figure immediately
This approach works because NaN value can be passed to line function but will not be plotted and neither will be lines from neighbour points.

How can I plot filled rectangles as a backdrop for a desired target in MATLAB?

I have two datasets, one of which is a target position, and the other is the actual position. I would like to plot the target with a +/- acceptable range and then overlay with the actual. This question is only concerning the target position however.
I have unsuccessfully attempted the built in area, fill, and rectangle functions. Using code found on stackoverflow here, it is only correct in certain areas.
For example
y = [1 1 1 2 1 1 3 3 1 1 1 1 1 1 1]; % Target datum
y1 = y+1; %variation in target size
y2 = y-1;
t = 1:15;
X=[t,fliplr(t)]; %create continuous x value array for plotting
Y=[y1,fliplr(y2)]; %create y values for out and then back
fill(X,Y,'b');
The figure produced looks like this:
I would prefer it to be filled within the red boxes drawn on here:
Thank you!
If you would just plot a function y against x, then you could use a stairs plot. Luckily for us, you can use the stairs function like:
[xs,ys] = stairs(x,y);
to create the vectors xs, ys which generate a stairs-plot when using the plot function. We can now use these vectors to generate the correct X and Y vectors for the fill function. Note that stairs generates column vectors, so we have to transpose them first:
y = [1 1 1 2 1 1 3 3 1 1 1 1 1 1 1]; % Target datum
y1 = y+1; %variation in target size
y2 = y-1;
t = 1:15;
[ts,ys1] = stairs(t,y1);
[ts,ys2] = stairs(t,y2);
X=[ts.',fliplr(ts.')]; %create continuous x value array for plotting
Y=[ys1.',fliplr(ys2.')]; %create y values for out and then back
fill(X,Y,'b');
Again, thank you hbaderts. You answered my question perfectly, however when I applied it to the large data set I needed for, I obtained this image
https://dl.dropboxusercontent.com/u/37982601/stair%20fill.png
I think it is because the fill function connects vertices to fill?
In any case, for the potential solution of another individual, combined your suggested code with the stair function and used the area function.
By plotting them on top of one another and setting the color of the lower area to be white, it appears as the rectangular figures I was after.
%sample code. produces image similar to o.p.
y = [1 1 1 2 1 1 3 3 1 1 1 1 1 1 1];
y1 = y+1;
y2 = y-1;
t = 1:15;
[ts,ys1] = stairs(t,y1);
[ts,ys2] = stairs(t,y2);
area(ts,ys1,'FaceColor','b','EdgeColor','none')
hold on
area(ts,ys2,'FaceColor','w','EdgeColor','none')
https://dl.dropboxusercontent.com/u/37982601/stair%20area.png
Thanks again for your help and for pointing me in the right direction!

Plot vertical lines at a certain time

I want to plot a line at a certain "x" (being a scalar) value so every second, a vertical line with certain values will be drawn. I know that one way to plot a vertical line (without considering time) is to declare a vector like:
y=0:0.01:5;
or something like:
y=3:0.01:6;
and write plot(x,y);
Also, I've done "animated" plots using the "pause" parameter but I don't know how to do that in this case. Thanks for your help.
Another way I can suggest is to use plot in combination with hold on. With plot, you'd only specify two points where each point has the same x value, but the y value can change to whatever you want. When you draw points using plot, the default behaviour is that a line is drawn in between the points. If we specify two points having the same x coordinate, but different y coordinates, we would essentially draw a vertical line in between these points.
For example, given that you want to have every "second" have a vertical line, we can do something like this:
ystart = [-1 -2 -3 -1 -2 -3];
yend = [1 2 3 1 2 3];
figure; hold on;
for idx = 1 : numel(ystart)
plot([idx idx], [ystart(idx) yend(idx)]);
end
This is what we get:
We define two arrays of 6 elements where ystart denote the starting y point and yend denote the ending y point. We spawn a new figure, use hold on to plot multiple lines on the same graph, then use a for loop with plot so that we draw a line in between two points: (x,y) = (idx, ystart(idx)) and (x,y) = (idx, yend(idx)). idx goes from 1 to 6. Obviously, you can change the location of where the x values are being plotted by specifying another array... call it x:
x = 0:2:10; %// Time values
ystart = [-1 -2 -3 -1 -2 -3];
yend = [1 2 3 1 2 3];
figure; hold on;
for idx = 1 : numel(ystart)
plot([x(idx) x(idx)], [ystart(idx) yend(idx)]);
end
Here, we will draw vertical lines starting from x = 0 up to x = 10 in steps of 2. The result would basically be the same, but the x values would be different, as well as where the vertical lines are being drawn.
There are several ways to plot vertical lines in Matlab. The easiest recommendation is the line function:
line(X,Y) adds the line defined in vectors X and Y to the current
axes. If X and Y are matrices of the same size, line draws one line
per column.
Call this as many times as you want.
h=line(X,Y)
will give you properties of the line
Another way is to
X= X0*(Y./Y)
then
plot(X,Y)
will plot a vertical line at the point X0. Another thing you can do is draw a line using the above line function from the point (X0, min(Y)) to the point (X0, max(Y)) which is the most elegant solution. If you are trying to create a movie, you will need to access the properties of this line using h=line(X,Y). To move the line to a new position, you will have to set the properties of this line by calling set(h, Property, value). For example in your movie, you need to move it to a new position so you will set that property. This way, by minimum change of data, you can move a line or show its accelerating.

How to plot function with different color for different interval?

I have a typical scenario in which there is a Vector X and Vector Y. Vector X contains increasing values, for example X = [1 1 1 2 2 3 4 4 4 4 4]. Vector Y contains real values of same size as X. Im looking to plot Index Vs Y with color change for each different value of X for the corresponding index.
For example, the plot should have color1 for the first 3 values of 1, color2 for the next 2 values of 2, color3 for 1 value 3 and so on.
Can any one help me
Building on Laurent's answer and implementing your "Index vs Y" requirement,
function color_plot(data_vector, color_vector)
styles={'ro','g.','bx','kd'};
hold off;
for i=unique(color_vector)
thisIdx=find(color_vector==i);
thisY=data_vector(color_vector==i);
thisStyle=styles{mod(i-1,numel(styles))+1};
plot(thisIdx,thisY,thisStyle);
hold on;
end
hold off;
My version also allows arbitrarily large color indices; if you don't have enough styles defined, it just wraps back around and reuses colors.
Update note I had to fix a sign above in the calculation oh thisStyle.
Testing it with
X = [1 1 1 2 2 3 4 4 4 4 4];
Y=rand(size(X))
color_plot(Y,X)
now gives
A plot() function option would be better (and maybe it exists).
Here's a workaround function to do this:
function colorPlot( data_vector, colors_vector)
%PLOTCOL plots data_vector with colors found in colors_vector
Styles=[{'r-'} {'g-'} {'b-'} {'k-'}];
last_off=0;
last_data=0;
for i=unique(colors_vector)
data_segment=data_vector(colors_vector==i);
len=length(data_segment);
if last_off==0
hold off;
plot( data_segment, 1:len,char(Styles(i)));
last_off=len;
else
plot([last_data data_segment],last_off:last_off+len,char(Styles(i)));
last_off=last_off+len;
end
last_data=data_segment(len);
hold on;
end
hold off;
end
Call it this way :
colorPlot(Y,X);