In generating plots with Matlab, when both the x and y minimum are zero, I prefer to have only one zero marking the origin rather than denoting it on both axes.
Matlab defaults to the latter, like so
Whereas I want something more like this
This can be done manually, but I'm trying to automate the process. Removing the x and y tick labels for 0 are obviously easy enough. However, I can't seem to access any handle for the position of the axis tick labels to properly position a text box for the 'new origin'. It seems the tick label offsets from the x and y axes may be somewhat constant in a given figure (in physical units, not normalized units), but I don't know if this is a standard across all figures or axes.
Any ideas?
I'm running Matlab 2014b.
Here's a simple way to do that:
% somthing to plot:
x = 0:0.1:2*pi;
y = abs(sin(x)+cos(x));
plot(x,y)
% get the handle for the current axes:
ax = gca;
% get the position of the zero tick on y-axis:
zeroYtick = ax.YAxis.TickValues==0;
% remove it (tick and lable)
ax.YAxis.TickValues(zeroYtick) = [];
The result:
If you want the 0 to be offset a little to the left, so it will be at the corner of the plot, you can use annotation:
% get the position of the zero tick on x-axis:
zeroXtick = ax.XAxis.TickValues==0;
% remove it (tick and lable)
ax.XAxis.TickValues(zeroXtick) = [];
% place a new zero at the origin:
dim = [0.1 0.01 0.1 0.1];
annotation('textbox',dim,'String','0',...
'FitBoxToText','on','LineStyle','none')
And you will get:
The advantage in annotation is that the origin always placed relative to the corner of the axes, and you don't need to know the values of the axes ticks to properly offset it.
EDIT:
For pre 2016a version you can use the following (I wrote it a little more compact):
ax = gca;
% remove the zero tick on y-axis (tick and lable):
ax.YTick(ax.YTick==0) = [];
% remove the zero tick on x-axis (tick and lable):
ax.XTick(ax.XTick==0) = [];
% place a new zero at the origin:
dim = [0.1 0.01 0.1 0.1];
annotation('textbox',dim,'String','0',...
'FitBoxToText','on','LineStyle','none')
EDIT 2:
Another option to keep the 0 in place is to stick it to the axes. this is done by replacing the Parent with the axes handle. First, we need a handle to the annotation (continuing from the last edit):
t = annotation('textbox',dim,'String','0',...
'FitBoxToText','on','LineStyle','none');
then we switch the Parent and set the new position:
t.Parent = ax;
t.Position(1:2) = -[0.2 0.1];
and finally, we super-glue it by converting the Units to pixels:
t.Units = 'Pixels';
The following will exclude the first tick on yaxis without shifting the graph at all. You can ensure that the correct item is being removed by setting the axis limits, ylim([0,upper_limit]) prior to removing the first one, or manually setting your yTicks, set(h,'yTick',my_ticks). Note that yTick changes the position of the ticks, while yTickLabel will change the text appearing at each position of yTick
h = plot([0,1],[0,1]);
yTicks = get(h,'yTick');
set(h,'yTick',yTick(2:end));
Related
Based code from here, I wrote a function which plots a figure and puts x-axis labels on both the top and bottom of the figure, as well as y-axis labels on the left and right side. My problem is that I need to run the code multiple times and each time the labels get written over, and for some reason the y-axis labels get overwritten in a weird way as shown,
first run:
second run:
The following is an mwe:
% sample data, plot
x=[1:168];
y=x;
plot(x, y, 'r', 'LineWidth', 1);
set(gca, 'XTick', [], 'YTick', []);
% set left yaxis label
ylabel(directions{1,1},'Rotation',-360);
% Adjust position - this seems to be what's causing the issue!
ylabelh = get(gca,'YLabel');
rpos = get(ylabelh,'Position');
set(ylabelh,'Position',rpos + [1.5*rpos(1) 0 0])
% do stuff... as per link above
axesPosition = get(gca,'Position');
hNewAxes = axes('Position',axesPosition,... %# Place a new axes on top...
'Color','none',... %# ... with no background color
'YAxisLocation','right',... %# ... located on the right
'XTick',[],'YTick',[],... %# ... with no x tick marks
'Box','off');
ylabel(hNewAxes,directions{2,1},'Rotation',-360); % yaxis label right
% Adjust position
ylabelh = get(gca,'YLabel');
Lpos = get(ylabelh,'Position');
set(gca,'YTick',[]);
set(ylabelh,'Position',Lpos+ [+Lpos(1)*0.02 0.05 0])
% -- And repeat for x axis -- %
% x axis labels
xlabel(directions{3,1},'Rotation',-360); % xaxis label bottom
% Adjust position
xlabelh = get(gca,'XLabel');
xlabpos = get(xlabelh,'Position');
set(gca,'XTick',[]);
rpos = get(xlabelh,'Position');
% do stuff ... as above
axesPosition = get(gca,'Position');
hNewAxes = axes('Position',axesPosition,... %# Place a new axes on top...
'Color','none',... %# ... with no background color
'XAxisLocation','top',... %# ... located on the right
'XTick',[],'YTick',[], ... %# ... with no x tick marks
'Box','off');
% xaxis label top
xlabel(hNewAxes,directions{4,1},'Rotation',-360);
Weirdly, if I run the code as a script it's fine (well, the labels get written over but I don't see the issue with the y axis labels) but if I run as a function multiple times in debug mode (which is currently the primary way I'm using it) then I see the above artefacts.
If you run your code then check at the end you will see that you have created 3 axes in your script. Run it once and they type get(gcf,'Children'). If you run it again you are adding another 2 axes, this continues everytime you run it.
Why does this happen?
Well your code creates 2 new axes handles (which you call the same name -> which you shouldn't do) - but only after you have already used gca -> i.e. the current axes (if none exists one is created).
I would advise you to completly reconfigure you code. Start by creating 2 axes before you do any plotting/labeling etc.. store the 2 axes variables independently which will allow you to refer to them as and when you need to.
Also it good practice to never rely on gca or gcf to get the currently axes or figure -> as it will eventually come back to bite you. Store the axes/figure handles and refer to them explicitly.
I find that data points that lie on or near the axes are difficult to see. The obvious fix, of course, is to simply change the plot area using axis([xmin xmax ymin ymax]), but this is not preferable in all cases; for example, if the x axis is time, then moving the minimum x value to -1 to show activity at 0 does not make sense.
Instead, I was hoping to simply move the x and y axes away from the plot area, like I have done here:
left: MATLAB generated, right: desired (image editing software)
Is there a way to automatically do this in MATLAB? I thought there might be a way to do it by using the outerposition axes property (i.e., set it to [0 0 0.9 0.9] and drawing new axes where they originally were?), but I didn't get anywhere with that strategy.
The answers here already show you most of the way - here is the last step to separate the x and y axle as per the example you put together.
f = figure ( 'color', 'white' );
% create the axes and set some properties
ax = axes ( 'parent', f, 'box', 'off', 'nextplot', 'add', 'XMinorTick', 'on', 'YMinorTick', 'on' );
% plot some data
plot ( ax, 0:10, [0:10].^2, 'rx-' )
% modify the x and y limits to below the data (by a small amount)
ax.XLim(1) = ax.XLim(1)-(ax.XTick(2)-ax.XTick(1))/4;
ax.YLim(1) = ax.YLim(1)-(ax.YTick(2)-ax.YTick(1))/4;
% Set the tick direction
ax.TickDir = 'out';
% draw the plot to generate the undocumented vertex data var
drawnow()
%% R2015a
% X, Y and Z row of the start and end of the individual axle.
ax.XRuler.Axle.VertexData(1,1) = 0;
ax.YRuler.Axle.VertexData(2,1) = 0;
%% R2015b
% extract the x axis vertext data
% X, Y and Z row of the start and end of the individual axle.
vd = get(ax.XAxis.Axle,'VertexData');
% reset the zero value
vd(1,1) = 0;
% Update the vertex data
set(ax.XAxis.Axle,'VertexData',vd);
% repeat for Y (set 2nd row)
vd = get(ax.YAxis.Axle,'VertexData');
vd(2,1) = 0;
set(ax.YAxis.Axle,'VertexData',vd);
Edit: The vertex is something that Matlab recreates whenever the axes/figure changes size or if you zoom or pan for example.
You can try to counteract this (remember you are using undocumented features here) by adding a listener to attempt to capture this. We can use the MarkedClean event which is called quite a lot of times.
addlistener ( ax, 'MarkedClean', #(obj,event)resetVertex(ax) );
Where you resetVertex function is something like: (R2015b shown only)
Edit 2 added the code to turn off the minor ticks below 0.
function resetVertex ( ax )
% extract the x axis vertext data
% X, Y and Z row of the start and end of the individual axle.
ax.XAxis.Axle.VertexData(1,1) = 0;
% repeat for Y (set 2nd row)
ax.YAxis.Axle.VertexData(2,1) = 0;
% You can modify the minor Tick values by modifying the vertex data
% for them, e.g. remove any minor ticks below 0
ax.XAxis.MinorTickChild.VertexData(:,ax.XAxis.MinorTickChild.VertexData(1,:)<0) = [];
ax.YAxis.MinorTickChild.VertexData(:,ax.YAxis.MinorTickChild.VertexData(2,:)<0) = [];
end
Note: this uses undocumented features -> so may only work in certain versions of Matlab (I have added the code for r2015a & r2015b) and Matlab may recreate the vertex data depending on what you do with the plots..
Here is a simple way for achieving that:
% some data:
x = 1:100;
f=#(x) 5.*x;
y=f(x)+rand(1,length(x))*50;
close all
% plotting:
f1 = figure('Color','white');
ax = axes;
plot(ax,x,y,'o');
% 'clean' the data area a little bit:
box off
ax.TickDir = 'out';
% pushing the axis a bit forward:
lims = axis;
pos = ax.Position;
axis([lims(1)-ax.XTick(2)/5 lims(2)+0.1 lims(3)-ax.YTick(2)/5 lims(4)+0.1])
% Create lines
firstXtick = 0.013; %this value need to be adjusted only once per figure
firstYtick = 0.023; %this value need to be adjusted only once per figure
lx = annotation(f1,'line',[pos(1) pos(1)+firstXtick],...
[pos(2) pos(2)],'Color',[1 1 1],'LineWidth',1);
ly = annotation(f1,'line',[pos(1) pos(1)],...
[pos(2) pos(2)+firstYtick],'Color',[1 1 1],'LineWidth',1);
Which yields this figure:
The only thing to adjust here, once per type of figure, is firstXtick and firstYtick values, that have to be fine tuned to the specific axis. After setting them to the correct value the figure can be resized with no problem. Zoom and pan require a little fixes.
You can start your axes from less than zero and then remove the less than zero ticks from your plot. e.g.
plot(0:3:30,0:3:30); %Some random data for plotting
h = gca;
axis([-1 30 -1 30]); %Setting the axis from less than zero
box off; %Removing box
h.TickDir = 'out'; %Setting Direction of ticks to outwards
h.XTickLabel(1)= {' '}; %Removing the first tick of X-axis
h.YTickLabel(1)= {' '}; %Removing the first tick of Y-axis
With this code, you'll get this result:
This may have a drawback, sometimes, that zero ticks may also get removed (as you can see in above figure). This is because the plot had set the first ticks of axes equal to zero. This can be avoided using if condition. So, the code can be modified as below:
plot(0:3:30,0:3:30);
h = gca;
axis([-1 30 -1 30]);
box off;
h.TickDir = 'out';
if str2num(cell2mat(h.XTickLabel(1))) <0
h.XTickLabel(1)= {' '};
end
if str2num(cell2mat(h.YTickLabel(1))) <0
h.YTickLabel(1)= {' '};
end
The above code will yield the following result:-
Also note that, for your case, since your axes ticks are very less, -1 may not be much suitable for the starting value of axes and you may need to use -0.1 instead i.e. axis([-0.1 30 -0.1 30]);
With a slight modification of #matlabgui's answer you can track the (major) tick limits:
ax = gca();
% Set the tick direction
ax.TickDir = 'out';
% Make sure this stays when saving, zooming, etc
addlistener ( ax, 'MarkedClean', #(obj,event) change_ticks(ax) );
% Draw the plot to generate the undocumented vertex data variable
% and call callback for the first time
drawnow();
The callback
function change_ticks( ax )
% Modify ruler
ax.XRuler.Axle.VertexData(1,1) = ax.XTick(1);
ax.XRuler.Axle.VertexData(1,2) = ax.XTick(end);
ax.YRuler.Axle.VertexData(2,1) = ax.YTick(1);
ax.YRuler.Axle.VertexData(2,2) = ax.YTick(end);
end
I haven't test extensively but seems to work for custom ticks too. This nicely cuts the rulers not only on zero but beyond the fist and last tick. This was tested in Matlab 2019a on Windows and ax.XRuler.Axle.VertexData works just fine. Note this is only for major ticks!
I am trying to create area plots with 2 y-axis using plotyy by using 'area' function. I don't want to have any tick marks or labels or titles but I just want the outside box. I would also like to save just the plot (not the entire figure window) as a png file.
When I turn off the x and y-axis ticks and labels, the box looks thin on the bottom and left, thick on the top and right. What am I doing wrong?
Also, if I try to make the box 'LineWidth' fat, I see two tick marks at the bottom left and bottom right - which ruin the plot and I am unable to remove. Can someone help?
My test code is below:
x = 1:100;
y1 = rand(size(x));
y2 = 100*rand(size(x));
fig_handle = figure('units','inches','position',[1 1 9 3]);
[a,p1,p2] = plotyy(x,y1,x,y2,'area');
c1 = get(p1,'child');
c2 = get(p2,'child');
set(c1,'facea',0.5,'FaceColor','b','EdgeColor',[0 0 0]);
set(c2,'facea',0.5,'FaceColor','r','EdgeColor',[0 0 0]);
set(c1,'Line','None');
set(c2,'Line','None');
set(a,'Layer','top')
set(a,'XTick',[]);
set(a(1),'YTick',[]);
set(a(2),'YTick',[]);
set(a,'TickDir','in')
set(a,'LineWidth',5);
Also, notice how the left Y-axis has red area on it, while the right Y-axis doesn't. Is this fix-able?
Any help would be appreciated! Also, I am new to StackOverflow - so, if these are too many questions in one post, please pardon me and I will post them as separate requests/questions.
Here is a workaround for the red appearing on the left Y-axis.
Since the axes line is quite thick, the data displayed close to it is drawn over it. To avoid this, you can slightly shift the x limits of the axes to make more room for the data. Do so by changing the XLim property of either axes since they share the same x limit:
XL = get(a,'Xlim');
xl = XL{1}; %// here XL{1} and XL{2} are the same...[1 100]
set(a(:),'Xlim',[xl(1)-.5 xl(2)+.5])
As for the annoying tick marks at the bottom of the plot, I must say that I don't know how to remove them while keeping the axes visible.
As an alternative solution to plotyy, here is a way to obtain a good result (I think) without plotyy. The trick is to superimpose 2 axes and make both of them not visible, then set the figure's color to white and add a black rectangle surrounding the plot.
Here is the code:
clear
clc
close all
x = 1:100;
y1 = rand(size(x));
y2 = 100*rand(size(x));
H1 = area(x,y1);
%// Set the figure color to white
set(gcf,'Color','w')
%// Plot data and set different properties for each axes
A1 = gca;
A2 = axes('Position',get(A1,'Position'));
H2 = area(x,y2);
set(A2,'YAxisLocation','right','Color','none','XTickLabel',[]);
set(A2,'XLim',get(A1,'XLim'),'XTick',[],'YTick',[]);
set(A1,'XTick',[],'YTick',[]);
%// Make both axes not visible.
set(A2,'Visible','off')
set(A1,'Visible','off')
axes(A1)
%// Get axes limits
XL = get(gca,'XLim');
YL= get(gca,'YLim');
%// Create rectangle as a bounding box
rectangle('Position',[XL(1) YL(1) XL(2)-1 YL(2)],'LineWidth',5)
%//===
%// Change the data color/properties
hP = findobj('Type','patch');
set(hP(1),'FaceColor','b','FaceAlpha',.5,'EdgeColor',[0 0 0],'line','none')
set(hP(2),'FaceColor','r','FaceAlpha',.5,'EdgeColor',[0 0 0],'line','none')
And the output:
It's not perfect but hope that helps!
I have a curve in which the minimum point is not obvious to the naked eye. For that reason I'm looking to highlight the minimum point using a marker.
Ideally I would highlight the point with a marker and also have its coordinates displayed in text on the figure.
You can do it this way:
%// Example plot
x = 1:10;
y = randn(1,10);
plot(x,y)
%// Marker at minimum
[ymin imin] = min(y);
xmin = x(imin);
hold on
style = 'ro'; %// red circle. Change as needed
markersize = 10; %// change as needed
plot(x(imin), ymin, style, 'markersize', markersize)
%// Text with coordinates of minimum
offset = -.05; %// vertical offset as a fraction of y-axis span. Change as needed.
text(x(imin),ymin+diff(ylim)*offset,['(' num2str(x(imin)) ',' num2str(ymin) ')'])
%// Enlarge y axis so that text is properly seen, if offset is negative
ylim(ylim+[diff(ylim)*offset*(offset<0) 0])
You might also want to enlarge the x axis if the text is close to the left or to the right. It could be done with xlim in a similar way.
Assuming you know the coordinates of that point, you can do something like:
hold on; % add things to the current figure
plot(x_coord, y_coord, '+r')
This will put a red plus sign at that point.
This should plot the minimum point(s), assuming you have data like y and x,
plot(x(y==min(y)),min(y),'o')
adding the text could be trickier depending on what you want, but at least these are the coordinates.
Is there a method of aligning tick labels?
I have a figure that has two y axes where the values vary greatly. I would like to align the tick labels so that each value shown on one y label matches up with a value on the opposite ylabel. For example:
data1 = 1+ (12-1).*rand(365,1);
data2 = 1 + (700-1).*rand(365,1);
time = 1:365;
figure(1);
ax1 = axes('position',[0.05 0.5 0.22 0.37]);
plot(time,data1,'k','linewidth',1);
ylabel('label 1');
pos=double(get(ax1,'position'));
ax2=axes('position',pos,'color','none','YAxisLocation','right','xtick',[])
hold on;
plot(time,data2,'r','linewidth',1,'parent',ax2);
ylabel(ax2,'label 2');
Here I would like the second y axis to have the same number of ticks as the first y axis as well as the same spacing between them. How can I achieve this?
Use plotyy instead of plot, it handles this for you:
plotyy(time, data1, time, data2);
You can set the y axis limits and tick locations explicitly:
ylim(ax1,[lowerBound upperBound])
set(ax1,'ytick',[tick1 tick2 tick3 tick4])
This can let you fine tune the tick locations for a particular plot. It makes zooming and panning less functional, since the ticks are often left behind.