From f795593a902b7550f7b05731362d9caa0808cf6e Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 6 Nov 2023 09:55:15 -0800 Subject: [PATCH 1/4] REF: validate_sharex --- pandas/plotting/_matplotlib/core.py | 30 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index fa5b7c906355c..c0e8d2804645b 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -12,6 +12,7 @@ from typing import ( TYPE_CHECKING, Literal, + final, ) import warnings @@ -24,6 +25,7 @@ from pandas.core.dtypes.common import ( is_any_real_numeric_dtype, + is_bool, is_float, is_float_dtype, is_hashable, @@ -129,7 +131,7 @@ def __init__( kind=None, by: IndexLabel | None = None, subplots: bool | Sequence[Sequence[str]] = False, - sharex=None, + sharex: bool | None = None, sharey: bool = False, use_index: bool = True, figsize: tuple[float, float] | None = None, @@ -190,17 +192,7 @@ def __init__( self.subplots = self._validate_subplots_kwarg(subplots) - if sharex is None: - # if by is defined, subplots are used and sharex should be False - if ax is None and by is None: - self.sharex = True - else: - # if we get an axis, the users should do the visibility - # setting... - self.sharex = False - else: - self.sharex = sharex - + self.sharex = self._validate_sharex(sharex, ax, by) self.sharey = sharey self.figsize = figsize self.layout = layout @@ -274,6 +266,20 @@ def __init__( self._validate_color_args() + @final + def _validate_sharex(self, sharex: bool | None, ax, by) -> bool: + if sharex is None: + # if by is defined, subplots are used and sharex should be False + if ax is None and by is None: + sharex = True + else: + # if we get an axis, the users should do the visibility + # setting... + sharex = False + elif not is_bool(sharex): + raise TypeError("sharex must be a bool or None") + return sharex + def _validate_subplots_kwarg( self, subplots: bool | Sequence[Sequence[str]] ) -> bool | list[tuple[int, ...]]: From d4ac73fe6ea6e9e7e699d5b4b624e8bc2d02a658 Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 6 Nov 2023 10:35:36 -0800 Subject: [PATCH 2/4] REF: make plotting less stateful (2) --- pandas/plotting/_matplotlib/boxplot.py | 1 - pandas/plotting/_matplotlib/core.py | 35 ++++++-------------------- pandas/plotting/_matplotlib/hist.py | 29 +++++++++------------ 3 files changed, 20 insertions(+), 45 deletions(-) diff --git a/pandas/plotting/_matplotlib/boxplot.py b/pandas/plotting/_matplotlib/boxplot.py index 8027cc76a9a40..8e2b6377a0fb2 100644 --- a/pandas/plotting/_matplotlib/boxplot.py +++ b/pandas/plotting/_matplotlib/boxplot.py @@ -81,7 +81,6 @@ def __init__(self, data, return_type: str = "axes", **kwargs) -> None: # Do not call LinePlot.__init__ which may fill nan MPLPlot.__init__(self, data, **kwargs) # pylint: disable=non-parent-init-called - def _args_adjust(self) -> None: if self.subplots: # Disable label ax sharing. Otherwise, all subplots shows last # column label diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index c0e8d2804645b..935666fd161ec 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -455,7 +455,6 @@ def draw(self) -> None: self.plt.draw_if_interactive() def generate(self) -> None: - self._args_adjust() self._compute_plot_data() fig = self._setup_subplots() self._make_plot(fig) @@ -467,10 +466,6 @@ def generate(self) -> None: self._post_plot_logic_common(ax, self.data) self._post_plot_logic(ax, self.data) - @abstractmethod - def _args_adjust(self) -> None: - pass - def _has_plotted_object(self, ax: Axes) -> bool: """check whether ax has data""" return len(ax.lines) != 0 or len(ax.artists) != 0 or len(ax.containers) != 0 @@ -1299,9 +1294,6 @@ def _make_plot(self, fig: Figure): err_kwds["ecolor"] = scatter.get_facecolor()[0] ax.errorbar(data[x].values, data[y].values, linestyle="none", **err_kwds) - def _args_adjust(self) -> None: - pass - class HexBinPlot(PlanePlot): @property @@ -1334,9 +1326,6 @@ def _make_plot(self, fig: Figure) -> None: def _make_legend(self) -> None: pass - def _args_adjust(self) -> None: - pass - class LinePlot(MPLPlot): _default_rot = 0 @@ -1495,9 +1484,6 @@ def _update_stacker(cls, ax: Axes, stacking_id, values) -> None: elif (values <= 0).all(): ax._stacker_neg_prior[stacking_id] += values - def _args_adjust(self) -> None: - pass - def _post_plot_logic(self, ax: Axes, data) -> None: from matplotlib.ticker import FixedLocator @@ -1607,9 +1593,6 @@ def _plot( # type: ignore[override] res = [rect] return res - def _args_adjust(self) -> None: - pass - def _post_plot_logic(self, ax: Axes, data) -> None: LinePlot._post_plot_logic(self, ax, data) @@ -1642,8 +1625,14 @@ def __init__(self, data, **kwargs) -> None: kwargs.setdefault("align", "center") self.tick_pos = np.arange(len(data)) - self.bottom = kwargs.pop("bottom", 0) - self.left = kwargs.pop("left", 0) + bottom = kwargs.pop("bottom", 0) + left = kwargs.pop("left", 0) + if is_list_like(bottom): + bottom = np.array(bottom) + if is_list_like(left): + left = np.array(left) + self.bottom = bottom + self.left = left self.log = kwargs.pop("log", False) MPLPlot.__init__(self, data, **kwargs) @@ -1664,12 +1653,6 @@ def __init__(self, data, **kwargs) -> None: self.ax_pos = self.tick_pos - self.tickoffset - def _args_adjust(self) -> None: - if is_list_like(self.bottom): - self.bottom = np.array(self.bottom) - if is_list_like(self.left): - self.left = np.array(self.left) - # error: Signature of "_plot" incompatible with supertype "MPLPlot" @classmethod def _plot( # type: ignore[override] @@ -1840,8 +1823,6 @@ def __init__(self, data, kind=None, **kwargs) -> None: if (data < 0).any().any(): raise ValueError(f"{self._kind} plot doesn't allow negative values") MPLPlot.__init__(self, data, kind=kind, **kwargs) - - def _args_adjust(self) -> None: self.grid = False self.logy = False self.logx = False diff --git a/pandas/plotting/_matplotlib/hist.py b/pandas/plotting/_matplotlib/hist.py index 0a2d158d5f3e8..2279905c053a4 100644 --- a/pandas/plotting/_matplotlib/hist.py +++ b/pandas/plotting/_matplotlib/hist.py @@ -58,36 +58,34 @@ def __init__( bottom: int | np.ndarray = 0, **kwargs, ) -> None: - self.bins = bins # use mpl default + if is_list_like(bottom): + bottom = np.array(bottom) self.bottom = bottom + self.xlabel = kwargs.get("xlabel") self.ylabel = kwargs.get("ylabel") # Do not call LinePlot.__init__ which may fill nan MPLPlot.__init__(self, data, **kwargs) # pylint: disable=non-parent-init-called - def _args_adjust(self) -> None: - # calculate bin number separately in different subplots - # where subplots are created based on by argument - if is_integer(self.bins): + self.bins = self._adjust_bins(bins) + + def _adjust_bins(self, bins: int | np.ndarray | list[np.ndarray]): + if is_integer(bins): if self.by is not None: by_modified = unpack_single_str_list(self.by) grouped = self.data.groupby(by_modified)[self.columns] - self.bins = [self._calculate_bins(group) for key, group in grouped] + bins = [self._calculate_bins(group, bins) for key, group in grouped] else: - self.bins = self._calculate_bins(self.data) - - if is_list_like(self.bottom): - self.bottom = np.array(self.bottom) + bins = self._calculate_bins(self.data, bins) + return bins - def _calculate_bins(self, data: DataFrame) -> np.ndarray: + def _calculate_bins(self, data: DataFrame, bins) -> np.ndarray: """Calculate bins given data""" nd_values = data.infer_objects(copy=False)._get_numeric_data() values = np.ravel(nd_values) values = values[~isna(values)] - hist, bins = np.histogram( - values, bins=self.bins, range=self.kwds.get("range", None) - ) + hist, bins = np.histogram(values, bins=bins, range=self.kwds.get("range", None)) return bins # error: Signature of "_plot" incompatible with supertype "LinePlot" @@ -211,9 +209,6 @@ def __init__(self, data, bw_method=None, ind=None, **kwargs) -> None: self.bw_method = bw_method self.ind = ind - def _args_adjust(self) -> None: - pass - def _get_ind(self, y): if self.ind is None: # np.nanmax() and np.nanmin() ignores the missing values From b4b490c1aed78f4709e4129957f2e25ec79e3f8a Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 6 Nov 2023 13:32:30 -0800 Subject: [PATCH 3/4] pylint ignore --- pandas/plotting/_matplotlib/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 935666fd161ec..0fa2a13e87541 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -270,7 +270,7 @@ def __init__( def _validate_sharex(self, sharex: bool | None, ax, by) -> bool: if sharex is None: # if by is defined, subplots are used and sharex should be False - if ax is None and by is None: + if ax is None and by is None: # pylint: disable=simplifiable-if-statement sharex = True else: # if we get an axis, the users should do the visibility From 0d970bb912b2ccd9f8567055d95c82ffce55f13f Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 6 Nov 2023 14:26:44 -0800 Subject: [PATCH 4/4] mypy fixup --- pandas/plotting/_matplotlib/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 0fa2a13e87541..75e84abc37a9e 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -278,7 +278,7 @@ def _validate_sharex(self, sharex: bool | None, ax, by) -> bool: sharex = False elif not is_bool(sharex): raise TypeError("sharex must be a bool or None") - return sharex + return bool(sharex) def _validate_subplots_kwarg( self, subplots: bool | Sequence[Sequence[str]]