How to find all the points of a given graph line - matlab

Hi everyone I want to find all the points from a plot and save it in array
for example I've this graph I want all the point from the beginning of the line to the end of it
% Drawing a Trajectory
prompt={'Enter The Number Of Lines:'}; % Enter statements at the Command Window to accept input from you.
title='Draw Line '; % Name the Command Window
n=inputdlg(prompt); % Create and open input dialog box
A = sscanf(n{1}, '%d'); % Convert from String to Int
[x,y] = ginput(A); % Graphical input from mouse or cursor
plot(x,y)
posth = [x,y]; % Save 'x' and 'y' as Array

If you want to obtain x and y with only the plot after the fact, in this case you can do:
line_handles = get(gca,'Children');
x = get(line_handles,'XData');
y = get(line_handles,'YData');
where gca refers to the current axis of the current figure (you can replace it with the handle, h, of the plot, i.e., change your code to h = plot(x,y)). If there is only one line, x and y will be vectors. If there are multiple lines, they will be cell arrays.
You can also output x and y simultaneously as a cell array via:
xy = get(get(gca,'Children'),{'XData','YData'});
where
x = xy{1,:};
y = xy{2,:};

Related

Change color of specific points in matlab?

I have a scatterplot in Matlab and was wondering if there was any way to the change the color of just one of the points?
you can plot the graph, after that replot the point which you want.
% plot the curve or graph
hold on
plot(x,y,'.r')
Try to hold on and then plot the point (x,y) which you want with the specified color (r) which you are interested in.
If you do not want to overlay a second plot on the first, you can plot each point individually and use handles. In that way, you can later perform arbitrary changes on each individual point.
You can find an example below.
% Generate some numbers
x = randn(10,1);
y = randn(10,1);
% Plot each point individually
figure
hold on
for idx = 1 : numel(x)
hdl(idx) = plot(x(idx),y(idx),'marker','.','color','k')
end
% change color, markerstyle, x-position, etc...
hdl(2).Color = [1 0 0]
hdl(3).Marker = 'o'
hdl(5).XData = 1
x = rand(10,1) ;
y = rand(10,1) ;
scatter(x,y) ;
[x1,y1] = getpts ;
hold on
plot(x1,y1,'Or') ;
click on the point, you want to change color, when prompted.

In Matlab, how to draw lines from the curve to specific xaxis position?

I have a spectral data (1000 variables on xaxis, and peak intensities as y) and a list of peaks of interest at various specific x locations (a matrix called Peak) which I obtained from a function I made. Here, I would like to draw a line from the maximum value of each peaks to the xaxis - or, eventually, place a vertical arrow above each peaks but I read it is quite troublesome, so just a vertical line is welcome. However, using the following code, I get "Error using line Value must be a vector of numeric type". Any thoughts?
X = spectra;
[Peak,intensity]=PeakDetection(X);
nrow = length(Peak);
Peak2=Peak; % to put inside the real xaxis value
plot(xaxis,X);
hold on
for i = 1 : nbrow
Peak2(:,i) = round(xaxis(:,i)); % to get the real xaxis value and round it
xline = Peak2(:,i);
line('XData',xline,'YData',X,'Color','red','LineWidth',2);
end
hold off
Simple annotation:
Here is a simple way to annotate the peaks:
plot(x,y,x_peak,y_peak+0.1,'v','MarkerFaceColor','r');
where x and y is your data, and x_peak and y_peak is the coordinates of the peaks you want to annotate. The add of 0.1 is just for a better placing of the annotation and should be calibrated for your data.
For example (with some arbitrary data):
x = 1:1000;
y = sin(0.01*x).*cos(0.05*x);
[y_peak,x_peak] = PeakDetection(y); % this is just a sketch based on your code...
plot(x,y,x_peak,y_peak+0.1,'v','MarkerFaceColor','r');
the result:
Line annotation:
This is just a little bit more complicated because we need 4 values for each line. Again, assuming x_peak and y_peak as before:
plot(x,y);
hold on
ax = gca;
ymin = ax.YLim(1);
plot([x_peak;x_peak],[ymin*ones(1,numel(y_peak));y_peak],'r')
% you could write instead:
% line([x_peak;x_peak],[ymin*ones(1,numel(y_peak));y_peak],'Color','r')
% but I prefer the PLOT function.
hold off
and the result:
Arrow annotation:
If you really want those arrows, then you need to first convert the peak location to the normalized figure units. Here how to do that:
plot(x,y);
ylim([-1.5 1.5]) % only for a better look of the arrows
peaks = [x_peak.' y_peak.'];
ax = gca;
% This prat converts the axis unites to the figure normalized unites
% AX is a handle to the figure
% PEAKS is a n-by-2 matrix, where the first column is the x values and the
% second is the y values
pos = ax.Position;
% NORMPEAKS is a matrix in the same size of PEAKS, but with all the values
% converted to normalized units
normpx = pos(3)*((peaks(:,1)-ax.XLim(1))./range(ax.XLim))+ pos(1);
normpy = pos(4)*((peaks(:,2)-ax.YLim(1))./range(ax.YLim))+ pos(2);
normpeaks = [normpx normpy];
for k = 1:size(normpeaks,1)
annotation('arrow',[normpeaks(k,1) normpeaks(k,1)],...
[normpeaks(k,2)+0.1 normpeaks(k,2)],...
'Color','red','LineWidth',2)
end
and the result:

Add space between plots for the plotmatrix function

Is there a way to add some space between the plots for the plotmatrix function? (I would like to label every x and y axis)
You can do it by using a specific output argument in the call to plotmatrix. You can then retrieve the position of each individual axes and modify it (making it smaller).
Example:
clc
clear
rng default
X = randn(50,3);
Y = reshape(1:150,50,3);
%// Use this output argument
[~,AX,~,~,~] = plotmatrix(X,Y);
%// Fetch all the positions and alter them (make axes smaller)
AllPos = get(AX(:),'Position');
AllPos = vertcat(AllPos{:});
NewPos = [AllPos(:,1)+.05 AllPos(:,2)+.05 AllPos(:,3)-.1 AllPos(:,4)-.1]
%// Update the plot
for k = 1:numel(AX)
axes(AX(k))
set(AX(k),'Position',NewPos(k,:))
xlabel(sprintf('Axes %i',k))
end
Outputs the following:
In contrast to the original plot:

Save cropped figure in Matlab

I have opened a highly complex .fig figure from file in matlab with openfig.
After this I have cropped the figure with
axis([X1, X2, Y1, Y2])
I now want save the cropped figure so that it is of a reduced size when saved. The problem is that savefig will save the complete image zoomed to the axis specified. How can I save the figure, permanently cropped to the axis I have specified? e.g. it should not be possible to zoom out when opening the new image.
I don't have MATLAB in front of me, and I don't have your data to test this with, so this is an approximate answer, but as #natan mentions in the comments above, you can harvest the data from the figure, which is stored in 'XData' and 'YData'. You can then crop the data to just the parts you want and then replace the existing data with the cropped data. It will end up looking something like this:
kids = findobj(gca,'Type','line');
K = length(kids);
Xrange = get(gca,'XLim');
Yrange = get(gca,'YLim');
for k = 1:K
X = get(kids(k),'XData');
Y = get(kids(k),'YData');
idx = X >= min(Xrange) & X <= max(Xrange) & Y >= min(Yrange) & Y <= max(Yrange);
set(kids(k),'XDATA',X(idx),'YDATA',Y(idx));
end
You'll have to modify this if your plots have patch objects, bar objects, etc., but the idea is there.
Edge cases:
There are a few edge cases to consider as #Jan has rightly pointed out. The first is that the approach above assumes a rather high density of points even in the expanded scales and will leave a small gap between the end of the line and the axes. For low-point-density lines, you need to expand the idx variable to capture the next point outside the axes in either direction:
idx_center = X >= min(Xrange) & X <= max(Xrange) & Y >= min(Yrange) & Y <= max(Yrange);
idx_plus = [false idx(1:end-1)];
idx_minus = [idx(2:end) false];
idx = idx_center | idx_plus | idx_minus;
That will only expand the number of points if there is at least one point inside the window. The other edge case is where the line passes through the window but does not contain any points inside the window, which would be the case if idx is all false. In that case, you need the largest point outside the left axis and the smallest point outside the right axis. If either of these searches come up empty, then the line doesn't pass through the window and can be discarded.
if ~any(idx)
idx_low = find(X < min(Xrange),1,'last');
idx_high = find(X > max(Xrange),1,'first');
if isempty(idx_low) | isempty(idx_high)
% if there aren't points outside either side of the axes then the line doesn't pass through
idx = false(size(X));
else
idx = idx_low:idx_high;
end
end
This doesn't test whether the line is inside the y-bounds, so it may be possible that the line will pass above or below the window without intersecting. If that is important you will need to test the line connecting the found points. An extra 2 points in memory should not be a big deal. If it is an issue, then I leave it as an exercise to the student to work out the test of whether a line passes through the axes.
Putting it all together:
kids = findobj(gca,'Type','line'); % find children of the axes that are lines
K = length(kids); % count them
Xrange = get(gca,'XLim'); % get the axis limits
Yrange = get(gca,'YLim');
for k = 1:K
X = get(kids(k),'XData'); % pull the x-y data for the line
Y = get(kids(k),'YData');
% find the points inside the window and then dilate the window by one in
% each direction
idx_center = X >= min(Xrange) & X <= max(Xrange) & Y >= min(Yrange) & Y <= max(Yrange);
idx_plus = [false idx(1:end-1)];
idx_minus = [idx(2:end) false];
idx = idx_center | idx_plus | idx_minus;
% handle the edge case where there are no points in the window but the line still passes through
if ~any(idx)
idx_low = find(X < min(Xrange),1,'last');
idx_high = find(X > max(Xrange),1,'first');
if isempty(idx_low) | isempty(idx_high)
% if there aren't points outside either side of the axes then the line doesn't pass
% through
idx = false(size(X));
else
% numerical indexing instead of logical, but it all works the same
idx = idx_low:idx_high;
end
end
if any(idx)
% reset the line with just the desired data
set(kids(k),'XDATA',X(idx),'YDATA',Y(idx));
else
% if there still aren't points in the window, remove the object from the figure
delete(kids(k));
end
end

Legend for multiple lines in Matlab plot

I have 13 lines on a plot, each line corresponding to a set of data from a text file. I'd like to label each line starting with the first set of data as 1.2, then subsequently 1.25, 1.30, to 1.80, etc., with each increment be 0.05. If I were to type it out manually, it would be
legend('1.20','1.25','1.30', ...., '1.80')
However, in the future, I might have more than 20 lines on the graph. So typing out each one is unrealistic. I tried creating a loop in the legend and it doesn't work.
How can I do this in a practical way?
N_FILES=13 ;
N_FRAMES=2999 ;
a=1.20 ;b=0.05 ;
phi_matrix = zeros(N_FILES,N_FRAMES) ;
for i=1:N_FILES
eta=a + (i-1)*b ;
fname=sprintf('phi_per_timestep_eta=%3.2f.txt', eta) ;
phi_matrix(i,:)=load(fname);
end
figure(1);
x=linspace(1,N_FRAMES,N_FRAMES) ;
plot(x,phi_matrix) ;
Need help here:
legend(a+0*b,a+1*b,a+2*b, ...., a+N_FILES*b)
As an alternative to constructing the legend, you can also set the DisplayName property of a line so that the legend is automatically correct.
Thus, you could do the following:
N_FILES = 13;
N_FRAMES = 2999;
a = 1.20; b = 0.05;
% # create colormap (look for distinguishable_colors on the File Exchange)
% # as an alternative to jet
cmap = jet(N_FILES);
x = linspace(1,N_FRAMES,N_FRAMES);
figure(1)
hold on % # make sure new plots aren't overwriting old ones
for i = 1:N_FILES
eta = a + (i-1)*b ;
fname = sprintf('phi_per_timestep_eta=%3.2f.txt', eta);
y = load(fname);
%# plot the line, choosing the right color and setting the displayName
plot(x,y,'Color',cmap(i,:),'DisplayName',sprintf('%3.2f',eta));
end
% # turn on the legend. It automatically has the right names for the curves
legend
Use 'DisplayName' as a plot() property, and call your legend as
legend('-DynamicLegend');
My code looks like this:
x = 0:h:xmax; % get an array of x-values
y = someFunction; % function
plot(x,y, 'DisplayName', 'Function plot 1'); % plot with 'DisplayName' property
legend('-DynamicLegend',2); % '-DynamicLegend' legend
source: http://undocumentedmatlab.com/blog/legend-semi-documented-feature/
legend can also take a cell list of strings as an argument. Try this:
legend_fcn = #(n)sprintf('%0.2f',a+b*n);
legend(cellfun(legend_fcn, num2cell(0:N_FILES) , 'UniformOutput', false));
The simplest approach would probably be to create a column vector of the numbers to use as your labels, convert them to a formatted character array with N_FILES rows using the function NUM2STR, then pass this as a single argument to LEGEND:
legend(num2str(a+b.*(0:N_FILES-1).','%.2f'));
I found this I found through Google:
legend(string_matrix) adds a legend containing the rows of the matrix string_matrix as labels. This is the same as legend(string_matrix(1,:),string_matrix(2,:),...).
So basically, it looks like you can construct a matrix somehow to do this.
An example:
strmatrix = ['a';'b';'c';'d'];
x = linspace(0,10,11);
ya = x;
yb = x+1;
yc = x+2;
yd = x+3;
figure()
plot(x,ya,x,yb,x,yc,x,yd)
legend(strmatrix)