[关闭]
@jfruan 2017-04-14T00:49:27.000000Z 字数 14743 阅读 2280

Matlab Rotate x-axis tick labels

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.

二、实例

1. rotateXLabels(ax, angle, varargin)

  1. clear
  2. % file size
  3. x=[136,148,158,168,178,188,198,208,228,248,276,296,316,336,356];
  4. % read local
  5. 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]';
  6. % write local
  7. 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]';
  8. % read aws
  9. 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]';
  10. % write aws
  11. 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]';
  12. % local
  13. data1 = [y11, y12];
  14. % aws
  15. data2 = [y21, y22];
  16. b = bar(data1,1.0);
  17. b(1).FaceColor = [1 128 104]/255;
  18. % b(2).FaceColor = [129 192 100]/255
  19. b(2).FaceColor = [255 255 102]/255;
  20. set(gcf,'unit','centimeters','position',[10 10 20 15])
  21. % set(gca,'YLim',[0 1.2]); %Y轴的数据显示范围
  22. set(gca,'XTickLabel',{'136','148','158','168','178','188','198','208','228','248','276','296','316','336','356'})
  23. h = gca;
  24. rotateXLabels(h, 45); %旋转角度
  25. xlabel('File size (MB)'),ylabel('Similarity')
  26. hl = legend('HDFS read operation ','HDFS write operation');
  27. set(hl,'Location','northoutside','Orientation','horizontal')
  28. set(gca,'FontSize',15);
  29. set(gca,'FontName','Times new roman');

2. xtickangle(ax,angle)

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.

  1. % For Matlab R2016b
  2. x = linspace(0,10000,21);
  3. y = x.^2;
  4. stem(x,y)
  5. ax = gca
  6. ax.XTickLabelRotation = 45
  1. % Start with Matlab R2014b, prior to R2016b
  2. x = 1000*rand(40,1);
  3. y = rand(40,1);
  4. scatter(x,y)
  5. xtickangle(45)
  6. ytickangle(90)

三、显示效果

1. rotateXLabels(ax, angle, varargin)

2. xtickangle(ax,angle)

四、代码

1. rotateXLabels.m

  1. function hh = rotateXLabels( ax, angle, varargin )
  2. %rotateXLabels: rotate any xticklabels
  3. %
  4. % hh = rotateXLabels(ax,angle) rotates all XLabels on axes AX by an angle
  5. % ANGLE (in degrees). Handles to the resulting text objects are returned
  6. % in HH.
  7. %
  8. % hh = rotateXLabels(ax,angle,param,value,...) also allows one or more
  9. % optional parameters to be specified. Possible parameters are:
  10. % 'MaxStringLength' The maximum length of label to show (default inf)
  11. %
  12. % Examples:
  13. % >> bar( hsv(5)+0.05 )
  14. % >> days = {'Monday','Tuesday','Wednesday','Thursday','Friday'};
  15. % >> set( gca(), 'XTickLabel', days )
  16. % >> rotateXLabels( gca(), 45 )
  17. %
  18. % See also: GCA, BAR
  19. % Copyright 2006-2013 The MathWorks Ltd.
  20. error( nargchk( 2, inf, nargin ) );
  21. if ~isnumeric( angle ) || ~isscalar( angle )
  22. error( 'RotateXLabels:BadAngle', 'Parameter ANGLE must be a scalar angle in degrees' )
  23. end
  24. angle = mod( angle, 360 );
  25. % From R2014b, rotating labels is built-in using 'XTickLabelRotation'
  26. if ~verLessThan('matlab','8.4.0')
  27. set(ax, 'XTickLabelRotation', angle)
  28. if nargout > 1
  29. hh = [];
  30. end
  31. return;
  32. end
  33. [maxStringLength] = parseInputs( varargin{:} );
  34. % Get the existing label texts and clear them
  35. [vals, labels] = findAndClearExistingLabels( ax, maxStringLength );
  36. % Create the new label texts
  37. h = createNewLabels( ax, vals, labels, angle );
  38. % Reposition the axes itself to leave space for the new labels
  39. repositionAxes( ax );
  40. % If an X-label is present, move it too
  41. repositionXLabel( ax );
  42. % Store angle
  43. setappdata( ax, 'RotateXLabelsAngle', angle );
  44. % Only send outputs if requested
  45. if nargout
  46. hh = h;
  47. end
  48. %-------------------------------------------------------------------------%
  49. function [maxStringLength] = parseInputs( varargin )
  50. % Parse optional inputs
  51. maxStringLength = inf;
  52. if nargin > 0
  53. params = varargin(1:2:end);
  54. values = varargin(2:2:end);
  55. if numel( params ) ~= numel( values )
  56. error( 'RotateXLabels:BadSyntax', 'Optional arguments must be specified as parameter-value pairs.' );
  57. end
  58. if any( ~cellfun( 'isclass', params, 'char' ) )
  59. error( 'RotateXLabels:BadSyntax', 'Optional argument names must be specified as strings.' );
  60. end
  61. for pp=1:numel( params )
  62. switch upper( params{pp} )
  63. case 'MAXSTRINGLENGTH'
  64. maxStringLength = values{pp};
  65. otherwise
  66. error( 'RotateXLabels:BadParam', 'Optional parameter ''%s'' not recognised.', params{pp} );
  67. end
  68. end
  69. end
  70. end % parseInputs
  71. %-------------------------------------------------------------------------%
  72. function [vals,labels] = findAndClearExistingLabels( ax, maxStringLength )
  73. % Get the current tick positions so that we can place our new labels
  74. vals = get( ax, 'XTick' );
  75. % Now determine the labels. We look first at for previously rotated labels
  76. % since if there are some the actual labels will be empty.
  77. ex = findall( ax, 'Tag', 'RotatedXTickLabel' );
  78. if isempty( ex )
  79. % Store the positions and labels
  80. labels = get( ax, 'XTickLabel' );
  81. if isempty( labels )
  82. % No labels!
  83. return
  84. else
  85. if ~iscell(labels)
  86. labels = cellstr(labels);
  87. end
  88. end
  89. % Clear existing labels so that xlabel is in the right position
  90. set( ax, 'XTickLabel', {}, 'XTickMode', 'Manual' );
  91. setappdata( ax, 'OriginalXTickLabels', labels );
  92. else
  93. % Labels have already been rotated, so capture them
  94. labels = getappdata( ax, 'OriginalXTickLabels' );
  95. set(ex, 'DeleteFcn', []);
  96. delete(ex);
  97. end
  98. % Limit the length, if requested
  99. if isfinite( maxStringLength )
  100. for ll=1:numel( labels )
  101. if length( labels{ll} ) > maxStringLength
  102. labels{ll} = labels{ll}(1:maxStringLength);
  103. end
  104. end
  105. end
  106. end % findAndClearExistingLabels
  107. %-------------------------------------------------------------------------%
  108. function restoreDefaultLabels( ax )
  109. % Restore the default axis behavior
  110. removeListeners( ax );
  111. % Try to restore the tick marks and labels
  112. set( ax, 'XTickMode', 'auto', 'XTickLabelMode', 'auto' );
  113. rmappdata( ax, 'OriginalXTickLabels' );
  114. % Try to restore the axes position
  115. if isappdata( ax, 'OriginalAxesPosition' )
  116. set( ax, 'Position', getappdata( ax, 'OriginalAxesPosition' ) );
  117. rmappdata( ax, 'OriginalAxesPosition' );
  118. end
  119. end
  120. %-------------------------------------------------------------------------%
  121. function textLabels = createNewLabels( ax, vals, labels, angle )
  122. % Work out the ticklabel positions
  123. zlim = get(ax,'ZLim');
  124. z = zlim(1);
  125. % We want to work in normalised coords, but this doesn't print
  126. % correctly. Instead we have to work in data units even though it
  127. % makes positioning hard.
  128. y = getYPositionToUse( ax );
  129. % Now create new text objects in similar positions.
  130. textLabels = -1*ones( numel( vals ), 1 );
  131. for ll=1:numel(vals)
  132. textLabels(ll) = text( ...
  133. 'Units', 'Data', ...
  134. 'Position', [vals(ll), y, z], ...
  135. 'String', labels{ll}, ...
  136. 'Parent', ax, ...
  137. 'Clipping', 'off', ...
  138. 'Rotation', angle, ...
  139. 'Tag', 'RotatedXTickLabel', ...
  140. 'UserData', vals(ll));
  141. end
  142. % So that we can respond to CLA and CLOSE, attach a delete
  143. % callback. We only attach it to one label to save massive numbers
  144. % of callbacks during axes shut-down.
  145. set(textLabels(end), 'DeleteFcn', @onTextLabelDeleted);
  146. % Now copy font properties into the texts
  147. updateFont();
  148. % Update the alignment of the text
  149. updateAlignment();
  150. end % createNewLabels
  151. %-------------------------------------------------------------------------%
  152. function repositionAxes( ax )
  153. % Reposition the axes so that there's room for the labels
  154. % Note that we only do this if the OuterPosition is the thing being
  155. % controlled
  156. if ~strcmpi( get( ax, 'ActivePositionProperty' ), 'OuterPosition' )
  157. return;
  158. end
  159. % Work out the maximum height required for the labels
  160. labelHeight = getLabelHeight(ax);
  161. % Remove listeners while we mess around with things, otherwise we'll
  162. % trigger redraws recursively
  163. removeListeners( ax );
  164. % Change to normalized units for the position calculation
  165. oldUnits = get( ax, 'Units' );
  166. set( ax, 'Units', 'Normalized' );
  167. % Not sure why, but the extent seems to be proportional to the height of the axes.
  168. % Correct that now.
  169. set( ax, 'ActivePositionProperty', 'Position' );
  170. pos = get( ax, 'Position' );
  171. axesHeight = pos(4);
  172. % Make sure we don't adjust away the axes entirely!
  173. heightAdjust = min( (axesHeight*0.9), labelHeight*axesHeight );
  174. % Move the axes
  175. if isappdata( ax, 'OriginalAxesPosition' )
  176. pos = getappdata( ax, 'OriginalAxesPosition' );
  177. else
  178. pos = get(ax,'Position');
  179. setappdata( ax, 'OriginalAxesPosition', pos );
  180. end
  181. if strcmpi( get( ax, 'XAxisLocation' ), 'Bottom' )
  182. % Move it up and reduce the height
  183. set( ax, 'Position', pos+[0 heightAdjust 0 -heightAdjust] )
  184. else
  185. % Just reduce the height
  186. set( ax, 'Position', pos+[0 0 0 -heightAdjust] )
  187. end
  188. set( ax, 'Units', oldUnits );
  189. set( ax, 'ActivePositionProperty', 'OuterPosition' );
  190. % Make sure we find out if axes properties are changed
  191. addListeners( ax );
  192. end % repositionAxes
  193. %-------------------------------------------------------------------------%
  194. function repositionXLabel( ax )
  195. % Try to work out where to put the xlabel
  196. removeListeners( ax );
  197. labelHeight = getLabelHeight(ax);
  198. % Use the new max extent to move the xlabel. We may also need to
  199. % move the title
  200. xlab = get(ax,'XLabel');
  201. titleh = get( ax, 'Title' );
  202. set( [xlab,titleh], 'Units', 'Normalized' );
  203. if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
  204. titleExtent = get( xlab, 'Extent' );
  205. set( xlab, 'Position', [0.5 1+labelHeight-titleExtent(4) 0] );
  206. set( titleh, 'Position', [0.5 1+labelHeight 0] );
  207. else
  208. set( xlab, 'Position', [0.5 -labelHeight 0] );
  209. set( titleh, 'Position', [0.5 1 0] );
  210. end
  211. addListeners( ax );
  212. end % repositionXLabel
  213. %-------------------------------------------------------------------------%
  214. function height = getLabelHeight(ax)
  215. height = 0;
  216. textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
  217. if isempty(textLabels)
  218. return;
  219. end
  220. oldUnits = get( textLabels(1), 'Units' );
  221. set( textLabels, 'Units', 'Normalized' );
  222. for ll=1:numel(vals)
  223. ext = get( textLabels(ll), 'Extent' );
  224. if ext(4) > height
  225. height = ext(4);
  226. end
  227. end
  228. set( textLabels, 'Units', oldUnits );
  229. end % getLabelExtent
  230. %-------------------------------------------------------------------------%
  231. function updateFont()
  232. % Update the rotated text fonts when the axes font changes
  233. properties = {
  234. 'FontName'
  235. 'FontSize'
  236. 'FontAngle'
  237. 'FontWeight'
  238. 'FontUnits'
  239. };
  240. propertyValues = get( ax, properties );
  241. textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
  242. set( textLabels, properties, propertyValues );
  243. end % updateFont
  244. function updateAlignment()
  245. textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
  246. angle = get( textLabels(1), 'Rotation' );
  247. % Depending on the angle, we may need to change the alignment. We change
  248. % alignments within 5 degrees of each 90 degree orientation.
  249. if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
  250. if 0 <= angle && angle < 5
  251. set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
  252. elseif 5 <= angle && angle < 85
  253. set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' );
  254. elseif 85 <= angle && angle < 95
  255. set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' );
  256. elseif 95 <= angle && angle < 175
  257. set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' );
  258. elseif 175 <= angle && angle < 185
  259. set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
  260. elseif 185 <= angle && angle < 265
  261. set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' );
  262. elseif 265 <= angle && angle < 275
  263. set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' );
  264. elseif 275 <= angle && angle < 355
  265. set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' );
  266. else % 355-360
  267. set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
  268. end
  269. else
  270. if 0 <= angle && angle < 5
  271. set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
  272. elseif 5 <= angle && angle < 85
  273. set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' );
  274. elseif 85 <= angle && angle < 95
  275. set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' );
  276. elseif 95 <= angle && angle < 175
  277. set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' );
  278. elseif 175 <= angle && angle < 185
  279. set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
  280. elseif 185 <= angle && angle < 265
  281. set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' );
  282. elseif 265 <= angle && angle < 275
  283. set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' );
  284. elseif 275 <= angle && angle < 355
  285. set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' );
  286. else % 355-360
  287. set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
  288. end
  289. end
  290. end
  291. %-------------------------------------------------------------------------%
  292. function onAxesFontChanged( ~, ~ )
  293. updateFont();
  294. repositionAxes( ax );
  295. repositionXLabel( ax );
  296. end % onAxesFontChanged
  297. %-------------------------------------------------------------------------%
  298. function onAxesPositionChanged( ~, ~ )
  299. % We need to accept the new position, so remove the appdata before
  300. % redrawing
  301. if isappdata( ax, 'OriginalAxesPosition' )
  302. rmappdata( ax, 'OriginalAxesPosition' );
  303. end
  304. if isappdata( ax, 'OriginalXLabelPosition' )
  305. rmappdata( ax, 'OriginalXLabelPosition' );
  306. end
  307. repositionAxes( ax );
  308. repositionXLabel( ax );
  309. end % onAxesPositionChanged
  310. %-------------------------------------------------------------------------%
  311. function onXAxisLocationChanged( ~, ~ )
  312. updateAlignment();
  313. repositionAxes( ax );
  314. repositionXLabel( ax );
  315. end % onXAxisLocationChanged
  316. %-------------------------------------------------------------------------%
  317. function onAxesLimitsChanged( ~, ~ )
  318. % The limits have moved, so make sure the labels are still ok
  319. textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
  320. xlim = get( ax, 'XLim' );
  321. pos = [0 getYPositionToUse( ax )];
  322. for tt=1:numel( textLabels )
  323. xval = get( textLabels(tt), 'UserData' );
  324. pos(1) = xval;
  325. % If the tick is off the edge, make it invisible
  326. if xval<xlim(1) || xval>xlim(2)
  327. set( textLabels(tt), 'Visible', 'off', 'Position', pos )
  328. elseif ~strcmpi( get( textLabels(tt), 'Visible' ), 'on' )
  329. set( textLabels(tt), 'Visible', 'on', 'Position', pos )
  330. else
  331. % Just set the position
  332. set( textLabels(tt), 'Position', pos );
  333. end
  334. end
  335. repositionXLabel( ax );
  336. end % onAxesPositionChanged
  337. %-------------------------------------------------------------------------%
  338. function onTextLabelDeleted( ~, ~ )
  339. % The final text label has been deleted. This is likely from a
  340. % "cla" or "close" call, so we should remove all of our dirty
  341. % hacks.
  342. restoreDefaultLabels(ax);
  343. end
  344. %-------------------------------------------------------------------------%
  345. function addListeners( ax )
  346. % Create listeners. We store the array of listeners in the axes to make
  347. % sure that they have the same life-span as the axes they are listening to.
  348. axh = handle( ax );
  349. listeners = [
  350. handle.listener( axh, findprop( axh, 'FontName' ), 'PropertyPostSet', @onAxesFontChanged )
  351. handle.listener( axh, findprop( axh, 'FontSize' ), 'PropertyPostSet', @onAxesFontChanged )
  352. handle.listener( axh, findprop( axh, 'FontWeight' ), 'PropertyPostSet', @onAxesFontChanged )
  353. handle.listener( axh, findprop( axh, 'FontAngle' ), 'PropertyPostSet', @onAxesFontChanged )
  354. handle.listener( axh, findprop( axh, 'FontUnits' ), 'PropertyPostSet', @onAxesFontChanged )
  355. handle.listener( axh, findprop( axh, 'OuterPosition' ), 'PropertyPostSet', @onAxesPositionChanged )
  356. handle.listener( axh, findprop( axh, 'XLim' ), 'PropertyPostSet', @onAxesLimitsChanged )
  357. handle.listener( axh, findprop( axh, 'YLim' ), 'PropertyPostSet', @onAxesLimitsChanged )
  358. handle.listener( axh, findprop( axh, 'XAxisLocation' ), 'PropertyPostSet', @onXAxisLocationChanged )
  359. ];
  360. setappdata( ax, 'RotateXLabelsListeners', listeners );
  361. end % addListeners
  362. %-------------------------------------------------------------------------%
  363. function removeListeners( ax )
  364. % Rempove any property listeners whilst we are fiddling with the axes
  365. if isappdata( ax, 'RotateXLabelsListeners' )
  366. delete( getappdata( ax, 'RotateXLabelsListeners' ) );
  367. rmappdata( ax, 'RotateXLabelsListeners' );
  368. end
  369. end % removeListeners
  370. %-------------------------------------------------------------------------%
  371. function y = getYPositionToUse( ax )
  372. % Use the direction and XAxisLocation properties to work out which
  373. % Y-value to draw the labels at.
  374. whichYLim = 1;
  375. % If YDir is reversed, switch where we position
  376. if strcmpi( get( ax, 'YDir' ), 'Reverse' )
  377. whichYLim = 3-whichYLim;
  378. end
  379. % If set to "top", then switch (again?)
  380. if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
  381. whichYLim = 3-whichYLim;
  382. end
  383. % Now get the value
  384. ylim = get( ax, 'YLim' );
  385. y = ylim(whichYLim);
  386. end % getYPositionToUse
  387. end % EOF
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注