@jfruan
2017-04-14T00:49:27.000000Z
字数 14743
阅读 2280
Matlab
Rotate x-tick labels to any angle, preserving font settings and coping with resize, zoom, pan etc.
The MATLAB plot gallery provides examples of many ways to display data graphically in MATLAB. You can view and download source code for each plot, and use it in your own MATLAB project.
Get started with Plotly's MATLAB graphing library to make interactive, publication-quality graphs online.
clear
% file size
x=[136,148,158,168,178,188,198,208,228,248,276,296,316,336,356];
% read local
y11 = [0.4768,0.8226,0.8697,0.9738,0.9484,0.9763,0.9703,0.9602,0.9786,0.952,0.852,0.9142,0.9341,0.9365,0.9162]';
% write local
y12 = [0.9706,0.9673,0.984,0.9174,0.9816,0.9396,0.9429,0.9385,0.9221,0.8361,0.9497,0.9266,0.936,0.9463,0.9456]';
% read aws
y21 = [0.5808,0.7663,0.7880,0.8978,0.8560,0.9079,0.8860,0.8784,0.9048,0.8770,0.8032,0.8396,0.8528,0.8745,0.8557]';
% write aws
y22 = [0.8161,0.7515,0.8364,0.8029,0.8265,0.8200,0.8194,0.8583,0.8290,0.7487,0.8392,0.7737,0.8248,0.8034,0.7700]';
% local
data1 = [y11, y12];
% aws
data2 = [y21, y22];
b = bar(data1,1.0);
b(1).FaceColor = [1 128 104]/255;
% b(2).FaceColor = [129 192 100]/255
b(2).FaceColor = [255 255 102]/255;
set(gcf,'unit','centimeters','position',[10 10 20 15])
% set(gca,'YLim',[0 1.2]); %Y轴的数据显示范围
set(gca,'XTickLabel',{'136','148','158','168','178','188','198','208','228','248','276','296','316','336','356'})
h = gca;
rotateXLabels(h, 45); %旋转角度
xlabel('File size (MB)'),ylabel('Similarity')
hl = legend('HDFS read operation ','HDFS write operation');
set(hl,'Location','northoutside','Orientation','horizontal')
set(gca,'FontSize',15);
set(gca,'FontName','Times new roman');
For releases prior to R2016b, specify the rotation using the XTickLabelRotation and YTickLabelRotation properties of the Axes object. For example, assign the Axes object to a variable, such as ax = gca. Then set the XTickLabelRotation property using dot notation, such as ax.XTickLabelRotation = 45.
% For Matlab R2016b
x = linspace(0,10000,21);
y = x.^2;
stem(x,y)
ax = gca
ax.XTickLabelRotation = 45
% Start with Matlab R2014b, prior to R2016b
x = 1000*rand(40,1);
y = rand(40,1);
scatter(x,y)
xtickangle(45)
ytickangle(90)
function hh = rotateXLabels( ax, angle, varargin )
%rotateXLabels: rotate any xticklabels
%
% hh = rotateXLabels(ax,angle) rotates all XLabels on axes AX by an angle
% ANGLE (in degrees). Handles to the resulting text objects are returned
% in HH.
%
% hh = rotateXLabels(ax,angle,param,value,...) also allows one or more
% optional parameters to be specified. Possible parameters are:
% 'MaxStringLength' The maximum length of label to show (default inf)
%
% Examples:
% >> bar( hsv(5)+0.05 )
% >> days = {'Monday','Tuesday','Wednesday','Thursday','Friday'};
% >> set( gca(), 'XTickLabel', days )
% >> rotateXLabels( gca(), 45 )
%
% See also: GCA, BAR
% Copyright 2006-2013 The MathWorks Ltd.
error( nargchk( 2, inf, nargin ) );
if ~isnumeric( angle ) || ~isscalar( angle )
error( 'RotateXLabels:BadAngle', 'Parameter ANGLE must be a scalar angle in degrees' )
end
angle = mod( angle, 360 );
% From R2014b, rotating labels is built-in using 'XTickLabelRotation'
if ~verLessThan('matlab','8.4.0')
set(ax, 'XTickLabelRotation', angle)
if nargout > 1
hh = [];
end
return;
end
[maxStringLength] = parseInputs( varargin{:} );
% Get the existing label texts and clear them
[vals, labels] = findAndClearExistingLabels( ax, maxStringLength );
% Create the new label texts
h = createNewLabels( ax, vals, labels, angle );
% Reposition the axes itself to leave space for the new labels
repositionAxes( ax );
% If an X-label is present, move it too
repositionXLabel( ax );
% Store angle
setappdata( ax, 'RotateXLabelsAngle', angle );
% Only send outputs if requested
if nargout
hh = h;
end
%-------------------------------------------------------------------------%
function [maxStringLength] = parseInputs( varargin )
% Parse optional inputs
maxStringLength = inf;
if nargin > 0
params = varargin(1:2:end);
values = varargin(2:2:end);
if numel( params ) ~= numel( values )
error( 'RotateXLabels:BadSyntax', 'Optional arguments must be specified as parameter-value pairs.' );
end
if any( ~cellfun( 'isclass', params, 'char' ) )
error( 'RotateXLabels:BadSyntax', 'Optional argument names must be specified as strings.' );
end
for pp=1:numel( params )
switch upper( params{pp} )
case 'MAXSTRINGLENGTH'
maxStringLength = values{pp};
otherwise
error( 'RotateXLabels:BadParam', 'Optional parameter ''%s'' not recognised.', params{pp} );
end
end
end
end % parseInputs
%-------------------------------------------------------------------------%
function [vals,labels] = findAndClearExistingLabels( ax, maxStringLength )
% Get the current tick positions so that we can place our new labels
vals = get( ax, 'XTick' );
% Now determine the labels. We look first at for previously rotated labels
% since if there are some the actual labels will be empty.
ex = findall( ax, 'Tag', 'RotatedXTickLabel' );
if isempty( ex )
% Store the positions and labels
labels = get( ax, 'XTickLabel' );
if isempty( labels )
% No labels!
return
else
if ~iscell(labels)
labels = cellstr(labels);
end
end
% Clear existing labels so that xlabel is in the right position
set( ax, 'XTickLabel', {}, 'XTickMode', 'Manual' );
setappdata( ax, 'OriginalXTickLabels', labels );
else
% Labels have already been rotated, so capture them
labels = getappdata( ax, 'OriginalXTickLabels' );
set(ex, 'DeleteFcn', []);
delete(ex);
end
% Limit the length, if requested
if isfinite( maxStringLength )
for ll=1:numel( labels )
if length( labels{ll} ) > maxStringLength
labels{ll} = labels{ll}(1:maxStringLength);
end
end
end
end % findAndClearExistingLabels
%-------------------------------------------------------------------------%
function restoreDefaultLabels( ax )
% Restore the default axis behavior
removeListeners( ax );
% Try to restore the tick marks and labels
set( ax, 'XTickMode', 'auto', 'XTickLabelMode', 'auto' );
rmappdata( ax, 'OriginalXTickLabels' );
% Try to restore the axes position
if isappdata( ax, 'OriginalAxesPosition' )
set( ax, 'Position', getappdata( ax, 'OriginalAxesPosition' ) );
rmappdata( ax, 'OriginalAxesPosition' );
end
end
%-------------------------------------------------------------------------%
function textLabels = createNewLabels( ax, vals, labels, angle )
% Work out the ticklabel positions
zlim = get(ax,'ZLim');
z = zlim(1);
% We want to work in normalised coords, but this doesn't print
% correctly. Instead we have to work in data units even though it
% makes positioning hard.
y = getYPositionToUse( ax );
% Now create new text objects in similar positions.
textLabels = -1*ones( numel( vals ), 1 );
for ll=1:numel(vals)
textLabels(ll) = text( ...
'Units', 'Data', ...
'Position', [vals(ll), y, z], ...
'String', labels{ll}, ...
'Parent', ax, ...
'Clipping', 'off', ...
'Rotation', angle, ...
'Tag', 'RotatedXTickLabel', ...
'UserData', vals(ll));
end
% So that we can respond to CLA and CLOSE, attach a delete
% callback. We only attach it to one label to save massive numbers
% of callbacks during axes shut-down.
set(textLabels(end), 'DeleteFcn', @onTextLabelDeleted);
% Now copy font properties into the texts
updateFont();
% Update the alignment of the text
updateAlignment();
end % createNewLabels
%-------------------------------------------------------------------------%
function repositionAxes( ax )
% Reposition the axes so that there's room for the labels
% Note that we only do this if the OuterPosition is the thing being
% controlled
if ~strcmpi( get( ax, 'ActivePositionProperty' ), 'OuterPosition' )
return;
end
% Work out the maximum height required for the labels
labelHeight = getLabelHeight(ax);
% Remove listeners while we mess around with things, otherwise we'll
% trigger redraws recursively
removeListeners( ax );
% Change to normalized units for the position calculation
oldUnits = get( ax, 'Units' );
set( ax, 'Units', 'Normalized' );
% Not sure why, but the extent seems to be proportional to the height of the axes.
% Correct that now.
set( ax, 'ActivePositionProperty', 'Position' );
pos = get( ax, 'Position' );
axesHeight = pos(4);
% Make sure we don't adjust away the axes entirely!
heightAdjust = min( (axesHeight*0.9), labelHeight*axesHeight );
% Move the axes
if isappdata( ax, 'OriginalAxesPosition' )
pos = getappdata( ax, 'OriginalAxesPosition' );
else
pos = get(ax,'Position');
setappdata( ax, 'OriginalAxesPosition', pos );
end
if strcmpi( get( ax, 'XAxisLocation' ), 'Bottom' )
% Move it up and reduce the height
set( ax, 'Position', pos+[0 heightAdjust 0 -heightAdjust] )
else
% Just reduce the height
set( ax, 'Position', pos+[0 0 0 -heightAdjust] )
end
set( ax, 'Units', oldUnits );
set( ax, 'ActivePositionProperty', 'OuterPosition' );
% Make sure we find out if axes properties are changed
addListeners( ax );
end % repositionAxes
%-------------------------------------------------------------------------%
function repositionXLabel( ax )
% Try to work out where to put the xlabel
removeListeners( ax );
labelHeight = getLabelHeight(ax);
% Use the new max extent to move the xlabel. We may also need to
% move the title
xlab = get(ax,'XLabel');
titleh = get( ax, 'Title' );
set( [xlab,titleh], 'Units', 'Normalized' );
if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
titleExtent = get( xlab, 'Extent' );
set( xlab, 'Position', [0.5 1+labelHeight-titleExtent(4) 0] );
set( titleh, 'Position', [0.5 1+labelHeight 0] );
else
set( xlab, 'Position', [0.5 -labelHeight 0] );
set( titleh, 'Position', [0.5 1 0] );
end
addListeners( ax );
end % repositionXLabel
%-------------------------------------------------------------------------%
function height = getLabelHeight(ax)
height = 0;
textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
if isempty(textLabels)
return;
end
oldUnits = get( textLabels(1), 'Units' );
set( textLabels, 'Units', 'Normalized' );
for ll=1:numel(vals)
ext = get( textLabels(ll), 'Extent' );
if ext(4) > height
height = ext(4);
end
end
set( textLabels, 'Units', oldUnits );
end % getLabelExtent
%-------------------------------------------------------------------------%
function updateFont()
% Update the rotated text fonts when the axes font changes
properties = {
'FontName'
'FontSize'
'FontAngle'
'FontWeight'
'FontUnits'
};
propertyValues = get( ax, properties );
textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
set( textLabels, properties, propertyValues );
end % updateFont
function updateAlignment()
textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
angle = get( textLabels(1), 'Rotation' );
% Depending on the angle, we may need to change the alignment. We change
% alignments within 5 degrees of each 90 degree orientation.
if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
if 0 <= angle && angle < 5
set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
elseif 5 <= angle && angle < 85
set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' );
elseif 85 <= angle && angle < 95
set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' );
elseif 95 <= angle && angle < 175
set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' );
elseif 175 <= angle && angle < 185
set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
elseif 185 <= angle && angle < 265
set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' );
elseif 265 <= angle && angle < 275
set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' );
elseif 275 <= angle && angle < 355
set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' );
else % 355-360
set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
end
else
if 0 <= angle && angle < 5
set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
elseif 5 <= angle && angle < 85
set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' );
elseif 85 <= angle && angle < 95
set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' );
elseif 95 <= angle && angle < 175
set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' );
elseif 175 <= angle && angle < 185
set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
elseif 185 <= angle && angle < 265
set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' );
elseif 265 <= angle && angle < 275
set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' );
elseif 275 <= angle && angle < 355
set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' );
else % 355-360
set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
end
end
end
%-------------------------------------------------------------------------%
function onAxesFontChanged( ~, ~ )
updateFont();
repositionAxes( ax );
repositionXLabel( ax );
end % onAxesFontChanged
%-------------------------------------------------------------------------%
function onAxesPositionChanged( ~, ~ )
% We need to accept the new position, so remove the appdata before
% redrawing
if isappdata( ax, 'OriginalAxesPosition' )
rmappdata( ax, 'OriginalAxesPosition' );
end
if isappdata( ax, 'OriginalXLabelPosition' )
rmappdata( ax, 'OriginalXLabelPosition' );
end
repositionAxes( ax );
repositionXLabel( ax );
end % onAxesPositionChanged
%-------------------------------------------------------------------------%
function onXAxisLocationChanged( ~, ~ )
updateAlignment();
repositionAxes( ax );
repositionXLabel( ax );
end % onXAxisLocationChanged
%-------------------------------------------------------------------------%
function onAxesLimitsChanged( ~, ~ )
% The limits have moved, so make sure the labels are still ok
textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
xlim = get( ax, 'XLim' );
pos = [0 getYPositionToUse( ax )];
for tt=1:numel( textLabels )
xval = get( textLabels(tt), 'UserData' );
pos(1) = xval;
% If the tick is off the edge, make it invisible
if xval<xlim(1) || xval>xlim(2)
set( textLabels(tt), 'Visible', 'off', 'Position', pos )
elseif ~strcmpi( get( textLabels(tt), 'Visible' ), 'on' )
set( textLabels(tt), 'Visible', 'on', 'Position', pos )
else
% Just set the position
set( textLabels(tt), 'Position', pos );
end
end
repositionXLabel( ax );
end % onAxesPositionChanged
%-------------------------------------------------------------------------%
function onTextLabelDeleted( ~, ~ )
% The final text label has been deleted. This is likely from a
% "cla" or "close" call, so we should remove all of our dirty
% hacks.
restoreDefaultLabels(ax);
end
%-------------------------------------------------------------------------%
function addListeners( ax )
% Create listeners. We store the array of listeners in the axes to make
% sure that they have the same life-span as the axes they are listening to.
axh = handle( ax );
listeners = [
handle.listener( axh, findprop( axh, 'FontName' ), 'PropertyPostSet', @onAxesFontChanged )
handle.listener( axh, findprop( axh, 'FontSize' ), 'PropertyPostSet', @onAxesFontChanged )
handle.listener( axh, findprop( axh, 'FontWeight' ), 'PropertyPostSet', @onAxesFontChanged )
handle.listener( axh, findprop( axh, 'FontAngle' ), 'PropertyPostSet', @onAxesFontChanged )
handle.listener( axh, findprop( axh, 'FontUnits' ), 'PropertyPostSet', @onAxesFontChanged )
handle.listener( axh, findprop( axh, 'OuterPosition' ), 'PropertyPostSet', @onAxesPositionChanged )
handle.listener( axh, findprop( axh, 'XLim' ), 'PropertyPostSet', @onAxesLimitsChanged )
handle.listener( axh, findprop( axh, 'YLim' ), 'PropertyPostSet', @onAxesLimitsChanged )
handle.listener( axh, findprop( axh, 'XAxisLocation' ), 'PropertyPostSet', @onXAxisLocationChanged )
];
setappdata( ax, 'RotateXLabelsListeners', listeners );
end % addListeners
%-------------------------------------------------------------------------%
function removeListeners( ax )
% Rempove any property listeners whilst we are fiddling with the axes
if isappdata( ax, 'RotateXLabelsListeners' )
delete( getappdata( ax, 'RotateXLabelsListeners' ) );
rmappdata( ax, 'RotateXLabelsListeners' );
end
end % removeListeners
%-------------------------------------------------------------------------%
function y = getYPositionToUse( ax )
% Use the direction and XAxisLocation properties to work out which
% Y-value to draw the labels at.
whichYLim = 1;
% If YDir is reversed, switch where we position
if strcmpi( get( ax, 'YDir' ), 'Reverse' )
whichYLim = 3-whichYLim;
end
% If set to "top", then switch (again?)
if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
whichYLim = 3-whichYLim;
end
% Now get the value
ylim = get( ax, 'YLim' );
y = ylim(whichYLim);
end % getYPositionToUse
end % EOF