Skip to content

Commit e04e0ec

Browse files
committed
Merge pull request #7844 from sinhrks/minor_rot
BUG/VIS: rot and fontsize are not applied to timeseries plots
2 parents 0d05226 + 7d5a2cd commit e04e0ec

File tree

3 files changed

+91
-36
lines changed

3 files changed

+91
-36
lines changed

doc/source/v0.15.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ Bug Fixes
308308
(except for the case of two DataFrames with ``pairwise=False``, where behavior is unchanged) (:issue:`7542`)
309309

310310

311-
311+
- Bug in ``DataFrame.plot`` and ``Series.plot`` may ignore ``rot`` and ``fontsize`` keywords (:issue:`7844`)
312312

313313

314314
- Bug in ``DatetimeIndex.value_counts`` doesn't preserve tz (:issue:`7735`)

pandas/tests/test_graphics.py

+41-12
Original file line numberDiff line numberDiff line change
@@ -240,21 +240,33 @@ def _check_ticks_props(self, axes, xlabelsize=None, xrot=None,
240240
yrot : number
241241
expected yticks rotation
242242
"""
243+
from matplotlib.ticker import NullFormatter
243244
axes = self._flatten_visible(axes)
244245
for ax in axes:
245246
if xlabelsize or xrot:
246-
xtick = ax.get_xticklabels()[0]
247-
if xlabelsize is not None:
248-
self.assertAlmostEqual(xtick.get_fontsize(), xlabelsize)
249-
if xrot is not None:
250-
self.assertAlmostEqual(xtick.get_rotation(), xrot)
247+
if isinstance(ax.xaxis.get_minor_formatter(), NullFormatter):
248+
# If minor ticks has NullFormatter, rot / fontsize are not retained
249+
labels = ax.get_xticklabels()
250+
else:
251+
labels = ax.get_xticklabels() + ax.get_xticklabels(minor=True)
252+
253+
for label in labels:
254+
if xlabelsize is not None:
255+
self.assertAlmostEqual(label.get_fontsize(), xlabelsize)
256+
if xrot is not None:
257+
self.assertAlmostEqual(label.get_rotation(), xrot)
251258

252259
if ylabelsize or yrot:
253-
ytick = ax.get_yticklabels()[0]
254-
if ylabelsize is not None:
255-
self.assertAlmostEqual(ytick.get_fontsize(), ylabelsize)
256-
if yrot is not None:
257-
self.assertAlmostEqual(ytick.get_rotation(), yrot)
260+
if isinstance(ax.yaxis.get_minor_formatter(), NullFormatter):
261+
labels = ax.get_yticklabels()
262+
else:
263+
labels = ax.get_yticklabels() + ax.get_yticklabels(minor=True)
264+
265+
for label in labels:
266+
if ylabelsize is not None:
267+
self.assertAlmostEqual(label.get_fontsize(), ylabelsize)
268+
if yrot is not None:
269+
self.assertAlmostEqual(label.get_rotation(), yrot)
258270

259271
def _check_ax_scales(self, axes, xaxis='linear', yaxis='linear'):
260272
"""
@@ -872,6 +884,7 @@ def test_plot(self):
872884
self._check_visible(ax.xaxis)
873885
self._check_visible(ax.get_xticklabels())
874886
self._check_visible([ax.xaxis.get_label()])
887+
self._check_ticks_props(ax, xrot=30)
875888

876889
_check_plot_works(df.plot, title='blah')
877890

@@ -1069,14 +1082,16 @@ def test_subplots_timeseries(self):
10691082
self._check_visible(axes[-1].get_xticklabels(minor=True))
10701083
self._check_visible(axes[-1].xaxis.get_label())
10711084
self._check_visible(axes[-1].get_yticklabels())
1085+
self._check_ticks_props(axes, xrot=30)
10721086

1073-
axes = df.plot(kind=kind, subplots=True, sharex=False)
1087+
axes = df.plot(kind=kind, subplots=True, sharex=False, rot=45, fontsize=7)
10741088
for ax in axes:
10751089
self._check_visible(ax.xaxis)
10761090
self._check_visible(ax.get_xticklabels())
10771091
self._check_visible(ax.get_xticklabels(minor=True))
10781092
self._check_visible(ax.xaxis.get_label())
10791093
self._check_visible(ax.get_yticklabels())
1094+
self._check_ticks_props(ax, xlabelsize=7, xrot=45)
10801095

10811096
def test_negative_log(self):
10821097
df = - DataFrame(rand(6, 4),
@@ -1363,7 +1378,17 @@ def test_plot_bar(self):
13631378
_check_plot_works(df.plot, kind='bar')
13641379

13651380
df = DataFrame({'a': [0, 1], 'b': [1, 0]})
1366-
_check_plot_works(df.plot, kind='bar')
1381+
ax = _check_plot_works(df.plot, kind='bar')
1382+
self._check_ticks_props(ax, xrot=90)
1383+
1384+
ax = df.plot(kind='bar', rot=35, fontsize=10)
1385+
self._check_ticks_props(ax, xrot=35, xlabelsize=10)
1386+
1387+
ax = _check_plot_works(df.plot, kind='barh')
1388+
self._check_ticks_props(ax, yrot=0)
1389+
1390+
ax = df.plot(kind='barh', rot=55, fontsize=11)
1391+
self._check_ticks_props(ax, yrot=55, ylabelsize=11)
13671392

13681393
def _check_bar_alignment(self, df, kind='bar', stacked=False,
13691394
subplots=False, align='center',
@@ -1591,6 +1616,10 @@ def test_kde(self):
15911616
ax = _check_plot_works(df.plot, kind='kde')
15921617
expected = [com.pprint_thing(c) for c in df.columns]
15931618
self._check_legend_labels(ax, labels=expected)
1619+
self._check_ticks_props(ax, xrot=0)
1620+
1621+
ax = df.plot(kind='kde', rot=20, fontsize=5)
1622+
self._check_ticks_props(ax, xrot=20, xlabelsize=5)
15941623

15951624
axes = _check_plot_works(df.plot, kind='kde', subplots=True)
15961625
self._check_axes_shape(axes, axes_num=4, layout=(4, 1))

pandas/tools/plotting.py

+49-23
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,7 @@ class MPLPlot(object):
753753
754754
"""
755755
_default_rot = 0
756+
orientation = None
756757

757758
_pop_attributes = ['label', 'style', 'logy', 'logx', 'loglog',
758759
'mark_right', 'stacked']
@@ -788,7 +789,14 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=True,
788789
self.use_index = use_index
789790

790791
self.fontsize = fontsize
791-
self.rot = rot
792+
793+
if rot is not None:
794+
self.rot = rot
795+
else:
796+
if isinstance(self._default_rot, dict):
797+
self.rot = self._default_rot[self.kind]
798+
else:
799+
self.rot = self._default_rot
792800

793801
if grid is None:
794802
grid = False if secondary_y else True
@@ -1018,14 +1026,30 @@ def _adorn_subplots(self):
10181026
else:
10191027
self.axes[0].set_title(self.title)
10201028

1021-
if self._need_to_set_index:
1022-
labels = [com.pprint_thing(key) for key in self.data.index]
1023-
labels = dict(zip(range(len(self.data.index)), labels))
1029+
labels = [com.pprint_thing(key) for key in self.data.index]
1030+
labels = dict(zip(range(len(self.data.index)), labels))
10241031

1025-
for ax_ in self.axes:
1026-
# ax_.set_xticks(self.xticks)
1027-
xticklabels = [labels.get(x, '') for x in ax_.get_xticks()]
1028-
ax_.set_xticklabels(xticklabels, rotation=self.rot)
1032+
for ax in self.axes:
1033+
if self.orientation == 'vertical' or self.orientation is None:
1034+
if self._need_to_set_index:
1035+
xticklabels = [labels.get(x, '') for x in ax.get_xticks()]
1036+
ax.set_xticklabels(xticklabels)
1037+
self._apply_axis_properties(ax.xaxis, rot=self.rot,
1038+
fontsize=self.fontsize)
1039+
elif self.orientation == 'horizontal':
1040+
if self._need_to_set_index:
1041+
yticklabels = [labels.get(y, '') for y in ax.get_yticks()]
1042+
ax.set_yticklabels(yticklabels)
1043+
self._apply_axis_properties(ax.yaxis, rot=self.rot,
1044+
fontsize=self.fontsize)
1045+
1046+
def _apply_axis_properties(self, axis, rot=None, fontsize=None):
1047+
labels = axis.get_majorticklabels() + axis.get_minorticklabels()
1048+
for label in labels:
1049+
if rot is not None:
1050+
label.set_rotation(rot)
1051+
if fontsize is not None:
1052+
label.set_fontsize(fontsize)
10291053

10301054
@property
10311055
def legend_title(self):
@@ -1336,6 +1360,8 @@ def _get_errorbars(self, label=None, index=None, xerr=True, yerr=True):
13361360

13371361

13381362
class KdePlot(MPLPlot):
1363+
orientation = 'vertical'
1364+
13391365
def __init__(self, data, bw_method=None, ind=None, **kwargs):
13401366
MPLPlot.__init__(self, data, **kwargs)
13411367
self.bw_method=bw_method
@@ -1480,6 +1506,9 @@ def _post_plot_logic(self):
14801506

14811507
class LinePlot(MPLPlot):
14821508

1509+
_default_rot = 30
1510+
orientation = 'vertical'
1511+
14831512
def __init__(self, data, **kwargs):
14841513
MPLPlot.__init__(self, data, **kwargs)
14851514
if self.stacked:
@@ -1656,16 +1685,9 @@ def _post_plot_logic(self):
16561685

16571686
index_name = self._get_index_name()
16581687

1659-
rot = 30
1660-
if self.rot is not None:
1661-
rot = self.rot
1662-
16631688
for ax in self.axes:
16641689
if condition:
1665-
format_date_labels(ax, rot=rot)
1666-
elif self.rot is not None:
1667-
for l in ax.get_xticklabels():
1668-
l.set_rotation(self.rot)
1690+
format_date_labels(ax, rot=self.rot)
16691691

16701692
if index_name is not None:
16711693
ax.set_xlabel(index_name)
@@ -1766,9 +1788,6 @@ def __init__(self, data, **kwargs):
17661788
self.ax_pos = self.tick_pos - self.tickoffset
17671789

17681790
def _args_adjust(self):
1769-
if self.rot is None:
1770-
self.rot = self._default_rot[self.kind]
1771-
17721791
if com.is_list_like(self.bottom):
17731792
self.bottom = np.array(self.bottom)
17741793
if com.is_list_like(self.left):
@@ -1858,8 +1877,7 @@ def _post_plot_logic(self):
18581877
if self.kind == 'bar':
18591878
ax.set_xlim((s_edge, e_edge))
18601879
ax.set_xticks(self.tick_pos)
1861-
ax.set_xticklabels(str_index, rotation=self.rot,
1862-
fontsize=self.fontsize)
1880+
ax.set_xticklabels(str_index)
18631881
if not self.log: # GH3254+
18641882
ax.axhline(0, color='k', linestyle='--')
18651883
if name is not None:
@@ -1868,14 +1886,22 @@ def _post_plot_logic(self):
18681886
# horizontal bars
18691887
ax.set_ylim((s_edge, e_edge))
18701888
ax.set_yticks(self.tick_pos)
1871-
ax.set_yticklabels(str_index, rotation=self.rot,
1872-
fontsize=self.fontsize)
1889+
ax.set_yticklabels(str_index)
18731890
ax.axvline(0, color='k', linestyle='--')
18741891
if name is not None:
18751892
ax.set_ylabel(name)
18761893
else:
18771894
raise NotImplementedError(self.kind)
18781895

1896+
@property
1897+
def orientation(self):
1898+
if self.kind == 'bar':
1899+
return 'vertical'
1900+
elif self.kind == 'barh':
1901+
return 'horizontal'
1902+
else:
1903+
raise NotImplementedError(self.kind)
1904+
18791905

18801906
class PiePlot(MPLPlot):
18811907

0 commit comments

Comments
 (0)