Skip to content

Commit ec26536

Browse files
committed
BUG: single column bar plot is misaligned
1 parent ac755b5 commit ec26536

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
@@ -256,5 +256,9 @@ Bug Fixes
256256
- Bug in ``DataFrame.reset_index`` loses ``tz`` (:issue:`3950`)
257257

258258

259+
- Bug in single column bar plot is misaligned (:issue:`7498`).
260+
261+
262+
259263
- Bug in non-monotonic ``Index.union`` may preserve ``name`` incorrectly (:issue:`7458`)
260264
- Bug in ``DatetimeIndex.intersection`` doesn't preserve timezone (:issue:`4690`)

pandas/tests/test_graphics.py

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

1258-
tick_pos = np.arange(len(df))
1259-
12601258
axes = self._flatten_visible(axes)
12611259

12621260
for ax in axes:
12631261
if kind == 'bar':
12641262
axis = ax.xaxis
12651263
ax_min, ax_max = ax.get_xlim()
1264+
min_edge = min([p.get_x() for p in ax.patches])
1265+
max_edge = max([p.get_x() + p.get_width() for p in ax.patches])
12661266
elif kind == 'barh':
12671267
axis = ax.yaxis
12681268
ax_min, ax_max = ax.get_ylim()
1269+
min_edge = min([p.get_y() for p in ax.patches])
1270+
max_edge = max([p.get_y() + p.get_height() for p in ax.patches])
12691271
else:
12701272
raise ValueError
12711273

1274+
# GH 7498
1275+
# compare margins between lim and bar edges
1276+
self.assertAlmostEqual(ax_min, min_edge - 0.25)
1277+
self.assertAlmostEqual(ax_max, max_edge + 0.25)
1278+
12721279
p = ax.patches[0]
12731280
if kind == 'bar' and (stacked is True or subplots is True):
12741281
edge = p.get_x()
12751282
center = edge + p.get_width() * position
1276-
tickoffset = width * position
12771283
elif kind == 'bar' and stacked is False:
12781284
center = p.get_x() + p.get_width() * len(df.columns) * position
12791285
edge = p.get_x()
1280-
if align == 'edge':
1281-
tickoffset = width * (position - 0.5) + p.get_width() * 1.5
1282-
else:
1283-
tickoffset = width * position + p.get_width()
12841286
elif kind == 'barh' and (stacked is True or subplots is True):
12851287
center = p.get_y() + p.get_height() * position
12861288
edge = p.get_y()
1287-
tickoffset = width * position
12881289
elif kind == 'barh' and stacked is False:
12891290
center = p.get_y() + p.get_height() * len(df.columns) * position
12901291
edge = p.get_y()
1291-
if align == 'edge':
1292-
tickoffset = width * (position - 0.5) + p.get_height() * 1.5
1293-
else:
1294-
tickoffset = width * position + p.get_height()
12951292
else:
12961293
raise ValueError
12971294

@@ -1307,59 +1304,43 @@ def _check_bar_alignment(self, df, kind='bar', stacked=False,
13071304
else:
13081305
raise ValueError
13091306

1310-
# Check starting point and axes limit margin
1311-
self.assertEqual(ax_min, tick_pos[0] - tickoffset - 0.25)
1312-
self.assertEqual(ax_max, tick_pos[-1] - tickoffset + 1)
1313-
# Check tick locations and axes limit margin
1314-
t_min = axis.get_ticklocs()[0] - tickoffset
1315-
t_max = axis.get_ticklocs()[-1] - tickoffset
1316-
self.assertAlmostEqual(ax_min, t_min - 0.25)
1317-
self.assertAlmostEqual(ax_max, t_max + 1.0)
13181307
return axes
13191308

13201309
@slow
13211310
def test_bar_stacked_center(self):
13221311
# GH2157
13231312
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
1324-
axes = self._check_bar_alignment(df, kind='bar', stacked=True)
1325-
# Check the axes has the same drawing range before fixing # GH4525
1326-
self.assertEqual(axes[0].get_xlim(), (-0.5, 4.75))
1327-
1313+
self._check_bar_alignment(df, kind='bar', stacked=True)
13281314
self._check_bar_alignment(df, kind='bar', stacked=True, width=0.9)
1329-
1330-
axes = self._check_bar_alignment(df, kind='barh', stacked=True)
1331-
self.assertEqual(axes[0].get_ylim(), (-0.5, 4.75))
1332-
1315+
self._check_bar_alignment(df, kind='barh', stacked=True)
13331316
self._check_bar_alignment(df, kind='barh', stacked=True, width=0.9)
13341317

13351318
@slow
13361319
def test_bar_center(self):
13371320
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
1338-
axes = self._check_bar_alignment(df, kind='bar', stacked=False)
1339-
self.assertEqual(axes[0].get_xlim(), (-0.75, 4.5))
1340-
1321+
self._check_bar_alignment(df, kind='bar', stacked=False)
13411322
self._check_bar_alignment(df, kind='bar', stacked=False, width=0.9)
1342-
1343-
axes = self._check_bar_alignment(df, kind='barh', stacked=False)
1344-
self.assertEqual(axes[0].get_ylim(), (-0.75, 4.5))
1345-
1323+
self._check_bar_alignment(df, kind='barh', stacked=False)
13461324
self._check_bar_alignment(df, kind='barh', stacked=False, width=0.9)
13471325

13481326
@slow
13491327
def test_bar_subplots_center(self):
13501328
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
1351-
axes = self._check_bar_alignment(df, kind='bar', subplots=True)
1352-
for ax in axes:
1353-
self.assertEqual(ax.get_xlim(), (-0.5, 4.75))
1354-
1329+
self._check_bar_alignment(df, kind='bar', subplots=True)
13551330
self._check_bar_alignment(df, kind='bar', subplots=True, width=0.9)
1356-
1357-
axes = self._check_bar_alignment(df, kind='barh', subplots=True)
1358-
for ax in axes:
1359-
self.assertEqual(ax.get_ylim(), (-0.5, 4.75))
1360-
1331+
self._check_bar_alignment(df, kind='barh', subplots=True)
13611332
self._check_bar_alignment(df, kind='barh', subplots=True, width=0.9)
13621333

1334+
@slow
1335+
def test_bar_align_single_column(self):
1336+
df = DataFrame(randn(5))
1337+
self._check_bar_alignment(df, kind='bar', stacked=False)
1338+
self._check_bar_alignment(df, kind='bar', stacked=True)
1339+
self._check_bar_alignment(df, kind='barh', stacked=False)
1340+
self._check_bar_alignment(df, kind='barh', stacked=True)
1341+
self._check_bar_alignment(df, kind='bar', subplots=True)
1342+
self._check_bar_alignment(df, kind='barh', subplots=True)
1343+
13631344
@slow
13641345
def test_bar_edge(self):
13651346
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)