diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 81f5b5cb0f74c..78c7082c69b6b 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -5,19 +5,16 @@ from pandas.core.dtypes.common import is_integer, is_list_like from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries -import pandas from pandas.core.base import PandasObject from pandas.core.generic import _shared_doc_kwargs, _shared_docs -# Automatically registering converters was deprecated in 0.21, but -# the deprecation warning wasn't showing until 0.24 -# This block will be eventually removed, but it's not clear when -if pandas.get_option('plotting.matplotlib.register_converters'): - try: - from .misc import register - register(explicit=False) - except ImportError: - pass +# Trigger matplotlib import, which implicitly registers our +# converts. Implicit registration is deprecated, and when enforced +# we can lazily import matplotlib. +try: + import pandas.plotting._matplotlib # noqa +except ImportError: + pass df_kind = """- 'scatter' : scatter plot - 'hexbin' : hexbin plot""" diff --git a/pandas/plotting/_matplotlib/__init__.py b/pandas/plotting/_matplotlib/__init__.py index 5cfb6843db9ed..1b775d03349d0 100644 --- a/pandas/plotting/_matplotlib/__init__.py +++ b/pandas/plotting/_matplotlib/__init__.py @@ -1,3 +1,5 @@ +from pandas._config import get_option + from pandas.plotting._matplotlib.boxplot import ( BoxPlot, boxplot, boxplot_frame, boxplot_frame_groupby) from pandas.plotting._matplotlib.converter import deregister, register @@ -11,6 +13,10 @@ from pandas.plotting._matplotlib.timeseries import tsplot from pandas.plotting._matplotlib.tools import table +if get_option("plotting.matplotlib.register_converters"): + register(explicit=False) + + __all__ = ['LinePlot', 'BarPlot', 'BarhPlot', 'HistPlot', 'BoxPlot', 'KdePlot', 'AreaPlot', 'PiePlot', 'ScatterPlot', 'HexBinPlot', 'hist_series', 'hist_frame', 'boxplot', 'boxplot_frame', 'boxplot_frame_groupby', diff --git a/pandas/plotting/_matplotlib/boxplot.py b/pandas/plotting/_matplotlib/boxplot.py index b8a7da5270fc0..f8bc531e3c344 100644 --- a/pandas/plotting/_matplotlib/boxplot.py +++ b/pandas/plotting/_matplotlib/boxplot.py @@ -1,7 +1,6 @@ from collections import namedtuple import warnings -from matplotlib import pyplot as plt from matplotlib.artist import setp import numpy as np @@ -11,6 +10,7 @@ import pandas as pd from pandas.io.formats.printing import pprint_thing +from pandas.plotting._matplotlib import converter from pandas.plotting._matplotlib.core import LinePlot, MPLPlot from pandas.plotting._matplotlib.style import _get_standard_colors from pandas.plotting._matplotlib.tools import _flatten, _subplots @@ -215,6 +215,7 @@ def boxplot(data, column=None, by=None, ax=None, fontsize=None, rot=0, grid=True, figsize=None, layout=None, return_type=None, **kwds): + import matplotlib.pyplot as plt # validate return_type: if return_type not in BoxPlot._valid_return_types: raise ValueError("return_type must be {'axes', 'dict', 'both'}") @@ -296,6 +297,8 @@ def plot_group(keys, values, ax): def boxplot_frame(self, column=None, by=None, ax=None, fontsize=None, rot=0, grid=True, figsize=None, layout=None, return_type=None, **kwds): + import matplotlib.pyplot as plt + converter._WARN = False # no warning for pandas plots ax = boxplot(self, column=column, by=by, ax=ax, fontsize=fontsize, grid=grid, rot=rot, figsize=figsize, layout=layout, return_type=return_type, **kwds) @@ -306,6 +309,7 @@ def boxplot_frame(self, column=None, by=None, ax=None, fontsize=None, rot=0, def boxplot_frame_groupby(grouped, subplots=True, column=None, fontsize=None, rot=0, grid=True, ax=None, figsize=None, layout=None, sharex=False, sharey=True, **kwds): + converter._WARN = False # no warning for pandas plots if subplots is True: naxes = len(grouped) fig, axes = _subplots(naxes=naxes, squeeze=False, diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index a7049afee80b0..5fb4d201223bd 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -2,7 +2,6 @@ from typing import Optional # noqa import warnings -import matplotlib.pyplot as plt import numpy as np from pandas._config import get_option @@ -61,6 +60,8 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=None, secondary_y=False, colormap=None, table=False, layout=None, **kwds): + import matplotlib.pyplot as plt + converter._WARN = False # no warning for pandas plots self.data = data self.by = by @@ -103,7 +104,7 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=None, self.rot = self._default_rot if grid is None: - grid = False if secondary_y else self.plt.rcParams['axes.grid'] + grid = False if secondary_y else plt.rcParams['axes.grid'] self.grid = grid self.legend = legend @@ -618,6 +619,8 @@ def _get_ax(self, i): @classmethod def get_default_ax(cls, ax): + import matplotlib.pyplot as plt + if ax is None and len(plt.get_fignums()) > 0: with plt.rc_context(): ax = plt.gca() diff --git a/pandas/plotting/_matplotlib/hist.py b/pandas/plotting/_matplotlib/hist.py index 585c407e33311..d34c0cb6a3889 100644 --- a/pandas/plotting/_matplotlib/hist.py +++ b/pandas/plotting/_matplotlib/hist.py @@ -1,6 +1,5 @@ import warnings -import matplotlib.pyplot as plt import numpy as np from pandas.core.dtypes.common import is_integer, is_list_like @@ -10,6 +9,7 @@ import pandas.core.common as com from pandas.io.formats.printing import pprint_thing +from pandas.plotting._matplotlib import converter from pandas.plotting._matplotlib.core import LinePlot, MPLPlot from pandas.plotting._matplotlib.tools import ( _flatten, _set_ticks_props, _subplots) @@ -203,6 +203,7 @@ def _grouped_hist(data, column=None, by=None, ax=None, bins=50, figsize=None, def plot_group(group, ax): ax.hist(group.dropna().values, bins=bins, **kwargs) + converter._WARN = False # no warning for pandas plots xrot = xrot or rot fig, axes = _grouped_plot(plot_group, data, column=column, @@ -220,6 +221,7 @@ def plot_group(group, ax): def hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds): + import matplotlib.pyplot as plt if by is None: if kwds.get('layout', None) is not None: raise ValueError("The 'layout' keyword is not supported when " @@ -261,6 +263,7 @@ def hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False, sharey=False, figsize=None, layout=None, bins=10, **kwds): + converter._WARN = False # no warning for pandas plots if by is not None: axes = _grouped_hist(data, column=column, by=by, ax=ax, grid=grid, figsize=figsize, sharex=sharex, sharey=sharey, diff --git a/pandas/plotting/_matplotlib/misc.py b/pandas/plotting/_matplotlib/misc.py index dacc9ef04f819..663a3c5153fac 100644 --- a/pandas/plotting/_matplotlib/misc.py +++ b/pandas/plotting/_matplotlib/misc.py @@ -2,7 +2,6 @@ import matplotlib.lines as mlines import matplotlib.patches as patches -import matplotlib.pyplot as plt import numpy as np from pandas.core.dtypes.missing import notna @@ -105,6 +104,7 @@ def _get_marker_compat(marker): def radviz(frame, class_column, ax=None, color=None, colormap=None, **kwds): + import matplotlib.pyplot as plt def normalize(series): a = min(series) @@ -169,6 +169,7 @@ def normalize(series): def andrews_curves(frame, class_column, ax=None, samples=200, color=None, colormap=None, **kwds): + import matplotlib.pyplot as plt def function(amplitudes): def f(t): @@ -224,6 +225,7 @@ def f(t): def bootstrap_plot(series, fig=None, size=50, samples=500, **kwds): + import matplotlib.pyplot as plt # random.sample(ndarray, int) fails on python 3.3, sigh data = list(series.values) samplings = [random.sample(data, size) for _ in range(samples)] @@ -270,6 +272,7 @@ def parallel_coordinates(frame, class_column, cols=None, ax=None, color=None, use_columns=False, xticks=None, colormap=None, axvlines=True, axvlines_kwds=None, sort_labels=False, **kwds): + import matplotlib.pyplot as plt if axvlines_kwds is None: axvlines_kwds = {'linewidth': 1, 'color': 'black'} @@ -336,6 +339,7 @@ def parallel_coordinates(frame, class_column, cols=None, ax=None, color=None, def lag_plot(series, lag=1, ax=None, **kwds): # workaround because `c='b'` is hardcoded in matplotlibs scatter method + import matplotlib.pyplot as plt kwds.setdefault('c', plt.rcParams['patch.facecolor']) data = series.values @@ -350,6 +354,8 @@ def lag_plot(series, lag=1, ax=None, **kwds): def autocorrelation_plot(series, ax=None, **kwds): + import matplotlib.pyplot as plt + n = len(series) data = np.asarray(series) if ax is None: diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 80a15942a2867..8c9e3ea330dd3 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -3,7 +3,6 @@ import matplotlib.cm as cm import matplotlib.colors -import matplotlib.pyplot as plt import numpy as np from pandas.core.dtypes.common import is_list_like @@ -13,6 +12,7 @@ def _get_standard_colors(num_colors=None, colormap=None, color_type='default', color=None): + import matplotlib.pyplot as plt if color is None and colormap is not None: if isinstance(colormap, str): cmap = colormap diff --git a/pandas/plotting/_matplotlib/timeseries.py b/pandas/plotting/_matplotlib/timeseries.py index 30038b599a386..e36ffed10d94f 100644 --- a/pandas/plotting/_matplotlib/timeseries.py +++ b/pandas/plotting/_matplotlib/timeseries.py @@ -3,8 +3,6 @@ import functools import warnings -from matplotlib import pylab -import matplotlib.pyplot as plt import numpy as np from pandas._libs.tslibs.frequencies import ( @@ -42,6 +40,7 @@ def tsplot(series, plotf, ax=None, **kwargs): .. deprecated:: 0.23.0 Use Series.plot() instead """ + import matplotlib.pyplot as plt warnings.warn("'tsplot' is deprecated and will be removed in a " "future version. Please use Series.plot() instead.", FutureWarning, stacklevel=2) @@ -323,6 +322,7 @@ def format_dateaxis(subplot, freq, index): default, changing the limits of the x axis will intelligently change the positions of the ticks. """ + from matplotlib import pylab # handle index specific formatting # Note: DatetimeIndex does not use this diff --git a/pandas/plotting/_matplotlib/tools.py b/pandas/plotting/_matplotlib/tools.py index f6393fc76892f..e491cfc3309a0 100644 --- a/pandas/plotting/_matplotlib/tools.py +++ b/pandas/plotting/_matplotlib/tools.py @@ -2,7 +2,6 @@ from math import ceil import warnings -import matplotlib.pyplot as plt import matplotlib.table import matplotlib.ticker as ticker import numpy as np @@ -168,6 +167,7 @@ def _subplots(naxes=None, sharex=False, sharey=False, squeeze=True, # Four polar axes plt.subplots(2, 2, subplot_kw=dict(polar=True)) """ + import matplotlib.pyplot as plt if subplot_kw is None: subplot_kw = {} @@ -345,6 +345,7 @@ def _get_xlim(lines): def _set_ticks_props(axes, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None): + import matplotlib.pyplot as plt for ax in _flatten(axes): if xlabelsize is not None: plt.setp(ax.get_xticklabels(), fontsize=xlabelsize) diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index 39cd48ff35f96..92d207e46b7ab 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -12,11 +12,30 @@ from pandas import Index, Period, Series, Timestamp, date_range import pandas.util.testing as tm +from pandas.plotting import ( + deregister_matplotlib_converters, register_matplotlib_converters) from pandas.tseries.offsets import Day, Micro, Milli, Second -converter = pytest.importorskip('pandas.plotting._converter') -from pandas.plotting import (deregister_matplotlib_converters, # isort:skip - register_matplotlib_converters) +try: + from pandas.plotting._matplotlib import converter +except ImportError: + # try / except, rather than skip, to avoid internal refactoring + # causing an improprer skip + pass + +pytest.importorskip('matplotlib.pyplot') + + +def test_initial_warning(): + code = ( + "import pandas as pd; import matplotlib.pyplot as plt; " + "s = pd.Series(1, pd.date_range('2000', periods=12)); " + "fig, ax = plt.subplots(); " + "ax.plot(s.index, s.values)" + ) + call = [sys.executable, '-c', code] + out = subprocess.check_output(call, stderr=subprocess.STDOUT).decode() + assert 'Using an implicitly' in out def test_timtetonum_accepts_unicode(): diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 10743ca95e29e..c3d824389aa4d 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -374,7 +374,6 @@ def test_axis_limits(self): def _test(ax): xlim = ax.get_xlim() ax.set_xlim(xlim[0] - 5, xlim[1] + 10) - ax.get_figure().canvas.draw() result = ax.get_xlim() assert result[0] == xlim[0] - 5 assert result[1] == xlim[1] + 10 @@ -383,7 +382,6 @@ def _test(ax): expected = (Period('1/1/2000', ax.freq), Period('4/1/2000', ax.freq)) ax.set_xlim('1/1/2000', '4/1/2000') - ax.get_figure().canvas.draw() result = ax.get_xlim() assert int(result[0]) == expected[0].ordinal assert int(result[1]) == expected[1].ordinal @@ -392,7 +390,6 @@ def _test(ax): expected = (Period('1/1/2000', ax.freq), Period('4/1/2000', ax.freq)) ax.set_xlim(datetime(2000, 1, 1), datetime(2000, 4, 1)) - ax.get_figure().canvas.draw() result = ax.get_xlim() assert int(result[0]) == expected[0].ordinal assert int(result[1]) == expected[1].ordinal @@ -429,12 +426,7 @@ def test_get_finder(self): def test_finder_daily(self): day_lst = [10, 40, 252, 400, 950, 2750, 10000] - if self.mpl_ge_3_0_0 or not self.mpl_ge_2_2_3: - xpl1 = xpl2 = [Period('1999-1-1', freq='B').ordinal] * len(day_lst) - else: # 2.2.3, 2.2.4 - xpl1 = [7565, 7564, 7553, 7546, 7518, 7428, 7066] - xpl2 = [7566, 7564, 7554, 7546, 7519, 7429, 7066] - + xpl1 = xpl2 = [Period('1999-1-1', freq='B').ordinal] * len(day_lst) rs1 = [] rs2 = [] for i, n in enumerate(day_lst): @@ -457,12 +449,7 @@ def test_finder_daily(self): def test_finder_quarterly(self): yrs = [3.5, 11] - if self.mpl_ge_3_0_0 or not self.mpl_ge_2_2_3: - xpl1 = xpl2 = [Period('1988Q1').ordinal] * len(yrs) - else: # 2.2.3, 2.2.4 - xpl1 = [68, 68] - xpl2 = [72, 68] - + xpl1 = xpl2 = [Period('1988Q1').ordinal] * len(yrs) rs1 = [] rs2 = [] for i, n in enumerate(yrs): @@ -485,12 +472,7 @@ def test_finder_quarterly(self): def test_finder_monthly(self): yrs = [1.15, 2.5, 4, 11] - if self.mpl_ge_3_0_0 or not self.mpl_ge_2_2_3: - xpl1 = xpl2 = [Period('Jan 1988').ordinal] * len(yrs) - else: # 2.2.3, 2.2.4 - xpl1 = [216, 216, 204, 204] - xpl2 = [216, 216, 216, 204] - + xpl1 = xpl2 = [Period('Jan 1988').ordinal] * len(yrs) rs1 = [] rs2 = [] for i, n in enumerate(yrs): @@ -521,11 +503,7 @@ def test_finder_monthly_long(self): @pytest.mark.slow def test_finder_annual(self): - if self.mpl_ge_3_0_0 or not self.mpl_ge_2_2_3: - xp = [1987, 1988, 1990, 1990, 1995, 2020, 2070, 2170] - else: # 2.2.3, 2.2.4 - xp = [1986, 1986, 1990, 1990, 1995, 2020, 1970, 1970] - + xp = [1987, 1988, 1990, 1990, 1995, 2020, 2070, 2170] xp = [Period(x, freq='A').ordinal for x in xp] rs = [] for i, nyears in enumerate([5, 10, 19, 49, 99, 199, 599, 1001]): @@ -1093,7 +1071,6 @@ def test_time(self): df.plot(ax=ax) # verify tick labels - fig.canvas.draw() ticks = ax.get_xticks() labels = ax.get_xticklabels() for t, l in zip(ticks, labels): @@ -1120,7 +1097,6 @@ def test_time_change_xlim(self): df.plot(ax=ax) # verify tick labels - fig.canvas.draw() ticks = ax.get_xticks() labels = ax.get_xticklabels() for t, l in zip(ticks, labels): @@ -1138,7 +1114,6 @@ def test_time_change_xlim(self): ax.set_xlim('1:30', '5:00') # check tick labels again - fig.canvas.draw() ticks = ax.get_xticks() labels = ax.get_xticklabels() for t, l in zip(ticks, labels): @@ -1165,7 +1140,6 @@ def test_time_musec(self): ax = df.plot(ax=ax) # verify tick labels - fig.canvas.draw() ticks = ax.get_xticks() labels = ax.get_xticklabels() for t, l in zip(ticks, labels): @@ -1432,7 +1406,7 @@ def test_format_timedelta_ticks_narrow(self): df = DataFrame(np.random.randn(len(rng), 3), rng) fig, ax = self.plt.subplots() df.plot(fontsize=2, ax=ax) - fig.canvas.draw() + self.plt.draw() labels = ax.get_xticklabels() result_labels = [x.get_text() for x in labels] @@ -1456,7 +1430,7 @@ def test_format_timedelta_ticks_wide(self): df = DataFrame(np.random.randn(len(rng), 3), rng) fig, ax = self.plt.subplots() ax = df.plot(fontsize=2, ax=ax) - fig.canvas.draw() + self.plt.draw() labels = ax.get_xticklabels() result_labels = [x.get_text() for x in labels] @@ -1529,7 +1503,7 @@ def test_matplotlib_scatter_datetime64(self): df["time"] = date_range("2018-01-01", periods=10, freq="D") fig, ax = self.plt.subplots() ax.scatter(x="time", y="y", data=df) - fig.canvas.draw() + self.plt.draw() label = ax.get_xticklabels()[0] if self.mpl_ge_3_0_0: expected = "2017-12-08" diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 4cc316ffdd7ab..e81a51e99f5a3 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -75,7 +75,7 @@ def safe_import(mod_name, min_version=None): def _skip_if_no_mpl(): mod = safe_import("matplotlib") if mod: - mod.use("Agg", warn=False) + mod.use("Agg", warn=True) else: return True