Skip to content

BUG: Fix for xlabel/ylabel in barh plot #45145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jan 16, 2022
Merged
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Period

Plotting
^^^^^^^^
-
- Bug in :meth:`DataFrame.plot.barh` that prevented labeling the x-axis and ``xlabel`` updating the y-axis label (:issue:`45144`)
-

Groupby/resample/rolling
Expand Down
18 changes: 13 additions & 5 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,10 @@ def _plot(cls, ax: Axes, x, y, style=None, is_errorbar: bool = False, **kwds):
args = (x, y, style) if style is not None else (x, y)
return ax.plot(*args, **kwds)

def _get_custom_index_name(self):
"""Specify whether xlabel/ylabel should be used to override index name"""
return self.xlabel

def _get_index_name(self) -> str | None:
if isinstance(self.data.index, ABCMultiIndex):
name = self.data.index.names
Expand All @@ -766,9 +770,10 @@ def _get_index_name(self) -> str | None:
if name is not None:
name = pprint_thing(name)

# GH 9093, override the default xlabel if xlabel is provided.
if self.xlabel is not None:
name = pprint_thing(self.xlabel)
# GH 45145, override the default axis label if one is provided.
index_name = self._get_custom_index_name()
if index_name is not None:
name = pprint_thing(index_name)

return name

Expand Down Expand Up @@ -1572,12 +1577,11 @@ def _post_plot_logic(self, ax: Axes, data):
str_index = [pprint_thing(key) for key in data.index]
else:
str_index = [pprint_thing(key) for key in range(data.shape[0])]
name = self._get_index_name()

s_edge = self.ax_pos[0] - 0.25 + self.lim_offset
e_edge = self.ax_pos[-1] + 0.25 + self.bar_width + self.lim_offset

self._decorate_ticks(ax, name, str_index, s_edge, e_edge)
self._decorate_ticks(ax, self._get_index_name(), str_index, s_edge, e_edge)

def _decorate_ticks(self, ax: Axes, name, ticklabels, start_edge, end_edge):
ax.set_xlim((start_edge, end_edge))
Expand Down Expand Up @@ -1608,13 +1612,17 @@ def _plot( # type: ignore[override]
):
return ax.barh(x, y, w, left=start, log=log, **kwds)

def _get_custom_index_name(self):
return self.ylabel

def _decorate_ticks(self, ax: Axes, name, ticklabels, start_edge, end_edge):
# horizontal bars
ax.set_ylim((start_edge, end_edge))
ax.set_yticks(self.tick_pos)
ax.set_yticklabels(ticklabels)
if name is not None and self.use_index:
ax.set_ylabel(name)
ax.set_xlabel(self.xlabel)


class PiePlot(MPLPlot):
Expand Down
12 changes: 8 additions & 4 deletions pandas/tests/plotting/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,16 +787,20 @@ def test_style_single_ok(self):
"index_name, old_label, new_label",
[(None, "", "new"), ("old", "old", "new"), (None, "", "")],
)
@pytest.mark.parametrize("kind", ["line", "area", "bar"])
@pytest.mark.parametrize("kind", ["line", "area", "bar", "barh"])
def test_xlabel_ylabel_series(self, kind, index_name, old_label, new_label):
# GH 9093
ser = Series([1, 2, 3, 4])
ser.index.name = index_name

# default is the ylabel is not shown and xlabel is index name
# default is the ylabel is not shown and xlabel is index name (reverse for barh)
ax = ser.plot(kind=kind)
assert ax.get_ylabel() == ""
assert ax.get_xlabel() == old_label
if kind == "barh":
assert ax.get_xlabel() == ""
assert ax.get_ylabel() == old_label
else:
assert ax.get_ylabel() == ""
assert ax.get_xlabel() == old_label

# old xlabel will be overridden and assigned ylabel will be used as ylabel
ax = ser.plot(kind=kind, ylabel=new_label, xlabel=new_label)
Expand Down