Skip to content

Commit 715945c

Browse files
committed
Merge pull request #7532 from sinhrks/baralign
BUG: single column bar plot is misaligned
2 parents a82dd1d + ec26536 commit 715945c

File tree

3 files changed

+50
-59
lines changed

3 files changed

+50
-59
lines changed

doc/source/v0.14.1.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ Bug Fixes
263263
- Bug in ``DatetimeIndex.freqstr`` raises ``AttributeError`` when ``freq`` is ``None`` (:issue:`7606`)
264264
- Bug in ``GroupBy.size`` created by ``TimeGrouper`` raises ``AttributeError`` (:issue:`7453`)
265265

266+
- Bug in single column bar plot is misaligned (:issue:`7498`).
267+
268+
269+
266270
- Bug in non-monotonic ``Index.union`` may preserve ``name`` incorrectly (:issue:`7458`)
267271
- Bug in ``DatetimeIndex.intersection`` doesn't preserve timezone (:issue:`4690`)
268272

pandas/tests/test_graphics.py

Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,43 +1261,40 @@ def _check_bar_alignment(self, df, kind='bar', stacked=False,
12611261
align=align, width=width, position=position,
12621262
grid=True)
12631263

1264-
tick_pos = np.arange(len(df))
1265-
12661264
axes = self._flatten_visible(axes)
12671265

12681266
for ax in axes:
12691267
if kind == 'bar':
12701268
axis = ax.xaxis
12711269
ax_min, ax_max = ax.get_xlim()
1270+
min_edge = min([p.get_x() for p in ax.patches])
1271+
max_edge = max([p.get_x() + p.get_width() for p in ax.patches])
12721272
elif kind == 'barh':
12731273
axis = ax.yaxis
12741274
ax_min, ax_max = ax.get_ylim()
1275+
min_edge = min([p.get_y() for p in ax.patches])
1276+
max_edge = max([p.get_y() + p.get_height() for p in ax.patches])
12751277
else:
12761278
raise ValueError
12771279

1280+
# GH 7498
1281+
# compare margins between lim and bar edges
1282+
self.assertAlmostEqual(ax_min, min_edge - 0.25)
1283+
self.assertAlmostEqual(ax_max, max_edge + 0.25)
1284+
12781285
p = ax.patches[0]
12791286
if kind == 'bar' and (stacked is True or subplots is True):
12801287
edge = p.get_x()
12811288
center = edge + p.get_width() * position
1282-
tickoffset = width * position
12831289
elif kind == 'bar' and stacked is False:
12841290
center = p.get_x() + p.get_width() * len(df.columns) * position
12851291
edge = p.get_x()
1286-
if align == 'edge':
1287-
tickoffset = width * (position - 0.5) + p.get_width() * 1.5
1288-
else:
1289-
tickoffset = width * position + p.get_width()
12901292
elif kind == 'barh' and (stacked is True or subplots is True):
12911293
center = p.get_y() + p.get_height() * position
12921294
edge = p.get_y()
1293-
tickoffset = width * position
12941295
elif kind == 'barh' and stacked is False:
12951296
center = p.get_y() + p.get_height() * len(df.columns) * position
12961297
edge = p.get_y()
1297-
if align == 'edge':
1298-
tickoffset = width * (position - 0.5) + p.get_height() * 1.5
1299-
else:
1300-
tickoffset = width * position + p.get_height()
13011298
else:
13021299
raise ValueError
13031300

@@ -1313,59 +1310,43 @@ def _check_bar_alignment(self, df, kind='bar', stacked=False,
13131310
else:
13141311
raise ValueError
13151312

1316-
# Check starting point and axes limit margin
1317-
self.assertEqual(ax_min, tick_pos[0] - tickoffset - 0.25)
1318-
self.assertEqual(ax_max, tick_pos[-1] - tickoffset + 1)
1319-
# Check tick locations and axes limit margin
1320-
t_min = axis.get_ticklocs()[0] - tickoffset
1321-
t_max = axis.get_ticklocs()[-1] - tickoffset
1322-
self.assertAlmostEqual(ax_min, t_min - 0.25)
1323-
self.assertAlmostEqual(ax_max, t_max + 1.0)
13241313
return axes
13251314

13261315
@slow
13271316
def test_bar_stacked_center(self):
13281317
# GH2157
13291318
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
1330-
axes = self._check_bar_alignment(df, kind='bar', stacked=True)
1331-
# Check the axes has the same drawing range before fixing # GH4525
1332-
self.assertEqual(axes[0].get_xlim(), (-0.5, 4.75))
1333-
1319+
self._check_bar_alignment(df, kind='bar', stacked=True)
13341320
self._check_bar_alignment(df, kind='bar', stacked=True, width=0.9)
1335-
1336-
axes = self._check_bar_alignment(df, kind='barh', stacked=True)
1337-
self.assertEqual(axes[0].get_ylim(), (-0.5, 4.75))
1338-
1321+
self._check_bar_alignment(df, kind='barh', stacked=True)
13391322
self._check_bar_alignment(df, kind='barh', stacked=True, width=0.9)
13401323

13411324
@slow
13421325
def test_bar_center(self):
13431326
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
1344-
axes = self._check_bar_alignment(df, kind='bar', stacked=False)
1345-
self.assertEqual(axes[0].get_xlim(), (-0.75, 4.5))
1346-
1327+
self._check_bar_alignment(df, kind='bar', stacked=False)
13471328
self._check_bar_alignment(df, kind='bar', stacked=False, width=0.9)
1348-
1349-
axes = self._check_bar_alignment(df, kind='barh', stacked=False)
1350-
self.assertEqual(axes[0].get_ylim(), (-0.75, 4.5))
1351-
1329+
self._check_bar_alignment(df, kind='barh', stacked=False)
13521330
self._check_bar_alignment(df, kind='barh', stacked=False, width=0.9)
13531331

13541332
@slow
13551333
def test_bar_subplots_center(self):
13561334
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
1357-
axes = self._check_bar_alignment(df, kind='bar', subplots=True)
1358-
for ax in axes:
1359-
self.assertEqual(ax.get_xlim(), (-0.5, 4.75))
1360-
1335+
self._check_bar_alignment(df, kind='bar', subplots=True)
13611336
self._check_bar_alignment(df, kind='bar', subplots=True, width=0.9)
1362-
1363-
axes = self._check_bar_alignment(df, kind='barh', subplots=True)
1364-
for ax in axes:
1365-
self.assertEqual(ax.get_ylim(), (-0.5, 4.75))
1366-
1337+
self._check_bar_alignment(df, kind='barh', subplots=True)
13671338
self._check_bar_alignment(df, kind='barh', subplots=True, width=0.9)
13681339

1340+
@slow
1341+
def test_bar_align_single_column(self):
1342+
df = DataFrame(randn(5))
1343+
self._check_bar_alignment(df, kind='bar', stacked=False)
1344+
self._check_bar_alignment(df, kind='bar', stacked=True)
1345+
self._check_bar_alignment(df, kind='barh', stacked=False)
1346+
self._check_bar_alignment(df, kind='barh', stacked=True)
1347+
self._check_bar_alignment(df, kind='bar', subplots=True)
1348+
self._check_bar_alignment(df, kind='barh', subplots=True)
1349+
13691350
@slow
13701351
def test_bar_edge(self):
13711352
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))

pandas/tools/plotting.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,9 +1784,10 @@ def __init__(self, data, **kwargs):
17841784
self.stacked = kwargs.pop('stacked', False)
17851785

17861786
self.bar_width = kwargs.pop('width', 0.5)
1787+
17871788
pos = kwargs.pop('position', 0.5)
17881789

1789-
kwargs['align'] = kwargs.pop('align', 'center')
1790+
kwargs.setdefault('align', 'center')
17901791
self.tick_pos = np.arange(len(data))
17911792

17921793
self.bottom = kwargs.pop('bottom', None)
@@ -1797,14 +1798,19 @@ def __init__(self, data, **kwargs):
17971798

17981799
if self.stacked or self.subplots:
17991800
self.tickoffset = self.bar_width * pos
1800-
elif kwargs['align'] == 'edge':
1801-
K = self.nseries
1802-
w = self.bar_width / K
1803-
self.tickoffset = self.bar_width * (pos - 0.5) + w * 1.5
1801+
if kwargs['align'] == 'edge':
1802+
self.lim_offset = self.bar_width / 2
1803+
else:
1804+
self.lim_offset = 0
18041805
else:
1805-
K = self.nseries
1806-
w = self.bar_width / K
1807-
self.tickoffset = self.bar_width * pos + w
1806+
if kwargs['align'] == 'edge':
1807+
w = self.bar_width / self.nseries
1808+
self.tickoffset = self.bar_width * (pos - 0.5) + w * 0.5
1809+
self.lim_offset = w * 0.5
1810+
else:
1811+
self.tickoffset = self.bar_width * pos
1812+
self.lim_offset = 0
1813+
18081814
self.ax_pos = self.tick_pos - self.tickoffset
18091815

18101816
def _args_adjust(self):
@@ -1881,9 +1887,8 @@ def _make_plot(self):
18811887
neg_prior = neg_prior + np.where(mask, 0, y)
18821888
else:
18831889
w = self.bar_width / K
1884-
rect = bar_f(ax, self.ax_pos + (i + 1.5) * w, y, w,
1890+
rect = bar_f(ax, self.ax_pos + (i + 0.5) * w, y, w,
18851891
start=start, label=label, **kwds)
1886-
18871892
self._add_legend_handle(rect, label, index=i)
18881893

18891894
def _post_plot_logic(self):
@@ -1894,8 +1899,12 @@ def _post_plot_logic(self):
18941899
str_index = [com.pprint_thing(key) for key in
18951900
range(self.data.shape[0])]
18961901
name = self._get_index_name()
1902+
1903+
s_edge = self.ax_pos[0] - 0.25 + self.lim_offset
1904+
e_edge = self.ax_pos[-1] + 0.25 + self.bar_width + self.lim_offset
1905+
18971906
if self.kind == 'bar':
1898-
ax.set_xlim([self.ax_pos[0] - 0.25, self.ax_pos[-1] + 1])
1907+
ax.set_xlim((s_edge, e_edge))
18991908
ax.set_xticks(self.tick_pos)
19001909
ax.set_xticklabels(str_index, rotation=self.rot,
19011910
fontsize=self.fontsize)
@@ -1905,7 +1914,7 @@ def _post_plot_logic(self):
19051914
ax.set_xlabel(name)
19061915
elif self.kind == 'barh':
19071916
# horizontal bars
1908-
ax.set_ylim([self.ax_pos[0] - 0.25, self.ax_pos[-1] + 1])
1917+
ax.set_ylim((s_edge, e_edge))
19091918
ax.set_yticks(self.tick_pos)
19101919
ax.set_yticklabels(str_index, rotation=self.rot,
19111920
fontsize=self.fontsize)
@@ -1915,9 +1924,6 @@ def _post_plot_logic(self):
19151924
else:
19161925
raise NotImplementedError(self.kind)
19171926

1918-
# if self.subplots and self.legend:
1919-
# self.axes[0].legend(loc='best')
1920-
19211927

19221928
class PiePlot(MPLPlot):
19231929

0 commit comments

Comments
 (0)