Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,7 @@ Period
Plotting
^^^^^^^^
- When given non-numeric data, :meth:`DataFrame.boxplot` now raises a ``ValueError`` rather than a cryptic ``KeyError`` or ``ZeroDivisionError``, in line with other plotting functions like :meth:`DataFrame.hist`. (:issue:`43480`)
- 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
14 changes: 11 additions & 3 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,8 +767,10 @@ def _get_index_name(self) -> str | None:
name = pprint_thing(name)

# GH 9093, override the default xlabel if xlabel is provided.
if self.xlabel is not None:
if self.xlabel is not None and self.orientation == "vertical":
name = pprint_thing(self.xlabel)
if self.ylabel is not None and self.orientation == "horizontal":
name = pprint_thing(self.ylabel)

return name

Expand Down Expand Up @@ -1567,17 +1569,19 @@ def _make_plot(self):
)
self._append_legend_handles_labels(rect, label)

def _get_bar_index_name(self):
return self.xlabel or self._get_index_name()

def _post_plot_logic(self, ax: Axes, data):
if self.use_index:
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_bar_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_bar_index_name(self):
return self.ylabel or self._get_index_name()

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