Skip to content

Commit 66a4945

Browse files
authored
BUG df.plot.box handles matplotlib Axes with sharey=True (#54940)
* BUG manage sharey in plot.box with vert=False * fix * add entry in whats new * iter
1 parent 271144a commit 66a4945

File tree

3 files changed

+44
-19
lines changed

3 files changed

+44
-19
lines changed

Diff for: doc/source/whatsnew/v2.2.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ Period
288288

289289
Plotting
290290
^^^^^^^^
291-
-
291+
- Bug in :meth:`DataFrame.plot.box` with ``vert=False`` and a matplotlib ``Axes`` created with ``sharey=True`` (:issue:`54941`)
292292
-
293293

294294
Groupby/resample/rolling

Diff for: pandas/plotting/_matplotlib/boxplot.py

+27-18
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,24 @@
4040
from pandas._typing import MatplotlibColor
4141

4242

43+
def _set_ticklabels(ax: Axes, labels: list[str], is_vertical: bool, **kwargs) -> None:
44+
"""Set the tick labels of a given axis.
45+
46+
Due to https://github.com/matplotlib/matplotlib/pull/17266, we need to handle the
47+
case of repeated ticks (due to `FixedLocator`) and thus we duplicate the number of
48+
labels.
49+
"""
50+
ticks = ax.get_xticks() if is_vertical else ax.get_yticks()
51+
if len(ticks) != len(labels):
52+
i, remainder = divmod(len(ticks), len(labels))
53+
assert remainder == 0, remainder
54+
labels *= i
55+
if is_vertical:
56+
ax.set_xticklabels(labels, **kwargs)
57+
else:
58+
ax.set_yticklabels(labels, **kwargs)
59+
60+
4361
class BoxPlot(LinePlot):
4462
@property
4563
def _kind(self) -> Literal["box"]:
@@ -193,7 +211,9 @@ def _make_plot(self) -> None:
193211
)
194212
self.maybe_color_bp(bp)
195213
self._return_obj[label] = ret
196-
self._set_ticklabels(ax, ticklabels)
214+
_set_ticklabels(
215+
ax=ax, labels=ticklabels, is_vertical=self.orientation == "vertical"
216+
)
197217
else:
198218
y = self.data.values.T
199219
ax = self._get_ax(0)
@@ -209,13 +229,9 @@ def _make_plot(self) -> None:
209229
labels = [pprint_thing(left) for left in labels]
210230
if not self.use_index:
211231
labels = [pprint_thing(key) for key in range(len(labels))]
212-
self._set_ticklabels(ax, labels)
213-
214-
def _set_ticklabels(self, ax: Axes, labels: list[str]) -> None:
215-
if self.orientation == "vertical":
216-
ax.set_xticklabels(labels)
217-
else:
218-
ax.set_yticklabels(labels)
232+
_set_ticklabels(
233+
ax=ax, labels=labels, is_vertical=self.orientation == "vertical"
234+
)
219235

220236
def _make_legend(self) -> None:
221237
pass
@@ -382,16 +398,9 @@ def plot_group(keys, values, ax: Axes, **kwds):
382398
ax.tick_params(axis="both", labelsize=fontsize)
383399

384400
# GH 45465: x/y are flipped when "vert" changes
385-
is_vertical = kwds.get("vert", True)
386-
ticks = ax.get_xticks() if is_vertical else ax.get_yticks()
387-
if len(ticks) != len(keys):
388-
i, remainder = divmod(len(ticks), len(keys))
389-
assert remainder == 0, remainder
390-
keys *= i
391-
if is_vertical:
392-
ax.set_xticklabels(keys, rotation=rot)
393-
else:
394-
ax.set_yticklabels(keys, rotation=rot)
401+
_set_ticklabels(
402+
ax=ax, labels=keys, is_vertical=kwds.get("vert", True), rotation=rot
403+
)
395404
maybe_color_bp(bp, **kwds)
396405

397406
# Return axes in multiplot case, maybe revisit later # 985

Diff for: pandas/tests/plotting/test_boxplot_method.py

+16
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,22 @@ def test_plot_xlabel_ylabel(self, vert):
329329
assert ax.get_xlabel() == xlabel
330330
assert ax.get_ylabel() == ylabel
331331

332+
@pytest.mark.parametrize("vert", [True, False])
333+
def test_plot_box(self, vert):
334+
# GH 54941
335+
rng = np.random.default_rng(2)
336+
df1 = DataFrame(rng.integers(0, 100, size=(100, 4)), columns=list("ABCD"))
337+
df2 = DataFrame(rng.integers(0, 100, size=(100, 4)), columns=list("ABCD"))
338+
339+
xlabel, ylabel = "x", "y"
340+
_, axs = plt.subplots(ncols=2, figsize=(10, 7), sharey=True)
341+
df1.plot.box(ax=axs[0], vert=vert, xlabel=xlabel, ylabel=ylabel)
342+
df2.plot.box(ax=axs[1], vert=vert, xlabel=xlabel, ylabel=ylabel)
343+
for ax in axs:
344+
assert ax.get_xlabel() == xlabel
345+
assert ax.get_ylabel() == ylabel
346+
mpl.pyplot.close()
347+
332348
@pytest.mark.parametrize("vert", [True, False])
333349
def test_boxplot_xlabel_ylabel(self, vert):
334350
df = DataFrame(

0 commit comments

Comments
 (0)