Skip to content

Commit 2e141aa

Browse files
authored
Subplot title count fix + fix for issue introduced in earlier PR (#61393)
* test case for subplot stacking * Removed overlooked print statement * Updated test to check other subplot in figure * Updated test cases to include more subplot stacking possibilities * removed savefig() left in test cases * Updated test cases to test more arrangements * Completed function fix (order of subplot input does not matter, need clarification if it matters) * appeasing the great pre-commit formatter * Updated whatsnew * Docstring adjustment * Moved self.subplot check to a seperate bool * Added ignore where mypy thinks self.subplots is a bool * Actually addressed mypy typing * Incorperated initial PR comments * Updated missing () after .all * Initial test cases * Addressed more comments on PR * Updated '&' to 'and' * Updated Test cases * Fixed crash when "subplots=True" is used * Title check checks for subplot length if specified * Updated Test cases * Title check checks for subplot length if specified * Updated test name * Removed extra '_' in test name * Fixed issue where expected_total_height returns as a df instead of a series * Updated change notes * Addressed mypy error * Addresed PR comments * Changed exception message raised and updated test case
1 parent 7595ed5 commit 2e141aa

File tree

3 files changed

+64
-10
lines changed

3 files changed

+64
-10
lines changed

doc/source/whatsnew/v3.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,7 @@ Plotting
801801
- Bug in :meth:`DataFrame.plot.bar` with ``stacked=True`` where labels on stacked bars with zero-height segments were incorrectly positioned at the base instead of the label position of the previous segment (:issue:`59429`)
802802
- Bug in :meth:`DataFrame.plot.line` raising ``ValueError`` when set both color and a ``dict`` style (:issue:`59461`)
803803
- Bug in :meth:`DataFrame.plot` that causes a shift to the right when the frequency multiplier is greater than one. (:issue:`57587`)
804+
- Bug in :meth:`DataFrame.plot` where ``title`` would require extra titles when plotting more than one column per subplot. (:issue:`61019`)
804805
- Bug in :meth:`Series.plot` preventing a line and bar from being aligned on the same plot (:issue:`61161`)
805806
- Bug in :meth:`Series.plot` preventing a line and scatter plot from being aligned (:issue:`61005`)
806807
- Bug in :meth:`Series.plot` with ``kind="pie"`` with :class:`ArrowDtype` (:issue:`59192`)

pandas/plotting/_matplotlib/core.py

+15-8
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,13 @@ def _adorn_subplots(self, fig: Figure) -> None:
802802
if self.title:
803803
if self.subplots:
804804
if is_list_like(self.title):
805-
if len(self.title) != self.nseries:
805+
if not isinstance(self.subplots, bool):
806+
if len(self.subplots) != len(self.title):
807+
raise ValueError(
808+
f"The number of titles ({len(self.title)}) must equal "
809+
f"the number of subplots ({len(self.subplots)})."
810+
)
811+
elif len(self.title) != self.nseries:
806812
raise ValueError(
807813
"The length of `title` must equal the number "
808814
"of columns if using `title` of type `list` "
@@ -1934,13 +1940,14 @@ def _make_plot(self, fig: Figure) -> None:
19341940

19351941
self.subplots: list[Any]
19361942

1937-
if bool(self.subplots) and self.stacked:
1938-
for i, sub_plot in enumerate(self.subplots):
1939-
if len(sub_plot) <= 1:
1940-
continue
1941-
for plot in sub_plot:
1942-
_stacked_subplots_ind[int(plot)] = i
1943-
_stacked_subplots_offsets.append([0, 0])
1943+
if not isinstance(self.subplots, bool):
1944+
if bool(self.subplots) and self.stacked:
1945+
for i, sub_plot in enumerate(self.subplots):
1946+
if len(sub_plot) <= 1:
1947+
continue
1948+
for plot in sub_plot:
1949+
_stacked_subplots_ind[int(plot)] = i
1950+
_stacked_subplots_offsets.append([0, 0])
19441951

19451952
for i, (label, y) in enumerate(self._iter_data(data=data)):
19461953
ax = self._get_ax(i)

pandas/tests/plotting/test_misc.py

+48-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
plt = pytest.importorskip("matplotlib.pyplot")
3232
cm = pytest.importorskip("matplotlib.cm")
3333

34+
import re
35+
3436
from pandas.plotting._matplotlib.style import get_standard_colors
3537

3638

@@ -727,7 +729,11 @@ def _df_bar_subplot_checker(df_bar_data, df_bar_df, subplot_data_df, subplot_col
727729
].reset_index()
728730
for i in range(len(subplot_columns))
729731
]
730-
expected_total_height = df_bar_df.loc[:, subplot_columns].sum(axis=1)
732+
733+
if len(subplot_columns) == 1:
734+
expected_total_height = df_bar_df.loc[:, subplot_columns[0]]
735+
else:
736+
expected_total_height = df_bar_df.loc[:, subplot_columns].sum(axis=1)
731737

732738
for i in range(len(subplot_columns)):
733739
sliced_df = subplot_sliced_by_source[i]
@@ -743,7 +749,6 @@ def _df_bar_subplot_checker(df_bar_data, df_bar_df, subplot_data_df, subplot_col
743749
tm.assert_series_equal(
744750
height_iter, expected_total_height, check_names=False, check_dtype=False
745751
)
746-
747752
else:
748753
# Checks each preceding bar ends where the next one starts
749754
next_start_coord = subplot_sliced_by_source[i + 1]["y_coord"]
@@ -816,3 +821,44 @@ def test_bar_2_subplots_1_triple_stacked(df_bar_data, df_bar_df, subplot_divisio
816821
_df_bar_subplot_checker(
817822
df_bar_data, df_bar_df, subplot_data_df_list[i], subplot_division[i]
818823
)
824+
825+
826+
def test_bar_subplots_stacking_bool(df_bar_data, df_bar_df):
827+
subplot_division = [("A"), ("B"), ("C"), ("D")]
828+
ax = df_bar_df.plot(subplots=True, kind="bar", stacked=True)
829+
subplot_data_df_list = _df_bar_xyheight_from_ax_helper(
830+
df_bar_data, ax, subplot_division
831+
)
832+
for i in range(len(subplot_data_df_list)):
833+
_df_bar_subplot_checker(
834+
df_bar_data, df_bar_df, subplot_data_df_list[i], subplot_division[i]
835+
)
836+
837+
838+
def test_plot_bar_label_count_default():
839+
df = DataFrame(
840+
[(30, 10, 10, 10), (20, 20, 20, 20), (10, 30, 30, 10)], columns=list("ABCD")
841+
)
842+
df.plot(subplots=True, kind="bar", title=["A", "B", "C", "D"])
843+
844+
845+
def test_plot_bar_label_count_expected_fail():
846+
df = DataFrame(
847+
[(30, 10, 10, 10), (20, 20, 20, 20), (10, 30, 30, 10)], columns=list("ABCD")
848+
)
849+
error_regex = re.escape(
850+
"The number of titles (4) must equal the number of subplots (3)."
851+
)
852+
with pytest.raises(ValueError, match=error_regex):
853+
df.plot(
854+
subplots=[("A", "B")],
855+
kind="bar",
856+
title=["A&B", "C", "D", "Extra Title"],
857+
)
858+
859+
860+
def test_plot_bar_label_count_expected_success():
861+
df = DataFrame(
862+
[(30, 10, 10, 10), (20, 20, 20, 20), (10, 30, 30, 10)], columns=list("ABCD")
863+
)
864+
df.plot(subplots=[("A", "B", "D")], kind="bar", title=["A&B&D", "C"])

0 commit comments

Comments
 (0)