Related
I'm trying to plot a simple graph using for loop as shown below
x=linspace(0,2*pi,100);
for i=1:numel(x)
y=sin(x(i));
plot(x(i),y)
hold on
end
However, nothing appear on my figure. Why is that?
Why this happens...
With plot(x(i),y) you are plotting 100 single points (one in each iteration) and they are not shown by default. Therefore the plot looks empty.
Solution 1: Vectorized calculation and direct plot
I assume you meant to draw a continuous line. In that case no for-loop is needed because you can calculate and plot vectors directly in MATLAB. So the following code does probably what you want:
x = linspace(0,2*pi,100);
y = sin(x);
plot(x,y);
Note that y is a vector as well as x and that y(n) equals to sin(x(n)) for all n. If you want to plot the points itself, use LineSpec-syntax when calling plot like this1:
plot(x,y,'*');
1) Other types of points are possible as well, see the above linked documentation.
Solution 2: Calculate values within for-loop and plot afterwards
If you want to calculate the values within a for-loop and plot it afterwards: Pre-allocate the needed variable (in this case y), calculate the values within the for-loop and finally plot it with one single command after the calculation.
x = linspace(0,2*pi,100);
y = zeros(size(x));
for i = 1:numel(x)
y(i) = sin(x(i));
end
plot(x,y);
Solution 3: Dynamically update plot while calculating
In case you insist on plotting within each iteration, the previous code from Solution 2 can be expanded as follows: Create a figure, add an 'empty' plot to it and store its handle. Within the for-loop calculate the values and add them to the y-vector as shown above. As a last step you can update the plot by changing its XData and YData properties and calling drawnow. Note that calling plot every time within the for-loop is unnecessarily expensive and I don't recommend it.
% create figure and plot
figure;
ph = plot(0,0);
ax = gca;
set(ax,'XLim',[0,2*pi]);
set(ax,'YLim',[-1,1]);
% calculate and update plot
x = linspace(0,2*pi,100);
y = zeros(size(x));
for i = 1:numel(x)
y(i) = sin(x(i));
set(ph,'XData',x(1:i));
set(ph,'YData',y(1:i));
drawnow;
end
Simple approach
If you want to draw a curve as you add data, try the following:
x = linspace(0,2 * pi, 100);
y = zeros(size(x));
for i=1:numel(x)
y(i) = sin(x(i));
plot(x(1:i), y(1:i), 'color', 'r')
drawnow();
end
Be aware that the plot automatically tries to set x and y limits (curve is scaled to the plot window), to prevent that you have to manually set the x- and y-limits with xlimand ylim.
As Matt wrote in his answer, calling plot in each iteration is quite expensive (i.e. time consuming). Therefore I suggest using datasources:
Update graph using data sources
% Create a panel and axes object
h_panel = uipanel;
h_axes = axes( 'Parent', h_panel);
% Create data sources
x = linspace(0,2 * pi, 100);
y = zeros(size(x));
% Create graph object, in this case stairs
% and bind the variables x and y as its data sources
h_stairs = stairs(h_axes, x, y, 'XDataSource', 'x', 'YDataSource', 'y');
for i=1:size(x)
y(i) = sin(x(i));
% Update the data of the stairs graph
refreshdata(h_stairs);
drawnow();
end
The call to drawnow isn't neccessary in each iteration, it is only used to update the visuals, so you can see the changes directly.
I created two scatter plots and then used lsline to add regression lines for each plot. I used this code:
for i=1:2
x = ..;
y = ..;
scatter(x, y, 50, 'MarkerFaceColor',myColours(i, :));
end
h_lines = lsline;
However, the darker line extends far beyond the last data point in that scatter plot (which is at around x=0.3):
lsline doesn't seem to have properties that allow its horizontal range to be set. Is there a workaround to set this separately for the two lines, in Matlab 2016a?
For a single data set
This is a workaround rather than a solution. lsline internally calls refline, which plots a line filling the axis as given by their current limits (xlim and ylim). So you can change those limits to the extent you want for the line, call lsline, and then restore the limits.
Example:
x = randn(1,100);
y = 2*x + randn(1,100); % random, correlated data
plot(x, y, '.') % scatter plot
xlim([-1.5 1.5]) % desired limit for line
lsline % plot line
xlim auto % restore axis limit
For several data sets
In this case you can apply the same procedure for each data set sequentially, but you need to keep only one data set visible when you call lsline; otherwise when you call it to create the second line it will also create a new version of the first (with the wrong range).
Example:
x = randn(1,100); y = 2*x + randn(1,100); % random, correlated data
h = plot(x, y, 'b.'); % scatter plot
axis([min(x) max(x) min(y) max(y)]) % desired limit for line
lsline % plot line
xlim auto % restore axis limit
hold on
x = 2*randn(1,100) - 5; y = 1.2*x + randn(1,100) + 6; % random, correlated data
plot(x, y, 'r.') % scatter plot
axis([min(x) max(x) min(y) max(y)]) % desired limit for line
set(h, 'HandleVisibility', 'off'); % hide previous plot
lsline % plot line
set(h, 'HandleVisibility', 'on'); % restore visibility
xlim auto % restore axis limit
Yet another solution: implement your own hsline. It's easy!
In MATLAB, doing a least squares fit of a straight line is trivial. Given column vectors x and y with N elements, b = [ones(N,1),x] \ y; are the parameters to the best fit line. [1,x1;1,x2]*b are the y locations of two points along the line with x-coordinates x1 and x2. Thus you can write (following Luis' example, and getting the exact same output):
N = 100;
x = randn(N,1); y = 2*x + randn(N,1); % random, correlated data
h = plot(x, y, 'b.'); % scatter plot
hold on
b = [ones(N,1),x] \ y;
x = [min(x);max(x)];
plot(x,[ones(2,1),x] * b, 'b-')
x = 2*randn(N,1) - 5; y = 1.2*x + randn(N,1) + 6; % random, correlated data
plot(x, y, 'r.') % scatter plot
b = [ones(N,1),x] \ y;
x = [min(x);max(x)];
plot(x,[ones(2,1),x] * b, 'r-')
You can get the points that define the line using
h_lines =lsline;
h_lines(ii).XData and h_lines(ii).YData will contain 2 points that define the lines for each ii=1,2 line. Use those to create en equation of a line, and plot the line in the range you want.
I have three matrices x, y, z which are plotted via scatter3 in matlab. However I also need vertical lines dropping from every point in the graph for better visualization.
Using matlab 2017a, implemented 3D scatter plot in matlab.
enter code here
clc;
figure
x = [0,0,0,0,0,10,10,10,10,10];
y = [0,10,20,30,40,-10,0,10,20,30];
z = [46,52,51,59,53,85,56,87,86,88];
scatter3(x, y, z, 30, 'filled')
You could also use the built in function stem, which is doing exactly that.
The minor trick is that you cannot pass the z coordinates in the shorthand form stem(x,y,z), but the graphic object still accept z data, you just have to send them as additional parameter.
The nice part of it is you do not need a loop ;-)
x = [0,0,0,0,0,10,10,10,10,10];
y = [0,10,20,30,40,-10,0,10,20,30];
z = [46,52,51,59,53,85,56,87,86,88];
hp = stem(x,y,'filled','ZData',z) ;
Or as Gnovice nicely pointed out, even easier to use the stem3 function which accept z data directly:
hp = stem3(x,y,z,'filled') ;
Both example above will produce:
As #SardarUsama pointed out, plot3 should do the trick. Code could be more compact but kept it as is for clarity.
% MATLAB R2017a
x = [0,0,0,0,0,10,10,10,10,10];
y = [0,10,20,30,40,-10,0,10,20,30];
z = [46,52,51,59,53,85,56,87,86,88];
figure
scatter3(x, y, z, 30, 'filled') % scatter plot (3D)
zRng = zlim;
hold on
for k = 1:length(x)
xL = [x(k) x(k)];
yL = [y(k) y(k)];
zL = [zRng(1) z(k)];
plot3(xL,yL,zL,'r-') % plot vertical line (3D)
end
I have to plot 1 line plot and 3 grouped scatter plots in a single plot window.
The following is the code I tried,
figure;
t1=0:0.1:10;
X = 2*sin(t1);
ts = 0:1:10;
Y1 = randi([0 1],length(ts),1);
Y2 = randi([0 1],length(ts),1);
Y3 = randi([0 1],length(ts),1);
plotyy(t1,X,[ts',ts',ts'],[Y1,Y2,Y3],'plot','scatter');
%plotyy(t1,X,[ts',ts',ts'],[Y1,Y2,Y3],'plot','plot');
The following are my questions,
The above code works if I replace 'scatter' by 'plot' (see commented out line), but 'scatter' works only for 1 data set and not for 3. Why?
How to individually assign colors to the 3 grouped scatter plots or plots?
Read the error message you're given:
Error using scatter (line 44) X and Y must be vectors of the same
length.
If you look at the documentation for scatter you'll see that the inputs must be vectors and you're attempting to pass arrays.
One option is to stack the vectors:
plotyy(t1,X,[ts';ts';ts'],[Y1;Y2;Y3],'plot','scatter');
But I don't know if this is what you're looking for, it certainly doesn't look like the commented line. You'll have to clarify what you want the final plot to look like.
As for the second question, I would honestly recommend not using plotyy. I may be biased but I've found it far to finicky for my tastes. The method I like to use is to stack multiple axes and plot to each one. This gives me full control over all of my graphics objects and plots.
For example:
t1=0:0.1:10;
X = 2*sin(t1);
ts = 0:1:10;
Y1 = randi([0 1],length(ts),1);
Y2 = randi([0 1],length(ts),1);
Y3 = randi([0 1],length(ts),1);
% Create axes & store handles
h.myfig = figure;
h.ax1 = axes('Parent', h.myfig, 'Box', 'off');
h.ax2 = axes('Parent', h.myfig, 'Position', h.ax1.Position, 'Color', 'none', 'YAxisLocation', 'Right');
% Preserve axes formatting
hold(h.ax1, 'on');
hold(h.ax2, 'on');
% Plot data
h.plot(1) = plot(h.ax1, t1, X);
h.scatter(1) = scatter(h.ax2, ts', Y1);
h.scatter(2) = scatter(h.ax2, ts', Y2);
h.scatter(3) = scatter(h.ax2, ts', Y3);
Gives you:
And now you have full control over all of the axes and line properties. Note that this assumes you have R2014b or newer in order to use the dot notation for accessing the Position property of h.ax1. If you are running an older version you can use get(h.ax1, 'Position') instead.
I have two vectors, c and d, whose histogram I need to plot side by side in the same figure in matlab. when i do
hist(c);
hold on;
hist(d)
the scale changes and I cant see the histogram of c vector. Where am i going wrong? Any help will be appreciated.
If you want the two to be in the same figure, you could try adjusting the X and Y limits to suit your needs (try help xlim and help ylim). However plotting them in the same figure might not always suit your needs, as a particular plot has to of course maintain a certain limit for X and Y.
If displaying them side by side in different figures would suffice however, you could consider using subplot():
>> A=[1 1 1 2 2];
>> B=[1 2 2 2 2];
>> figure(1);
>> hold on;
>> subplot(1,2,1);
>> hist(A);
>> subplot(1,2,2);
>> hist(B);
Resultant figure:
Notice how the different axis limits are maintained.
You can use axis([xmin xmax ymin ymax]) to control the x and y axis and select a range that will display both histograms. Depending on what you want your plot to look like, you may also want to try using nelements = hist(___) to get the number of elements in each bin and then plot them using bar(x,nelements) to control the location of each bar.
hist assumes you want to divide the range into 10 equal sized bins by default. If you want to use the same bins for both histograms, first find the range of your values and make a set of bin centers (e.g. binCenters = linspace(min(x), max(x), 15)'), then callhist(x, binCenters)`.
I use MATLAB histograms quite frequently and have wrote this small matlab script to plot two histograms (first one red and second blue) in one figure. The script is quite simple but the important thing is that the histograms should be comparable (i.e. equally spaced frequency bins).
function myhist(varargin)
% myhist function to plot the histograms of x1 and x2 in a single figure.
% This function uses the same xvalue range and same bins to plot the
% histograms, which makes comparison possible.
if nargin<2
x1 = cell2mat(varargin(1));
x2 = x1;
res = 100;
elseif nargin==2
x1 = cell2mat(varargin(1));
if length(cell2mat(varargin(2)))==1
res = cell2mat(varargin(2));
x2 = x1;
else
x2 = cell2mat(varargin(2));
res = 100;
end
elseif nargin>2
x1 = cell2mat(varargin(1));
x2 = cell2mat(varargin(2));
res = cell2mat(varargin(3));
end
if numel(x1)~=length(x1) || numel(x2)~=length(x2)
error('Inputs must be vectors.')
return
end
xrangel = max(min(x1),min(x2));
xrangeh = min(max(x1),max(x2));
x1_tmp = x1(x1>=xrangel & x1<=xrangeh);
x2_tmp = x2(x2>=xrangel & x2<=xrangeh);
xbins = xrangel:(xrangeh - xrangel)/res:xrangeh;
hist(x1_tmp,xbins)
hold on
h = findobj(gca,'Type','patch');
set(h,'FaceColor','r','EdgeColor','w');
hist(x2_tmp,xbins)