Related
Do you know a simple way to draw arbitrary splines or lines in a plot-figure? I've got the following figure which I created in powerpoint with more or less arbitrary spline-curve (blue) but I'd like to do the same in MATLAB now because of a better look in the final plot-output.
I'm now wondering if I've got to manually "find" data-values to draw some sort of spline (which looks roughly like the blue one below) myself or if there's maybe a tool where I can simply insert some points into a plot interactively and there's a curve fitted though it to create something similar!?
The green and red lines I can figure out myself (probably also have to plot them manually, do I)?!?
Thanks in advance!
EDIT
Okay I found a way myself doing it in MATLAB to gnerate a nice spline: Use splinetool, then either use an example or import some data and then you can interactively add and delete points until your spline looks roughly like it should. Then file->print to figure and then tools->edit plot! You can then delete everything you don't need, add title, xlabel and ylabel etc. and then export it to latex with e.g. matlab2tikz :-) Very nice stuff!!
According the purpose, print nice plots for the thesis, I have some out-of-Matlab recommendations.
1) plot everything as usual, you get a figure handle and an axes handle
h = figure( ... )
a = gca
2) use the data cursor function of the figure window and interactively insert the base points for your later splines. You can additional points by right-click.
t = linspace(0,2*pi,1000);
[x y] = deal(sin(t),cos(t))
Later you delete the "visual" part of the data tip inside Illustrator/Inkscape, if you just want to keep the anchor point of the vector graphic to snap your splines.
There is also the possibility of custom data tips: Tutorial at Matlab Central
3) I once wrote a function to nicely plot Matlab figures as vector graphic based PDFs. You can specify height, width and how much white margin around you want. You just need to pass figure and axes handle and the name:
function saveFigure( fig_handle, axes_handle, name , height , width , margin)
set(axes_handle,'LooseInset',get(gca,'TightInset'));
set(fig_handle, 'Units','centimeters','PaperUnits','centimeters')
% the last two parameters of 'Position' define the figure size
set(fig_handle,'Position',[-margin -margin width height],...
'PaperPosition',[0 0 width+margin height+margin],...
'PaperSize',[width+margin height+margin],...
'PaperPositionMode','auto',...
'InvertHardcopy', 'on',...
'Renderer','painters'... %recommended if there are no alphamaps
);
saveas(fig_handle,name,'pdf')
end
4) You get a PDF you could directly use for Latex - for use in MS Office use 'emf' (
Enhanced metafile) rather than 'pdf'. Open this file with Adobe Illustrator (preferable as it offers layers) or Inkskape (Open Source).
5) The datatips are recognized as graphical objects, you can catch them and draw a spline on them. For Illustrator I'd recommend to put the spline in another layer than the actual figure. Later you can just swap the figure and give the spline new anchor points. In Inkscape you could use the grouping function to keep everything together.
6) I'd say you save a lot of time over a only-Matlab-solution. Good look!
I used the regress function to find the slope for some data I plotted. I have managed to plot the data and the fitted line both on the same plot. I know how to make it clear that the fitted line is the slope, but I would also like to add a box in corner of the graph (dont care where) that shows the actual value of the slope (basically shows the value that the regress function returns), and I'm trying to find a way to do this automatically (like if there's a function for that or something). Can anybody help (I hope I explained my question well enough...)?
I didn't try to recreate your slope line but have you considered using an annotation?
Example:
x = [-1:.2:1];
plot(x,x.^2,'-bo');
annotation('textbox', [.4 .4 .1 .1], 'String', ...
['slope at x = 0.6 is: ',num2str(2*.6)]);
Which shows:
Of course you can control how the box is positioned, formatted, and so forth.
Check the help files for more detailed info. In some cases you might also consider using a legend().
The function text adds text to a figure. It requires a position and a string to display. In addition, you can highly customize the appearance of the text. For example:
x = 1:100;
y = randn(size(x)) + 0.3*x;
plot(x,y,'.');
p = polyfit(x,y,1);
hold on;
plot(x, polyval(p,x),'k-');
h = text(min(xlim(gca)), max(ylim(gca)), ...
sprintf('%fx + %f', p(1), p(2)),...
'verticalalignment','top',...
'horizontalalignment','left');
Then, to see the various settinsg you can change, look at:
get(h)
Those properties can almost all be changes at creation (like verticalalignment above) or after creation (e.g. set(h, verticalalignment, 'top')).
I have been trying to draw arrows in Matlab in my figure but they all look terrible.
Also, I want my arrowhead to be a solid triangle, not a V.
I tried using the packages available on the Matlab File Exchange: arrow, arrows, arrows3, and probably at least one other one.
I even tried manually creating an arrow in the Matlab figure editor, but when I adjust the line width, the arrow looks like this:
I used the annotation command to create the arrow above:
annotation(gcf,'arrow',[0.621875 0.457916666666667],...
[0.205421152030217 0.40755429650614],...
'HeadLength',4,'LineWidth',5);
Here's the result trying to use the arrow package available here: Arrow.m (notice how the bottom arrow head is not perpendicular to the line:
I even tried the following and here is the result below (notice the terrible looking arrowhead):
figure
plot(1:10, 1:10)
annotation(gcf,'arrow',[0.621875 0.457916666666667],...
[0.205421152030217 0.40755429650614],...
'HeadLength',4,'LineWidth',5);
Vector graphics is hard. Though Matlab's typography is just as bad, but here's a simplistic text-based solution (I refuse to do this sort of annotation in Matlab any more):
figure
plot(1:10, 1:10)
text(5,4,'\rightarrow','FontSize',54,'Rotation',135,...
'HorizontalAlignment','center');
which yields a figure like this
Note that I have used '\leftarrow' because it points in the direction of zero degrees, which makes doing math in my head easier. This is no canned solution, you'll still need to fiddle with position to overcome the fact that Matlab is aligning this as text (see the 'Extent' and 'Margin' properties). Not surprisingly, you may see small glitches. The LaTeX interpreter can be used to obtain a different style arrow head:
text(5,4,'$\rightarrow$','FontSize',54,'Rotation',135,...
'HorizontalAlignment','center','Interpreter','latex');
I don't get the small glitches with this option, but the arrows look different (there are likely other LaTeX arrow styles that could be substituted). Changing the font may also have an effect and there are certainly other text-based arrows that could be used. More details on adding arrows can be found in this article from The MathWorks.
Another solution is to use the open-source Waterloo graphics - a library that addresses this by providing a pure Java library of 2D graphics functions that can easily be integrated in Matlab. See some examples here...
For example, try this code (after properly installing waterloo)
f = GXFigure();
x = -5:0.1:5;
gr1 = gxgca();
a1 = line(gr1, x, cos(x), 'LineSpec','-ob');
b1 = line(gr1, x, sin(x), 'LineSpec','-sg');
annotation(gr1,'arrow',[0.1 0.1],[0.4 0.4],'HeadLength',0.2,'HeadWidth', 0.5, 'LineWidth',2);
gr1.getObject().getView().autoScale();
Having worked with Matlab for more than 10 years and seeing almost zero progress in the quality of the plots (anti-aliased output to bitmap, decent looking eps-files, ...), I decided that my long time solution will be here. Some examples of decent looking arrows here, more beautiful graphs here. Unfortunately, some of the toolboxes prevent me from going completely to numpy/scipy/matplotlib. I know this is more of a rant than an answer, but that is my solution ...
Using quiver or quiver3 for 3-D plots will certainly look better than the that last arrow you made using annotation. I'm not sure it will look much better than the ones you made using the Arrow.m package though. It's possible to change the stlyes on quiver as well if you want.
I resorted to installing InkScape and drawing arrows in there. Nothing beat the simplicity and quality.
InkScape Website
When line-based arrows fail, you can always try patch-based ones:
xArrow = [0 1 1 1.2 1 1 0]; % X-coords of arrow edge points
yArrow = [-0.1 -0.1 -0.2 0 0.2 0.1 0.1]; % Y-coords of arrow edge points
hArrow = fill(xArrow,yArrow, [1 0 0]); % Plot a red arrow
axis equal % Set axis tick marks to be equal
Although more work, you can potentially parameterize the above into a function that takes a number of arguments for adjusting the scaling, aspect ratio, rotation, and position of the arrow. You can even adjust the color, edge line color, and alpha transparency.
This sort of idea appears to have been implemented by François Beauducel in his File Exchange submission ARROWS: generalized 2-D arrows plot.
There is now the DaVinci Draw toolbox (full disclosure: I wrote/sell the toolbox), which uses low-level Matlab commands like plot() and patch() to draw mid-level shapes like arrows. Example syntax:
davinci( 'arrow', 'X', [0 10], ...
'Y', [0 2], ...
'Shaft.Width', 1, ...
'Head.Length', 2.5, ...
'Color', 'w', ...
'EdgeColor', 'k', ...
'LineWidth', 2.5 )
From the documentation:
In the past, there has been asked a question about customizing the linewidth in gplot (see In MatLab, how to adjust the line width drawn by the function 'gplot'?). I am dealing with a slightly more complicated version, which prevents me from using the solution given there. Therefore, I would like to ask how to do the following: I would like to adapt the line width of some of the calls of gplot, and not of others. I am namely calling gplot several times and using hold on to plot them in one figure. I am trying to draw a graph, with multiple types of edges (A and A2). And k paths in it. I am currently using the following code:
figure
hold on
gplot(A,coor,'k*:')
gplot(A2,coor,'k-')
plot(coor(T,1),coor(T,2),'k.','MarkerSize',20)
plot(coor(T,1),coor(T,2),'bo','MarkerSize',20)
% a line where I define my own colors (not shown, since not relevant)
set(gca,'ColorOrder',colors)
hold all
for i=1:k
gplot(Path,coor)
end
hold off
But I would like to draw the paths with a larger line width, while keeping the A and A2 at the standard line width 1.
Can someone help me? Thank you very much!
You can get the children of the axis before and after adding the extra lines, and only set the new ones to have a larger linewidth:
figure
hold on
gplot(A,coor,'k*:')
gplot(A2,coor,'k-')
plot(coor(T,1),coor(T,2),'k.','MarkerSize',20)
plot(coor(T,1),coor(T,2),'bo','MarkerSize',20)
ChildrenBefore=get(gca,'children');
% a line where I define my own colors (not shown, since not relevant)
set(gca,'ColorOrder',colors)
hold all
for i=1:k
gplot(Path,coor)
end
hold off
ChildrenAfter=get(gca,'children');
NewChildren=setdiff(ChildrenAfter,ChildrenBefore);
set(intersect(findall(gcf,'type','line'),NewChildren),'LineWidth',5)
I have had some success in the past with seeking help here at SO and have garnered many good hints and tips from you guys. I have run into three problems that I would really like help with.
Firstly here is a picture of the scatter plot I'm using as input:
Scatter plot
Here is the code I've used to generate that exact plot (minus the manual arrows/lines):
Source Code
I would like to know if someone could help me with three questions I have:
How do I programatically draw those lines I have entered manually given the points above? I've tried a LS approach and it produces ok(ish) results and I am uncertain of get 'tighter' straight lines.
There are sometimes outliers in my data that don't exactly conform with the other data in row/column fashion. Is there a way to remove such outliers (as seen in Point A on the Plot)?
Finally is there a way to calculate the 'centres' of each of the 'rectangles' given that the points aren't ordered in a specific order? Could one order these points in a specific order to help? Or is there a smarter way?
Thanks for taking the time to look at my question. I really hope somebody can help me with this - I've been wracking my brain trying to figure out how to do these things and I cannot come up with a solution.
EDIT: Removed the link to my LS approach as it is incorrect in the horizontal direction.
Alright, so I had a little downtime to try to lend a hand here. I think I can at least get you started on programatically drawing the lines and hopefully it will give you some insight into tackling the other two questions you have here.
I pretty much copied the code you had and started from there. What I paste here is what I've added/tweaked after you declared you cx and cy matricies.
Now to explain my thought process and logic. Cy is designed to be the "zipped" matrix of cx and cy but sorted along the Y coordinate value. Cx is the same except sorted along the X coordinate instead. The logic and such should be pretty straight forward there I hope. If not just say so and I'll explain!
Now the diffY and diffX. I took the difference of each coordinate (Xs and Ys seperately) since we know there is a pattern here that causes the XY pairs to form a grid-like shape. I'm essentially banking on the fact that there is a sizeable difference the the Y (or X) value for each line of points (horizontal and vertical). Once I have this difference between adjcent points I look for a difference greater than 2 (this is post-hoc after seeing what the differences were). The -1 is used to give me the actual end of the row instead of the start of the next row since we're going to be starting from item (1,:) in our list (makes more sense if you look at the lines in the code).
(This is of course a HUGE assumption and makes this specific for this particular data set. It can likely be generalized for most grid-like shapes though, but that has to be tested.)
Also a note. I did notice that later on in the list of points I gleaned using the difference my thought process started to break down. You can continue to plot more lines following the pattern I have in the code to see what I mean.
As far as your other two questions. You can likely take the idea that I have here and improve/expand upon it. I'm thinking that you should be able to snag the outlier by simply looking at all the points in a given row (idx[1] -> idx[2]) and making sure they fit along the line. Essentially requiring that they satisfy the general formula for the equation of a line that you can get from using the points that are used to draw the line. You will of course have to have a little error tolerance for that.
Then for the rectangle you can use the ordered group of points to create your rectangles and probably utilize the regionprops function to calculate the centroid (center in a 2D world) of a rectangle specified by the data points you can glean.
Best of luck!
Cy = zeros(length(cy),2);
[Cy(:,2), IX] = sort(cy);
Cy(:,1) = cx(IX)';
diffY = [0; diff(Cy(:,2))]
idx = find(diffY>2)-1
Cy(idx,:) % Last mark in given row, appears to be slight issue further along.
Cx = zeros(length(cx),2);
[Cx(:,1), IX] = sort(cx);
Cx(:,2) = cy(IX)';
diffX = [0; diff(Cx(:,1))]
idx2 = find(diffX>2)-1
Cx(idx2,:) % Last mark in given row, appears to be slightly off later on. (top of grid though)
%%
figure('Position',[0,0,c,r]);
scatter(Cy(:,1),Cy(:,2),'.');
axis([0 c 0 r])
set(gca,'YDir','reverse')
% Horizontal lines
line([Cy(1,1) Cy(idx(1),1)], [Cy(1,2) Cy(idx(1),2)], 'Color', 'red', 'LineWidth', 2)
line([Cy(idx(1)+1,1) Cy(idx(2),1)], [Cy(idx(1)+1,2) Cy(idx(2),2)], 'Color', 'red', 'LineWidth', 2)
line([Cy(idx(2)+1,1) Cy(idx(3),1)], [Cy(idx(2)+1,2) Cy(idx(3),2)], 'Color', 'red', 'LineWidth', 2)
% Vertical lines
line([Cx(1,1) Cx(idx2(1),1)], [Cx(1,2) Cx(idx2(1),2)], 'Color', 'red', 'LineWidth', 2)
line([Cx(idx2(1)+1,1) Cx(idx2(2),1)], [Cx(idx2(1)+1,2) Cx(idx2(2),2)], 'Color', 'red', 'LineWidth', 2)