diff --git a/pandas/plotting/_matplotlib/boxplot.py b/pandas/plotting/_matplotlib/boxplot.py index 9fd3bd3a01be0..e6481aab50f6e 100644 --- a/pandas/plotting/_matplotlib/boxplot.py +++ b/pandas/plotting/_matplotlib/boxplot.py @@ -93,7 +93,7 @@ def __init__(self, data, return_type: str = "axes", **kwargs) -> None: # error: Signature of "_plot" incompatible with supertype "MPLPlot" @classmethod def _plot( # type: ignore[override] - cls, ax, y, column_num=None, return_type: str = "axes", **kwds + cls, ax: Axes, y, column_num=None, return_type: str = "axes", **kwds ): if y.ndim == 2: y = [remove_na_arraylike(v) for v in y] diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 57caa05048a60..f2dc671b80b35 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -11,6 +11,7 @@ ) from typing import ( TYPE_CHECKING, + Any, Literal, final, ) @@ -998,7 +999,9 @@ def on_right(self, i: int): return self.data.columns[i] in self.secondary_y @final - def _apply_style_colors(self, colors, kwds, col_num, label: str): + def _apply_style_colors( + self, colors, kwds: dict[str, Any], col_num: int, label: str + ): """ Manage style and color based on column number and its label. Returns tuple of appropriate style and kwds which "color" may be added. diff --git a/pandas/plotting/_matplotlib/hist.py b/pandas/plotting/_matplotlib/hist.py index 904624fb83b40..fd0dde40c0ab3 100644 --- a/pandas/plotting/_matplotlib/hist.py +++ b/pandas/plotting/_matplotlib/hist.py @@ -2,7 +2,9 @@ from typing import ( TYPE_CHECKING, + Any, Literal, + final, ) import numpy as np @@ -58,6 +60,7 @@ def __init__( bottom: int | np.ndarray = 0, *, range=None, + weights=None, **kwargs, ) -> None: if is_list_like(bottom): @@ -65,6 +68,7 @@ def __init__( self.bottom = bottom self._bin_range = range + self.weights = weights self.xlabel = kwargs.get("xlabel") self.ylabel = kwargs.get("ylabel") @@ -96,7 +100,7 @@ def _calculate_bins(self, data: DataFrame, bins) -> np.ndarray: @classmethod def _plot( # type: ignore[override] cls, - ax, + ax: Axes, y, style=None, bottom: int | np.ndarray = 0, @@ -140,7 +144,7 @@ def _make_plot(self, fig: Figure) -> None: if style is not None: kwds["style"] = style - kwds = self._make_plot_keywords(kwds, y) + self._make_plot_keywords(kwds, y) # the bins is multi-dimension array now and each plot need only 1-d and # when by is applied, label should be columns that are grouped @@ -149,21 +153,8 @@ def _make_plot(self, fig: Figure) -> None: kwds["label"] = self.columns kwds.pop("color") - # We allow weights to be a multi-dimensional array, e.g. a (10, 2) array, - # and each sub-array (10,) will be called in each iteration. If users only - # provide 1D array, we assume the same weights is used for all iterations - weights = kwds.get("weights", None) - if weights is not None: - if np.ndim(weights) != 1 and np.shape(weights)[-1] != 1: - try: - weights = weights[:, i] - except IndexError as err: - raise ValueError( - "weights must have the same shape as data, " - "or be a single column" - ) from err - weights = weights[~isna(y)] - kwds["weights"] = weights + if self.weights is not None: + kwds["weights"] = self._get_column_weights(self.weights, i, y) y = reformat_hist_y_given_by(y, self.by) @@ -175,12 +166,29 @@ def _make_plot(self, fig: Figure) -> None: self._append_legend_handles_labels(artists[0], label) - def _make_plot_keywords(self, kwds, y): + def _make_plot_keywords(self, kwds: dict[str, Any], y) -> None: """merge BoxPlot/KdePlot properties to passed kwds""" # y is required for KdePlot kwds["bottom"] = self.bottom kwds["bins"] = self.bins - return kwds + + @final + @staticmethod + def _get_column_weights(weights, i: int, y): + # We allow weights to be a multi-dimensional array, e.g. a (10, 2) array, + # and each sub-array (10,) will be called in each iteration. If users only + # provide 1D array, we assume the same weights is used for all iterations + if weights is not None: + if np.ndim(weights) != 1 and np.shape(weights)[-1] != 1: + try: + weights = weights[:, i] + except IndexError as err: + raise ValueError( + "weights must have the same shape as data, " + "or be a single column" + ) from err + weights = weights[~isna(y)] + return weights def _post_plot_logic(self, ax: Axes, data) -> None: if self.orientation == "horizontal": @@ -207,11 +215,14 @@ def _kind(self) -> Literal["kde"]: def orientation(self) -> Literal["vertical"]: return "vertical" - def __init__(self, data, bw_method=None, ind=None, **kwargs) -> None: + def __init__( + self, data, bw_method=None, ind=None, *, weights=None, **kwargs + ) -> None: # Do not call LinePlot.__init__ which may fill nan MPLPlot.__init__(self, data, **kwargs) # pylint: disable=non-parent-init-called self.bw_method = bw_method self.ind = ind + self.weights = weights @staticmethod def _get_ind(y, ind): @@ -233,9 +244,10 @@ def _get_ind(y, ind): return ind @classmethod - def _plot( + # error: Signature of "_plot" incompatible with supertype "MPLPlot" + def _plot( # type: ignore[override] cls, - ax, + ax: Axes, y, style=None, bw_method=None, @@ -253,10 +265,9 @@ def _plot( lines = MPLPlot._plot(ax, ind, y, style=style, **kwds) return lines - def _make_plot_keywords(self, kwds, y): + def _make_plot_keywords(self, kwds: dict[str, Any], y) -> None: kwds["bw_method"] = self.bw_method kwds["ind"] = self._get_ind(y, ind=self.ind) - return kwds def _post_plot_logic(self, ax, data) -> None: ax.set_ylabel("Density")