From 5a1e12b282bea2e5b1824476c6c0917d5df2e523 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler <61934744+phofl@users.noreply.github.com> Date: Sun, 18 Sep 2022 13:21:25 +0200 Subject: [PATCH] Backport PR #48601: CI: Fix matplolib release issues --- doc/source/user_guide/visualization.rst | 1 + pandas/plotting/_core.py | 2 +- pandas/plotting/_matplotlib/compat.py | 1 + pandas/plotting/_matplotlib/core.py | 21 +++++++-- pandas/plotting/_matplotlib/style.py | 8 +++- pandas/plotting/_misc.py | 44 +++++++++---------- pandas/tests/plotting/frame/test_frame.py | 26 +++++++---- .../tests/plotting/frame/test_frame_color.py | 4 +- pandas/tests/plotting/test_datetimelike.py | 7 +++ pandas/tests/plotting/test_hist_method.py | 6 +++ pandas/tests/plotting/test_series.py | 16 +++++-- 11 files changed, 94 insertions(+), 42 deletions(-) diff --git a/doc/source/user_guide/visualization.rst b/doc/source/user_guide/visualization.rst index 5ce2f7ca599a7..147981f29476f 100644 --- a/doc/source/user_guide/visualization.rst +++ b/doc/source/user_guide/visualization.rst @@ -625,6 +625,7 @@ To plot multiple column groups in a single axes, repeat ``plot`` method specifyi It is recommended to specify ``color`` and ``label`` keywords to distinguish each groups. .. ipython:: python + :okwarning: ax = df.plot.scatter(x="a", y="b", color="DarkBlue", label="Group 1") @savefig scatter_plot_repeated.png diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 96f9abb301471..1fcefe884dec4 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -1016,7 +1016,7 @@ def __call__(self, *args, **kwargs): >>> s = pd.Series([1, 3, 2]) >>> s.plot.line() - + .. plot:: :context: close-figs diff --git a/pandas/plotting/_matplotlib/compat.py b/pandas/plotting/_matplotlib/compat.py index 6015662999a7d..86b218db4ebe6 100644 --- a/pandas/plotting/_matplotlib/compat.py +++ b/pandas/plotting/_matplotlib/compat.py @@ -19,3 +19,4 @@ def inner(): mpl_ge_3_4_0 = _mpl_version("3.4.0", operator.ge) mpl_ge_3_5_0 = _mpl_version("3.5.0", operator.ge) +mpl_ge_3_6_0 = _mpl_version("3.6.0", operator.ge) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 0b6e5b346062a..bacf19df06205 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -14,6 +14,7 @@ ) import warnings +import matplotlib as mpl from matplotlib.artist import Artist import numpy as np @@ -54,6 +55,7 @@ from pandas.core.frame import DataFrame from pandas.io.formats.printing import pprint_thing +from pandas.plotting._matplotlib.compat import mpl_ge_3_6_0 from pandas.plotting._matplotlib.converter import register_pandas_matplotlib_converters from pandas.plotting._matplotlib.groupby import reconstruct_data_with_by from pandas.plotting._matplotlib.misc import unpack_single_str_list @@ -1205,9 +1207,6 @@ def _make_plot(self): color_by_categorical = c_is_column and is_categorical_dtype(self.data[c]) - # pandas uses colormap, matplotlib uses cmap. - cmap = self.colormap or "Greys" - cmap = self.plt.cm.get_cmap(cmap) color = self.kwds.pop("color", None) if c is not None and color is not None: raise TypeError("Specify exactly one of `c` and `color`") @@ -1222,6 +1221,17 @@ def _make_plot(self): else: c_values = c + # cmap is only used if c_values are integers, otherwise UserWarning + if is_integer_dtype(c_values): + # pandas uses colormap, matplotlib uses cmap. + cmap = self.colormap or "Greys" + if mpl_ge_3_6_0(): + cmap = mpl.colormaps[cmap] + else: + cmap = self.plt.cm.get_cmap(cmap) + else: + cmap = None + if color_by_categorical: from matplotlib import colors @@ -1286,7 +1296,10 @@ def _make_plot(self): ax = self.axes[0] # pandas uses colormap, matplotlib uses cmap. cmap = self.colormap or "BuGn" - cmap = self.plt.cm.get_cmap(cmap) + if mpl_ge_3_6_0(): + cmap = mpl.colormaps[cmap] + else: + cmap = self.plt.cm.get_cmap(cmap) cb = self.kwds.pop("colorbar", True) if C is None: diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 2f29aafbdf5cf..d8823c7ec8d3d 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -12,6 +12,7 @@ ) import warnings +import matplotlib as mpl import matplotlib.cm as cm import matplotlib.colors import numpy as np @@ -22,6 +23,8 @@ import pandas.core.common as com +from pandas.plotting._matplotlib.compat import mpl_ge_3_6_0 + if TYPE_CHECKING: from matplotlib.colors import Colormap @@ -155,7 +158,10 @@ def _get_cmap_instance(colormap: str | Colormap) -> Colormap: """Get instance of matplotlib colormap.""" if isinstance(colormap, str): cmap = colormap - colormap = cm.get_cmap(colormap) + if mpl_ge_3_6_0(): + colormap = mpl.colormaps[colormap] + else: + colormap = cm.get_cmap(colormap) if colormap is None: raise ValueError(f"Colormap {cmap} is not recognized") return colormap diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py index 5bd2e8a53e8e8..71209e1598d9a 100644 --- a/pandas/plotting/_misc.py +++ b/pandas/plotting/_misc.py @@ -139,22 +139,22 @@ def scatter_matrix( >>> df = pd.DataFrame(np.random.randn(1000, 4), columns=['A','B','C','D']) >>> pd.plotting.scatter_matrix(df, alpha=0.2) - array([[, - , - , - ], - [, - , - , - ], - [, - , - , - ], - [, - , - , - ]], dtype=object) + array([[, + , + , + ], + [, + , + , + ], + [, + , + , + ], + [, + , + , + ]], dtype=object) """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.scatter_matrix( @@ -247,7 +247,7 @@ def radviz( ... } ... ) >>> pd.plotting.radviz(df, 'Category') - + """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.radviz( @@ -311,7 +311,7 @@ def andrews_curves( ... 'pandas/main/pandas/tests/io/data/csv/iris.csv' ... ) >>> pd.plotting.andrews_curves(df, 'Name') - + """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.andrews_curves( @@ -445,7 +445,7 @@ def parallel_coordinates( >>> pd.plotting.parallel_coordinates( ... df, 'Name', color=('#556270', '#4ECDC4', '#C7F464') ... ) - + """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.parallel_coordinates( @@ -494,7 +494,7 @@ def lag_plot(series: Series, lag: int = 1, ax: Axes | None = None, **kwds) -> Ax >>> x = np.cumsum(np.random.normal(loc=1, scale=5, size=50)) >>> s = pd.Series(x) >>> s.plot() - + A lag plot with ``lag=1`` returns @@ -502,7 +502,7 @@ def lag_plot(series: Series, lag: int = 1, ax: Axes | None = None, **kwds) -> Ax :context: close-figs >>> pd.plotting.lag_plot(s, lag=1) - + """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.lag_plot(series=series, lag=lag, ax=ax, **kwds) @@ -536,7 +536,7 @@ def autocorrelation_plot(series: Series, ax: Axes | None = None, **kwargs) -> Ax >>> spacing = np.linspace(-9 * np.pi, 9 * np.pi, num=1000) >>> s = pd.Series(0.7 * np.random.rand(1000) + 0.3 * np.sin(spacing)) >>> pd.plotting.autocorrelation_plot(s) - + """ plot_backend = _get_plot_backend("matplotlib") return plot_backend.autocorrelation_plot(series=series, ax=ax, **kwargs) diff --git a/pandas/tests/plotting/frame/test_frame.py b/pandas/tests/plotting/frame/test_frame.py index b38c9adb0a893..09d310e5ce060 100644 --- a/pandas/tests/plotting/frame/test_frame.py +++ b/pandas/tests/plotting/frame/test_frame.py @@ -32,9 +32,15 @@ from pandas.io.formats.printing import pprint_thing import pandas.plotting as plotting +try: + from pandas.plotting._matplotlib.compat import mpl_ge_3_6_0 +except ImportError: + mpl_ge_3_6_0 = lambda: True + @td.skip_if_no_mpl class TestDataFramePlots(TestPlotBase): + @pytest.mark.xfail(mpl_ge_3_6_0(), reason="Api changed") @pytest.mark.slow def test_plot(self): df = tm.makeTimeDataFrame() @@ -733,7 +739,7 @@ def test_plot_scatter_with_c(self): from pandas.plotting._matplotlib.compat import mpl_ge_3_4_0 df = DataFrame( - np.random.randn(6, 4), + np.random.randint(low=0, high=100, size=(6, 4)), index=list(string.ascii_letters[:6]), columns=["x", "y", "z", "four"], ) @@ -1533,8 +1539,8 @@ def test_errorbar_plot_iterator(self): def test_errorbar_with_integer_column_names(self): # test with integer column names - df = DataFrame(np.random.randn(10, 2)) - df_err = DataFrame(np.random.randn(10, 2)) + df = DataFrame(np.abs(np.random.randn(10, 2))) + df_err = DataFrame(np.abs(np.random.randn(10, 2))) ax = _check_plot_works(df.plot, yerr=df_err) self._check_has_errorbars(ax, xerr=0, yerr=2) ax = _check_plot_works(df.plot, y=0, yerr=1) @@ -1542,8 +1548,8 @@ def test_errorbar_with_integer_column_names(self): @pytest.mark.slow def test_errorbar_with_partial_columns(self): - df = DataFrame(np.random.randn(10, 3)) - df_err = DataFrame(np.random.randn(10, 2), columns=[0, 2]) + df = DataFrame(np.abs(np.random.randn(10, 3))) + df_err = DataFrame(np.abs(np.random.randn(10, 2)), columns=[0, 2]) kinds = ["line", "bar"] for kind in kinds: ax = _check_plot_works(df.plot, yerr=df_err, kind=kind) @@ -1631,9 +1637,11 @@ def test_table(self): assert len(ax.tables) == 1 def test_errorbar_scatter(self): - df = DataFrame(np.random.randn(5, 2), index=range(5), columns=["x", "y"]) + df = DataFrame( + np.abs(np.random.randn(5, 2)), index=range(5), columns=["x", "y"] + ) df_err = DataFrame( - np.random.randn(5, 2) / 5, index=range(5), columns=["x", "y"] + np.abs(np.random.randn(5, 2)) / 5, index=range(5), columns=["x", "y"] ) ax = _check_plot_works(df.plot.scatter, x="x", y="y") @@ -1660,7 +1668,9 @@ def _check_errorbar_color(containers, expected, has_err="has_xerr"): ) # GH 8081 - df = DataFrame(np.random.randn(10, 5), columns=["a", "b", "c", "d", "e"]) + df = DataFrame( + np.abs(np.random.randn(10, 5)), columns=["a", "b", "c", "d", "e"] + ) ax = df.plot.scatter(x="a", y="b", xerr="d", yerr="e", c="red") self._check_has_errorbars(ax, xerr=1, yerr=1) _check_errorbar_color(ax.containers, "red", has_err="has_xerr") diff --git a/pandas/tests/plotting/frame/test_frame_color.py b/pandas/tests/plotting/frame/test_frame_color.py index 1cc8381642bbe..e384861d8a57c 100644 --- a/pandas/tests/plotting/frame/test_frame_color.py +++ b/pandas/tests/plotting/frame/test_frame_color.py @@ -655,6 +655,6 @@ def test_colors_of_columns_with_same_name(self): def test_invalid_colormap(self): df = DataFrame(np.random.randn(3, 2), columns=["A", "B"]) - msg = "'invalid_colormap' is not a valid value for name; supported values are " - with pytest.raises(ValueError, match=msg): + msg = "(is not a valid value)|(is not a known colormap)" + with pytest.raises((ValueError, KeyError), match=msg): df.plot(colormap="invalid_colormap") diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index cb428daac84ba..f75e5cd3491a4 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -39,6 +39,11 @@ from pandas.core.indexes.timedeltas import timedelta_range from pandas.tests.plotting.common import TestPlotBase +try: + from pandas.plotting._matplotlib.compat import mpl_ge_3_6_0 +except ImportError: + mpl_ge_3_6_0 = lambda: True + from pandas.tseries.offsets import WeekOfMonth @@ -260,6 +265,7 @@ def test_plot_multiple_inferred_freq(self): ser = Series(np.random.randn(len(dr)), index=dr) _check_plot_works(ser.plot) + @pytest.mark.xfail(mpl_ge_3_6_0(), reason="Api changed") def test_uhf(self): import pandas.plotting._matplotlib.converter as conv @@ -1209,6 +1215,7 @@ def test_secondary_legend(self): # TODO: color cycle problems assert len(colors) == 4 + @pytest.mark.xfail(mpl_ge_3_6_0(), reason="Api changed") def test_format_date_axis(self): rng = date_range("1/1/2012", periods=12, freq="M") df = DataFrame(np.random.randn(len(rng), 3), rng) diff --git a/pandas/tests/plotting/test_hist_method.py b/pandas/tests/plotting/test_hist_method.py index 9c11d589716fe..dc586d15ba115 100644 --- a/pandas/tests/plotting/test_hist_method.py +++ b/pandas/tests/plotting/test_hist_method.py @@ -18,6 +18,11 @@ _check_plot_works, ) +try: + from pandas.plotting._matplotlib.compat import mpl_ge_3_6_0 +except ImportError: + mpl_ge_3_6_0 = lambda: True + @pytest.fixture def ts(): @@ -191,6 +196,7 @@ def test_hist_kwargs(self, ts): ax = ts.plot.hist(align="left", stacked=True, ax=ax) tm.close() + @pytest.mark.xfail(mpl_ge_3_6_0(), reason="Api changed") @td.skip_if_no_scipy def test_hist_kde(self, ts): diff --git a/pandas/tests/plotting/test_series.py b/pandas/tests/plotting/test_series.py index c49354816b8b0..46b2b827d56b3 100644 --- a/pandas/tests/plotting/test_series.py +++ b/pandas/tests/plotting/test_series.py @@ -21,6 +21,11 @@ import pandas.plotting as plotting +try: + from pandas.plotting._matplotlib.compat import mpl_ge_3_6_0 +except ImportError: + mpl_ge_3_6_0 = lambda: True + @pytest.fixture def ts(): @@ -493,6 +498,7 @@ def test_kde_missing_vals(self): # gh-14821: check if the values have any missing values assert any(~np.isnan(axes.lines[0].get_xdata())) + @pytest.mark.xfail(mpl_ge_3_6_0(), reason="Api changed") def test_boxplot_series(self, ts): _, ax = self.plt.subplots() ax = ts.plot.box(logy=True, ax=ax) @@ -575,8 +581,10 @@ def test_errorbar_asymmetrical(self): def test_errorbar_plot(self): s = Series(np.arange(10), name="x") - s_err = np.random.randn(10) - d_err = DataFrame(np.random.randn(10, 2), index=s.index, columns=["x", "y"]) + s_err = np.abs(np.random.randn(10)) + d_err = DataFrame( + np.abs(np.random.randn(10, 2)), index=s.index, columns=["x", "y"] + ) # test line and bar plots kinds = ["line", "bar"] for kind in kinds: @@ -597,8 +605,8 @@ def test_errorbar_plot(self): # test time series plotting ix = date_range("1/1/2000", "1/1/2001", freq="M") ts = Series(np.arange(12), index=ix, name="x") - ts_err = Series(np.random.randn(12), index=ix) - td_err = DataFrame(np.random.randn(12, 2), index=ix, columns=["x", "y"]) + ts_err = Series(np.abs(np.random.randn(12)), index=ix) + td_err = DataFrame(np.abs(np.random.randn(12, 2)), index=ix, columns=["x", "y"]) ax = _check_plot_works(ts.plot, yerr=ts_err) self._check_has_errorbars(ax, xerr=0, yerr=1)