Matlab plotting specific points on x-axis - matlab

I want to plot a function with markers where f(x)=0.
I have a function findmanyzeros that returns an array of x-values where f(x)=0 within a range.
For example:
findmanyzeros(#(x)sin(x)-exp(-x), 0, 10, 50, 1e-10);
ans = [0.5885 3.0964 6.2850 9.4247]
I am trying to plot the graph of f(x) and the zeros of f(x) that lie on the x-axis, but when I run the code below the resulting figure does not display the zeros.
How can I properly display the zeros of f(x) along the x-axis?
Also, how can I do it such that I'm not hardcoding the amount of values ([ 0 0 0 0])?
% Find zeros of f(x)
f = #(x)sin(x)-exp(-x);
zeros = findmanyzeros(f, 0, 10, 50, 1e-10);
% Plot f(x)
fplot(f, [0 10], 'linewidth', 2, 'color', 'b');
hold on
% Plot the x-axis
yline(0, 'linewidth', 2, 'color', 'r');
hold on
% Plot the zeros of f(x)
plot(zeros, [0 0 0 0], 'color', 'g', 'MarkerSize',20);
hold off
grid on
ylim([-1.5 1])
EDIT: I want my figure to look like this:

First of all, it is strongly recommended avoiding naming variables with names of existing functions, so calling your values vector "zeros" isn't a good idea, and you'll be better off with calling it "my_func_zeros" or something similar. zeros is a built-in matlab function used to create zeros-filled vectors/matrices.
As for your questions:
I believe you've just forgot to specify what type of marker you want, so by default it's an infinitely small point which won't appear. Further more, when using one of MATLAB's basic colors\markers, there is no need to specify the option name (will be more clear in the code line I'll attach).
You can use the function zeros and length to create a vector with a dependent size.
Therefor, your code should look somewhat like this:
my_func_zeros = findmanyzeros(f, 0, 10, 50, 1e-10);
...
plot(my_func_zeros, zeros(1,length(my_func_zeros)), '*g', 'MarkerSize',20);
where:
zeros(1,length(my_func_zeros))
creates a vector containing zeros with the size of your my_func_zeros vector, and:
'*g'
specifies the marker type (*) and the color (g).
By the way, there is no need to add hold on after every plot - after you wrote it once anything you plot will be plotted on the same figure until the next hold off or figure command.
Good luck!

Related

How to use progressive coloring with plot3?

I want to use plot3 with varying colors as the array index progresses.
I have 3 variables: x, y, z.
All of these variables contain values in the timeline progress.
For example:
x = [1, 2, 3, 4, 5];
y = [1, 2, 3, 4, 5];
z = [1, 2, 3, 4, 5];
plot3(x, y, z, 'o')
I'd like to see a change of color from (x(1), y(1), z(1)) to the last value in the plot (x(5), y(5), z(5)). How can I change this color dynamically?
The way to go here, in my opinion, is to use scatter3, where you can specify the colour value explicitly:
scatter3(x,y,z,3,1:numel(x))
where 3 would be the size argument and 1:numel(x) gives increasing colours. Afterward you can choose your colour map as usual.
Using plot3 you could do the same, but it requires a loop:
cMap = jet(numel(x)); % Generates colours on the desired map
figure
hold on % Force figure to stay open, rather than overwriting
for ii = 1:numel(x)
% Plot each point separately
plot3(x(ii), y(ii), z(ii), 'o', 'color',cMap(ii,:))
end
I'd only use the plot3 option in case you want successively coloured lines between elements, which scatter3 can't do:
cMap = jet(numel(x)); % Generates colours on the desired map
figure
hold on % Force figure to stay open, rather than overwriting
for ii = 1:numel(x)-1
% Plot each line element separately
plot3(x(ii:ii+1), y(ii:ii+1), z(ii:ii+1), 'o-', 'color',cMap(ii,:))
end
% Redraw the last point to give it a separate colour as well
plot3(x(ii+1), y(ii+1), z(ii+1), 'o', 'color',cMap(ii+1,:))
NB: tested and exported images on R2007b, cross-checked the syntax with R2021b docs

How to ignore values that are negative when plotting a line plot with matlab?

I have a matrix where some values have -1 as an indication that there was an error. Normally I would just use ylim([0 100]) to not show these values in my graph, but when using line graph the connection will still drop down to the point. I want a chart that consists of lines, not a scatter plot. Is there a simple way of ignoring negative values when plotting a line and only connect positive values when using the plot function in MATLAB?
I have written a small example program which behaves similarly, but the way I use seems a little "too complicated" and I want to know if there is an easier way to achieve this. It works fine as I put the values to NaN, and now the x and y values are the same amount. However deleting or sorting out the values from the vector will lead to different amount of x and y values.
I was hoping for a modification or a flag or something.
x = 2*rand(10) - rand(10)
xx = 10:10:100;
figure;
for i=1:length(x)
for j=1:length(x(i,:))
if x(i,j) < 0
x(i,j) = NaN;
end
end
end
plot(xx,x)
Please note that this is only an example, the whole code would be too large to post here.
When having non-corresponding x-values (so that the plot function simply uses 1,2,3... and so on for the corresponding y values) this can be achieved by using
plot(x(x>0))
In this case, the corresponding values are different, in the real code they are measured data, here I simply use a 10th step for simplification.
x = 2*rand(10) - rand(10)
xx = 10:10:100;
plot(xx,x(x>0))
The above code will error with the message "Vectors must be the same length".
This is just like using NaN instead of negative values, only that the original vector is not modified at all. You might notice that this solution is vectorized.
y = 2*randn(10,1) - randn(10,1);
figure(); plot(1:numel(y), y./(y<=0) );
a = [50, -1, 10, 5, 8, 22, -1];
b = a > 0;
c = a(b);
Output:
c
[50, 10, 5, 8, 22]
Now you can plot c
[~, s] = size(c);
xx = 1:1:s;
You could also do it directly without saving the calculation and modifying it. just plot it.
plot(x(x>0))

plotting from cell array of different lengths

this might come as an eco in this forum but I couldn't find a solution that I could apply to my problem. I have a cell of res_A1 of size (1x500) and in each cell there is a vector (1xlength) where length varies. I would like to plot in the same graph every vector or maybe a handful of them. All lines can be in the same color. I have tried following but the graph make no sense
data=res_A1(1,:,end);
plot(cell2mat(data)');
Also I would like to plot the average of the 500 vectors, preferably this should be in the same graph in another color. Is there a nice way of doing this?
You can use cat to combine the vectors along the first dimension. Then you can pass the transpose of this matrix to plot and each column will be plotted as it's own plot.
plot(cat(1, data{:}).');
If we create some example data, this will yield.
data = arrayfun(#(x)rand(1,10), ones(1, 5), 'uni', 0);
plot(cat(1, data{:}).');
If you want specific ones (i.e. [1 3 5]), you can replace : above with the indices of the ones you want.
plot(cat(1, data{[1 3 5]}).');
If you want to plot the average, simply use mean on the result of the call to cat.
avg = mean(cat(1, data{:}), 1);
plot(avg);
And if you wanted it in the same plot:
alldata = cat(1, data{:});
avg = mean(alldata, 1);
% Plot all of the curves
plot(alldata.');
hold on
% Plot the average curve
plot(avg, 'LineWidth', 3, 'Color', [0.5 0.5 0.5], 'LineStyle', '--')
Update
If your data is all different lengths, You have two options, you could plot everything with a loop.
hax = axes;
hold(hax, 'on');
for k = 1:numel(data)
plot(data{k}, 'Parent', hax);
end
Or you could still try to combine everything into one matrix, padding with NaN values.
% Find the longest vector length
maxlength = max(cellfun(#(x)numel(x), data));
alldata = nan(maxlength, numel(data));
for k = 1:numel(data)
alldata(1:numel(data{k}),k) = data{k};
end
Then you can plot this and take the mean using nanmean.
plot(alldata);
avg = nanmean(alldata, 2);

How to insert two X axis in a Matlab a plot

I would like create a Matlab figure with a double X axis (m/s and km/h) with the same plot.
I have found plotyy and - in Matlab reposity - plotyyy, but I am looking for:
A double X axis.
Together below the plot.
My code is very simple:
stem(M(:, 1) .* 3.6, M(:, 3));
grid on
xlabel('Speed (km/h)');
ylabel('Samples');
M(:, 1) is the speed (in m/s), and M(:, 3) is the data.
I would like only a second line, in the bottom, with the speeds in m/s.
You can do something like the following. In comparison to the solution of #Benoit_11 I do use the normal Matlab labels and refer to both axes with handles so the assignments are explicit.
The following code creates an empty x-axis b with the units m/s with a negligible height. After this, the actual plot is drawn in a second axes a located a bit above the other axes and with units km/h. To plot on a specific axes, insert the axes-handle as the first argument of stem. The conversion from m/s to km/h is directly written in the call to stem. Finally, it's needed to set the xlim-property of the both axes to the same values.
% experimental data
M(:,1) = [ 0, 1, 2, 3, 4, 5];
M(:,3) = [12, 10, 15, 12, 11, 13];
% get bounds
xmaxa = max(M(:,1))*3.6; % km/h
xmaxb = max(M(:,1)); % m/s
figure;
% axis for m/s
b=axes('Position',[.1 .1 .8 1e-12]);
set(b,'Units','normalized');
set(b,'Color','none');
% axis for km/h with stem-plot
a=axes('Position',[.1 .2 .8 .7]);
set(a,'Units','normalized');
stem(a,M(:,1).*3.6, M(:,3));
% set limits and labels
set(a,'xlim',[0 xmaxa]);
set(b,'xlim',[0 xmaxb]);
xlabel(a,'Speed (km/h)')
xlabel(b,'Speed (m/s)')
ylabel(a,'Samples');
title(a,'Double x-axis plot');
As a very simple alternative you could also create a 2nd axis (transparent) and put it below the first one so that you only see the x axis.
Example:
clear
clc
close all
x = 1:10;
x2 = x/3.6;
y = rand(size(x));
hP1 = plot(x,y);
a1Pos = get(gca,'Position');
%// Place axis 2 below the 1st.
ax2 = axes('Position',[a1Pos(1) a1Pos(2)-.05 a1Pos(3) a1Pos(4)],'Color','none','YTick',[],'YTickLabel',[]);
%// Adjust limits
xlim([min(x2(:)) max(x2(:))])
text(2.85,0 ,'m/s','FontSize',14,'Color','r')
text(2.85,.05 ,'km/h','FontSize',14,'Color','r')
Output:
Then you can manually add the x labels for each unit, in different color for example.
The best way i can think to do it is to use 2 plots, for example, you can split the plot into a large and small section by doing something like this:
subplot(100, 1, 1:99) // plot your graph as you normally would
plot(...
subplot(100, 1, 100) // Plot a really small plot to get the axis
plot(...)
b = axis()
axis([b(1:2), 0, 0]) // set the y axis to really small
This is untested, you might need to fiddle around a little but it should hopefully put you on the right track.

MATLAB fill area between lines

I'm trying to do something similar to what's outlined in this post:
MATLAB, Filling in the area between two sets of data, lines in one figure
but running into a roadblock. I'm trying to shade the area of a graph that represents the mean +/- standard deviation. The variable definitions are a bit complicated but it boils down to this code, and when plotted without shading, I get the screenshot below:
x = linspace(0, 100, 101)';
mean = torqueRnormMean(:,1);
meanPlusSTD = torqueRnormMean(:,1) + torqueRnormStd(:,1);
meanMinusSTD = torqueRnormMean(:,1) - torqueRnormStd(:,1);
plot(x, mean, 'k', 'LineWidth', 2)
plot(x, meanPlusSTD, 'k--')
plot(x, meanMinusSTD, 'k--')
But when I try to implement shading just on the lower half of the graph (between mean and meanMinusSTD) by adding the code below, I get a plot that looks like this:
fill( [x fliplr(x)], [mean fliplr(meanMinusSTD)], 'y', 'LineStyle','--');
It's obviously not shading the correct area of the graph, and new near-horizontal lines are being created close to 0 that are messing with the shading.
Any thoughts? I'm stumped.
You may be getting a problem with using mean as a variable, since it's also a reserved MATLAB command. Try clearing the variable space and then using a unique variable name.
As for the second problem, you want
fill( [x fliplr(x)], [meanUniqueName fliplr(meanMinusSTD)], 'y', 'LineStyle','--');
You also don't need to do this in two steps, but can do it all at once. A code snippet from a script I'm currently working on does the exact same thing and contains the lines:
avar = allan(DATA, tau);
xFill = [avar.tau1 fliplr(avar.tau1)];
yFill = [avar.sig2+avar.sig2err fliplr(avar.sig2-avar.sig2err)];
figure(2);
fill(xFill,yFill,'y','LineStyle','--')
line(avar.tau1,avar.sig2);
So I fill the area between the two error lines, and then draw the data line on top.
It turned out to be a column vs row vector issue. For some reason using the fill method above with flipud with the original column vectors doesn't work, but transposing the original variables then using fliplr does. Go figure. Here's the code in case it helps someone else:
x = linspace(0,100, 101);
mean = torqueRnormMean(:,DOF)';
meanPlusSTD = torqueRnormMean(:,DOF)' + torqueRnormStd(:,DOF)';
meanMinusSTD = torqueRnormMean(:,DOF)' - torqueRnormStd(:,DOF)';
fill( [x fliplr(x)], [meanPlusSTD fliplr(meanMinusSTD)], 'k');
alpha(.25);
plot(x, mean, 'k', 'LineWidth', 2)
plot(x, meanPlusSTD, 'k')
plot(x, meanMinusSTD, 'k')
Note that I removed the dotted line and just used thin vs. thick lines to denote standard deviation and mean. I did this because the line style was inconsistent. This code is in a loop where DOF runs from 1:9, and in some of the subplots both std curves would be dashed and in some just the top or bottom. It didn't matter enough to me to have them dashed so I kept it simple. Now this is an example of the graph I get:
One thing that you appear to be doing wrong is that you're applying fliplr to column vectors. That will have no effect. The example you cited uses row vectors. You also concatenate them into a matrix instead of into a single vector like the example. I think that the equivalent with column vectors would be:
fill( [x;flipud(x)], [mean;flipud(meanMinusSTD)], 'y');
Another possibility:
x = 1:1000; % example x values
y_upper = 5+sin(2*pi/200*x); % example upper curve
y_lower = 2+sin(2*pi/230*x); % example lower curve
bar(x, y_upper, 1, 'b', 'edgecolor', 'b');
hold on
bar(x, y_lower, 1, 'w', 'edgecolor', 'w');
axis([0 1000 0 7])
It uses bar (with unit width and same-color edges) to fill the upper curve, and then a second bar to "remove" (plot in white) the lower part.