diff --git a/ci/check_imports.py b/ci/check_imports.py
index a83436e7d258c..d6f24ebcc4d3e 100644
--- a/ci/check_imports.py
+++ b/ci/check_imports.py
@@ -9,7 +9,6 @@
'ipython',
'jinja2'
'lxml',
- 'matplotlib',
'numexpr',
'openpyxl',
'py',
diff --git a/doc/source/api.rst b/doc/source/api.rst
index f3405fcdee608..5044d35de703b 100644
--- a/doc/source/api.rst
+++ b/doc/source/api.rst
@@ -2375,6 +2375,17 @@ Style Export and Import
Styler.use
Styler.to_excel
+Plotting
+~~~~~~~~
+
+.. currentmodule:: pandas
+
+.. autosummary::
+ :toctree: generated/
+
+ plotting.register_matplotlib_converters
+ plotting.deregister_matplotlib_converters
+
.. currentmodule:: pandas
General utility functions
diff --git a/doc/source/options.rst b/doc/source/options.rst
index 2da55a5a658a4..be3a3d9a55534 100644
--- a/doc/source/options.rst
+++ b/doc/source/options.rst
@@ -273,164 +273,166 @@ Options are 'right', and 'left'.
Available Options
-----------------
-=================================== ============ ==================================
-Option Default Function
-=================================== ============ ==================================
-display.chop_threshold None If set to a float value, all float
- values smaller then the given
- threshold will be displayed as
- exactly 0 by repr and friends.
-display.colheader_justify right Controls the justification of
- column headers. used by DataFrameFormatter.
-display.column_space 12 No description available.
-display.date_dayfirst False When True, prints and parses dates
- with the day first, eg 20/01/2005
-display.date_yearfirst False When True, prints and parses dates
- with the year first, eg 2005/01/20
-display.encoding UTF-8 Defaults to the detected encoding
- of the console. Specifies the encoding
- to be used for strings returned by
- to_string, these are generally strings
- meant to be displayed on the console.
-display.expand_frame_repr True Whether to print out the full DataFrame
- repr for wide DataFrames across
- multiple lines, `max_columns` is
- still respected, but the output will
- wrap-around across multiple "pages"
- if its width exceeds `display.width`.
-display.float_format None The callable should accept a floating
- point number and return a string with
- the desired format of the number.
- This is used in some places like
- SeriesFormatter.
- See core.format.EngFormatter for an example.
-display.large_repr truncate For DataFrames exceeding max_rows/max_cols,
- the repr (and HTML repr) can show
- a truncated table (the default),
- or switch to the view from df.info()
- (the behaviour in earlier versions of pandas).
- allowable settings, ['truncate', 'info']
-display.latex.repr False Whether to produce a latex DataFrame
- representation for jupyter frontends
- that support it.
-display.latex.escape True Escapes special characters in DataFrames, when
- using the to_latex method.
-display.latex.longtable False Specifies if the to_latex method of a DataFrame
- uses the longtable format.
-display.latex.multicolumn True Combines columns when using a MultiIndex
-display.latex.multicolumn_format 'l' Alignment of multicolumn labels
-display.latex.multirow False Combines rows when using a MultiIndex.
- Centered instead of top-aligned,
- separated by clines.
-display.max_columns 20 max_rows and max_columns are used
- in __repr__() methods to decide if
- to_string() or info() is used to
- render an object to a string. In
- case python/IPython is running in
- a terminal this can be set to 0 and
- pandas will correctly auto-detect
- the width the terminal and swap to
- a smaller format in case all columns
- would not fit vertically. The IPython
- notebook, IPython qtconsole, or IDLE
- do not run in a terminal and hence
- it is not possible to do correct
- auto-detection. 'None' value means
- unlimited.
-display.max_colwidth 50 The maximum width in characters of
- a column in the repr of a pandas
- data structure. When the column overflows,
- a "..." placeholder is embedded in
- the output.
-display.max_info_columns 100 max_info_columns is used in DataFrame.info
- method to decide if per column information
- will be printed.
-display.max_info_rows 1690785 df.info() will usually show null-counts
- for each column. For large frames
- this can be quite slow. max_info_rows
- and max_info_cols limit this null
- check only to frames with smaller
- dimensions then specified.
-display.max_rows 60 This sets the maximum number of rows
- pandas should output when printing
- out various output. For example,
- this value determines whether the
- repr() for a dataframe prints out
- fully or just a summary repr.
- 'None' value means unlimited.
-display.max_seq_items 100 when pretty-printing a long sequence,
- no more then `max_seq_items` will
- be printed. If items are omitted,
- they will be denoted by the addition
- of "..." to the resulting string.
- If set to None, the number of items
- to be printed is unlimited.
-display.memory_usage True This specifies if the memory usage of
- a DataFrame should be displayed when the
- df.info() method is invoked.
-display.multi_sparse True "Sparsify" MultiIndex display (don't
- display repeated elements in outer
- levels within groups)
-display.notebook_repr_html True When True, IPython notebook will
- use html representation for
- pandas objects (if it is available).
-display.pprint_nest_depth 3 Controls the number of nested levels
- to process when pretty-printing
-display.precision 6 Floating point output precision in
- terms of number of places after the
- decimal, for regular formatting as well
- as scientific notation. Similar to
- numpy's ``precision`` print option
-display.show_dimensions truncate Whether to print out dimensions
- at the end of DataFrame repr.
- If 'truncate' is specified, only
- print out the dimensions if the
- frame is truncated (e.g. not display
- all rows and/or columns)
-display.width 80 Width of the display in characters.
- In case python/IPython is running in
- a terminal this can be set to None
- and pandas will correctly auto-detect
- the width. Note that the IPython notebook,
- IPython qtconsole, or IDLE do not run in a
- terminal and hence it is not possible
- to correctly detect the width.
-display.html.table_schema False Whether to publish a Table Schema
- representation for frontends that
- support it.
-display.html.border 1 A ``border=value`` attribute is
- inserted in the ``
`` tag
- for the DataFrame HTML repr.
-io.excel.xls.writer xlwt The default Excel writer engine for
- 'xls' files.
-io.excel.xlsm.writer openpyxl The default Excel writer engine for
- 'xlsm' files. Available options:
- 'openpyxl' (the default).
-io.excel.xlsx.writer openpyxl The default Excel writer engine for
- 'xlsx' files.
-io.hdf.default_format None default format writing format, if
- None, then put will default to
- 'fixed' and append will default to
- 'table'
-io.hdf.dropna_table True drop ALL nan rows when appending
- to a table
-io.parquet.engine None The engine to use as a default for
- parquet reading and writing. If None
- then try 'pyarrow' and 'fastparquet'
-mode.chained_assignment warn Raise an exception, warn, or no
- action if trying to use chained
- assignment, The default is warn
-mode.sim_interactive False Whether to simulate interactive mode
- for purposes of testing.
-mode.use_inf_as_na False True means treat None, NaN, -INF,
- INF as NA (old way), False means
- None and NaN are null, but INF, -INF
- are not NA (new way).
-compute.use_bottleneck True Use the bottleneck library to accelerate
- computation if it is installed.
-compute.use_numexpr True Use the numexpr library to accelerate
- computation if it is installed.
-=================================== ============ ==================================
+======================================= ============ ==================================
+Option Default Function
+======================================= ============ ==================================
+display.chop_threshold None If set to a float value, all float
+ values smaller then the given
+ threshold will be displayed as
+ exactly 0 by repr and friends.
+display.colheader_justify right Controls the justification of
+ column headers. used by DataFrameFormatter.
+display.column_space 12 No description available.
+display.date_dayfirst False When True, prints and parses dates
+ with the day first, eg 20/01/2005
+display.date_yearfirst False When True, prints and parses dates
+ with the year first, eg 2005/01/20
+display.encoding UTF-8 Defaults to the detected encoding
+ of the console. Specifies the encoding
+ to be used for strings returned by
+ to_string, these are generally strings
+ meant to be displayed on the console.
+display.expand_frame_repr True Whether to print out the full DataFrame
+ repr for wide DataFrames across
+ multiple lines, `max_columns` is
+ still respected, but the output will
+ wrap-around across multiple "pages"
+ if its width exceeds `display.width`.
+display.float_format None The callable should accept a floating
+ point number and return a string with
+ the desired format of the number.
+ This is used in some places like
+ SeriesFormatter.
+ See core.format.EngFormatter for an example.
+display.large_repr truncate For DataFrames exceeding max_rows/max_cols,
+ the repr (and HTML repr) can show
+ a truncated table (the default),
+ or switch to the view from df.info()
+ (the behaviour in earlier versions of pandas).
+ allowable settings, ['truncate', 'info']
+display.latex.repr False Whether to produce a latex DataFrame
+ representation for jupyter frontends
+ that support it.
+display.latex.escape True Escapes special characters in DataFrames, when
+ using the to_latex method.
+display.latex.longtable False Specifies if the to_latex method of a DataFrame
+ uses the longtable format.
+display.latex.multicolumn True Combines columns when using a MultiIndex
+display.latex.multicolumn_format 'l' Alignment of multicolumn labels
+display.latex.multirow False Combines rows when using a MultiIndex.
+ Centered instead of top-aligned,
+ separated by clines.
+display.max_columns 20 max_rows and max_columns are used
+ in __repr__() methods to decide if
+ to_string() or info() is used to
+ render an object to a string. In
+ case python/IPython is running in
+ a terminal this can be set to 0 and
+ pandas will correctly auto-detect
+ the width the terminal and swap to
+ a smaller format in case all columns
+ would not fit vertically. The IPython
+ notebook, IPython qtconsole, or IDLE
+ do not run in a terminal and hence
+ it is not possible to do correct
+ auto-detection. 'None' value means
+ unlimited.
+display.max_colwidth 50 The maximum width in characters of
+ a column in the repr of a pandas
+ data structure. When the column overflows,
+ a "..." placeholder is embedded in
+ the output.
+display.max_info_columns 100 max_info_columns is used in DataFrame.info
+ method to decide if per column information
+ will be printed.
+display.max_info_rows 1690785 df.info() will usually show null-counts
+ for each column. For large frames
+ this can be quite slow. max_info_rows
+ and max_info_cols limit this null
+ check only to frames with smaller
+ dimensions then specified.
+display.max_rows 60 This sets the maximum number of rows
+ pandas should output when printing
+ out various output. For example,
+ this value determines whether the
+ repr() for a dataframe prints out
+ fully or just a summary repr.
+ 'None' value means unlimited.
+display.max_seq_items 100 when pretty-printing a long sequence,
+ no more then `max_seq_items` will
+ be printed. If items are omitted,
+ they will be denoted by the addition
+ of "..." to the resulting string.
+ If set to None, the number of items
+ to be printed is unlimited.
+display.memory_usage True This specifies if the memory usage of
+ a DataFrame should be displayed when the
+ df.info() method is invoked.
+display.multi_sparse True "Sparsify" MultiIndex display (don't
+ display repeated elements in outer
+ levels within groups)
+display.notebook_repr_html True When True, IPython notebook will
+ use html representation for
+ pandas objects (if it is available).
+display.pprint_nest_depth 3 Controls the number of nested levels
+ to process when pretty-printing
+display.precision 6 Floating point output precision in
+ terms of number of places after the
+ decimal, for regular formatting as well
+ as scientific notation. Similar to
+ numpy's ``precision`` print option
+display.show_dimensions truncate Whether to print out dimensions
+ at the end of DataFrame repr.
+ If 'truncate' is specified, only
+ print out the dimensions if the
+ frame is truncated (e.g. not display
+ all rows and/or columns)
+display.width 80 Width of the display in characters.
+ In case python/IPython is running in
+ a terminal this can be set to None
+ and pandas will correctly auto-detect
+ the width. Note that the IPython notebook,
+ IPython qtconsole, or IDLE do not run in a
+ terminal and hence it is not possible
+ to correctly detect the width.
+display.html.table_schema False Whether to publish a Table Schema
+ representation for frontends that
+ support it.
+display.html.border 1 A ``border=value`` attribute is
+ inserted in the ```` tag
+ for the DataFrame HTML repr.
+io.excel.xls.writer xlwt The default Excel writer engine for
+ 'xls' files.
+io.excel.xlsm.writer openpyxl The default Excel writer engine for
+ 'xlsm' files. Available options:
+ 'openpyxl' (the default).
+io.excel.xlsx.writer openpyxl The default Excel writer engine for
+ 'xlsx' files.
+io.hdf.default_format None default format writing format, if
+ None, then put will default to
+ 'fixed' and append will default to
+ 'table'
+io.hdf.dropna_table True drop ALL nan rows when appending
+ to a table
+io.parquet.engine None The engine to use as a default for
+ parquet reading and writing. If None
+ then try 'pyarrow' and 'fastparquet'
+mode.chained_assignment warn Raise an exception, warn, or no
+ action if trying to use chained
+ assignment, The default is warn
+mode.sim_interactive False Whether to simulate interactive mode
+ for purposes of testing.
+mode.use_inf_as_na False True means treat None, NaN, -INF,
+ INF as NA (old way), False means
+ None and NaN are null, but INF, -INF
+ are not NA (new way).
+compute.use_bottleneck True Use the bottleneck library to accelerate
+ computation if it is installed.
+compute.use_numexpr True Use the numexpr library to accelerate
+ computation if it is installed.
+plotting.matplotlib.register_converters True Register custom converters with
+ matplotlib. Set to False to de-register.
+======================================= ============ ========================================
.. _basics.console_output:
diff --git a/doc/source/whatsnew/v0.21.1.txt b/doc/source/whatsnew/v0.21.1.txt
index e307e605687bf..55efbae68d8ef 100644
--- a/doc/source/whatsnew/v0.21.1.txt
+++ b/doc/source/whatsnew/v0.21.1.txt
@@ -7,6 +7,36 @@ This is a minor release from 0.21.1 and includes a number of deprecations, new
features, enhancements, and performance improvements along with a large number
of bug fixes. We recommend that all users upgrade to this version.
+.. _whatsnew_0211.special:
+
+Restore Matplotlib datetime Converter Registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pandas implements some matplotlib converters for nicely formatting the axis
+labels on plots with ``datetime`` or ``Period`` values. Prior to pandas 0.21.0,
+these were implicitly registered with matplotlib, as a side effect of ``import
+pandas``.
+
+In pandas 0.21.0, we required users to explicitly register the
+converter. This caused problems for some users who relied on those converters
+being present for regular ``matplotlib.pyplot`` plotting methods, so we're
+temporarily reverting that change; pandas will again register the converters on
+import.
+
+We've added a new option to control the converters:
+``pd.options.plotting.matplotlib.register_converters``. By default, they are
+registered. Toggling this to ``False`` removes pandas' formatters and restore
+any converters we overwrote when registering them (:issue:`18301`).
+
+We're working with the matplotlib developers to make this easier. We're trying
+to balance user convenience (automatically registering the converters) with
+import performance and best practices (importing pandas shouldn't have the side
+effect of overwriting any custom converters you've already set). In the future
+we hope to have most of the datetime formatting functionality in matplotlib,
+with just the pandas-specific converters in pandas. We'll then gracefully
+deprecate the automatic registration of converters in favor of users explicitly
+registering them when they want them.
+
.. _whatsnew_0211.enhancements:
New features
@@ -30,9 +60,8 @@ Other Enhancements
Deprecations
~~~~~~~~~~~~
--
--
--
+- ``pandas.tseries.register`` has been renamed to
+ :func:`pandas.plotting.register_matplotlib_converters`` (:issue:`18301`)
.. _whatsnew_0211.performance:
diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py
index 59578b96807e1..c3307c60b8ed9 100644
--- a/pandas/core/config_init.py
+++ b/pandas/core/config_init.py
@@ -480,3 +480,29 @@ def use_inf_as_na_cb(key):
cf.register_option(
'engine', 'auto', parquet_engine_doc,
validator=is_one_of_factory(['auto', 'pyarrow', 'fastparquet']))
+
+# --------
+# Plotting
+# ---------
+
+register_converter_doc = """
+: bool
+ Whether to register converters with matplotlib's units registry for
+ dates, times, datetimes, and Periods. Toggling to False will remove
+ the converters, restoring any converters that pandas overwrote.
+"""
+
+
+def register_converter_cb(key):
+ from pandas.plotting import register_matplotlib_converters
+ from pandas.plotting import deregister_matplotlib_converters
+
+ if cf.get_option(key):
+ register_matplotlib_converters()
+ else:
+ deregister_matplotlib_converters()
+
+
+with cf.config_prefix("plotting.matplotlib"):
+ cf.register_option("register_converters", True, register_converter_doc,
+ validator=bool, cb=register_converter_cb)
diff --git a/pandas/plotting/__init__.py b/pandas/plotting/__init__.py
index 8f98e297e3e66..385d4d7f047c7 100644
--- a/pandas/plotting/__init__.py
+++ b/pandas/plotting/__init__.py
@@ -11,3 +11,10 @@
from pandas.plotting._core import boxplot
from pandas.plotting._style import plot_params
from pandas.plotting._tools import table
+try:
+ from pandas.plotting._converter import \
+ register as register_matplotlib_converters
+ from pandas.plotting._converter import \
+ deregister as deregister_matplotlib_converters
+except ImportError:
+ pass
diff --git a/pandas/plotting/_converter.py b/pandas/plotting/_converter.py
index 9daee918b9f30..10b1d26a5aa77 100644
--- a/pandas/plotting/_converter.py
+++ b/pandas/plotting/_converter.py
@@ -1,3 +1,4 @@
+import warnings
from datetime import datetime, timedelta
import datetime as pydt
import numpy as np
@@ -45,14 +46,96 @@
MUSEC_PER_DAY = 1e6 * SEC_PER_DAY
+_WARN = True # Global for whether pandas has registered the units explicitly
+_mpl_units = {} # Cache for units overwritten by us
-def register():
- units.registry[lib.Timestamp] = DatetimeConverter()
- units.registry[Period] = PeriodConverter()
- units.registry[pydt.datetime] = DatetimeConverter()
- units.registry[pydt.date] = DatetimeConverter()
- units.registry[pydt.time] = TimeConverter()
- units.registry[np.datetime64] = DatetimeConverter()
+
+def get_pairs():
+ pairs = [
+ (lib.Timestamp, DatetimeConverter),
+ (Period, PeriodConverter),
+ (pydt.datetime, DatetimeConverter),
+ (pydt.date, DatetimeConverter),
+ (pydt.time, TimeConverter),
+ (np.datetime64, DatetimeConverter),
+ ]
+ return pairs
+
+
+def register(explicit=True):
+ """Register Pandas Formatters and Converters with matplotlib
+
+ This function modifies the global ``matplotlib.units.registry``
+ dictionary. Pandas adds custom converters for
+
+ * pd.Timestamp
+ * pd.Period
+ * np.datetime64
+ * datetime.datetime
+ * datetime.date
+ * datetime.time
+
+ See Also
+ --------
+ deregister_matplotlib_converter
+ """
+ # Renamed in pandas.plotting.__init__
+ global _WARN
+
+ if explicit:
+ _WARN = False
+
+ pairs = get_pairs()
+ for type_, cls in pairs:
+ converter = cls()
+ if type_ in units.registry:
+ previous = units.registry[type_]
+ _mpl_units[type_] = previous
+ units.registry[type_] = converter
+
+
+def deregister():
+ """Remove pandas' formatters and converters
+
+ Removes the custom converters added by :func:`register`. This
+ attempts to set the state of the registry back to the state before
+ pandas registered its own units. Converters for pandas' own types like
+ Timestamp and Period are removed completely. Converters for types
+ pandas overwrites, like ``datetime.datetime``, are restored to their
+ original value.
+
+ See Also
+ --------
+ deregister_matplotlib_converters
+ """
+ # Renamed in pandas.plotting.__init__
+ for type_, cls in get_pairs():
+ # We use type to catch our classes directly, no inheritance
+ if type(units.registry.get(type_)) is cls:
+ units.registry.pop(type_)
+
+ # restore the old keys
+ for unit, formatter in _mpl_units.items():
+ if type(formatter) not in {DatetimeConverter, PeriodConverter,
+ TimeConverter}:
+ # make it idempotent by excluding ours.
+ units.registry[unit] = formatter
+
+
+def _check_implicitly_registered():
+ global _WARN
+
+ if _WARN:
+ msg = ("Using an implicitly registered datetime converter for a "
+ "matplotlib plotting method. The converter was registered "
+ "by pandas on import. Future versions of pandas will require "
+ "you to explicitly register matplotlib converters.\n\n"
+ "To register the converters:\n\t"
+ ">>> from pandas.plotting import register_matplotlib_converters"
+ "\n\t"
+ ">>> register_matplotlib_converters()")
+ warnings.warn(msg, FutureWarning)
+ _WARN = False
def _to_ordinalf(tm):
@@ -190,6 +273,7 @@ class DatetimeConverter(dates.DateConverter):
@staticmethod
def convert(values, unit, axis):
# values might be a 1-d array, or a list-like of arrays.
+ _check_implicitly_registered()
if is_nested_list_like(values):
values = [DatetimeConverter._convert_1d(v, unit, axis)
for v in values]
@@ -274,6 +358,7 @@ class PandasAutoDateLocator(dates.AutoDateLocator):
def get_locator(self, dmin, dmax):
'Pick the best locator based on a distance.'
+ _check_implicitly_registered()
delta = relativedelta(dmax, dmin)
num_days = (delta.years * 12.0 + delta.months) * 31.0 + delta.days
@@ -315,6 +400,7 @@ def get_unit_generic(freq):
def __call__(self):
# if no data have been set, this will tank with a ValueError
+ _check_implicitly_registered()
try:
dmin, dmax = self.viewlim_to_dt()
except ValueError:
@@ -917,6 +1003,8 @@ def _get_default_locs(self, vmin, vmax):
def __call__(self):
'Return the locations of the ticks.'
# axis calls Locator.set_axis inside set_m_formatter
+ _check_implicitly_registered()
+
vi = tuple(self.axis.get_view_interval())
if vi != self.plot_obj.view_interval:
self.plot_obj.date_axis_info = None
@@ -1001,6 +1089,8 @@ def set_locs(self, locs):
'Sets the locations of the ticks'
# don't actually use the locs. This is just needed to work with
# matplotlib. Force to use vmin, vmax
+ _check_implicitly_registered()
+
self.locs = locs
(vmin, vmax) = vi = tuple(self.axis.get_view_interval())
@@ -1012,6 +1102,8 @@ def set_locs(self, locs):
self._set_default_format(vmin, vmax)
def __call__(self, x, pos=0):
+ _check_implicitly_registered()
+
if self.formatdict is None:
return ''
else:
@@ -1042,6 +1134,7 @@ def format_timedelta_ticks(x, pos, n_decimals):
return s
def __call__(self, x, pos=0):
+ _check_implicitly_registered()
(vmin, vmax) = tuple(self.axis.get_view_interval())
n_decimals = int(np.ceil(np.log10(100 * 1e9 / (vmax - vmin))))
if n_decimals > 9:
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
index af8fc78f7d4a7..adaaa206edadd 100644
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -11,6 +11,7 @@
from pandas.util._decorators import cache_readonly
from pandas.core.base import PandasObject
+from pandas.core.config import get_option
from pandas.core.dtypes.missing import isna, notna, remove_na_arraylike
from pandas.core.dtypes.common import (
is_list_like,
@@ -40,16 +41,13 @@
_get_xlim, _set_ticks_props,
format_date_labels)
-_registered = False
-
-
-def _setup():
- # delay the import of matplotlib until nescessary
- global _registered
- if not _registered:
- from pandas.plotting import _converter
- _converter.register()
- _registered = True
+try:
+ from pandas.plotting import _converter
+except ImportError:
+ pass
+else:
+ if get_option('plotting.matplotlib.register_converters'):
+ _converter.register(explicit=True)
def _get_standard_kind(kind):
@@ -99,7 +97,7 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=None,
secondary_y=False, colormap=None,
table=False, layout=None, **kwds):
- _setup()
+ _converter._WARN = False
self.data = data
self.by = by
@@ -2064,7 +2062,7 @@ 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
- _setup()
+ _converter._WARN = False
ax = boxplot(self, column=column, by=by, ax=ax, fontsize=fontsize,
grid=grid, rot=rot, figsize=figsize, layout=layout,
return_type=return_type, **kwds)
@@ -2160,7 +2158,7 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None,
`**kwds` : other plotting keyword arguments
To be passed to hist function
"""
- _setup()
+ _converter._WARN = False
if by is not None:
axes = grouped_hist(data, column=column, by=by, ax=ax, grid=grid,
figsize=figsize, sharex=sharex, sharey=sharey,
@@ -2294,6 +2292,8 @@ def grouped_hist(data, column=None, by=None, ax=None, bins=50, figsize=None,
-------
axes: collection of Matplotlib Axes
"""
+ _converter._WARN = False
+
def plot_group(group, ax):
ax.hist(group.dropna().values, bins=bins, **kwargs)
@@ -2358,7 +2358,7 @@ def boxplot_frame_groupby(grouped, subplots=True, column=None, fontsize=None,
>>> grouped = df.unstack(level='lvl1').groupby(level=0, axis=1)
>>> boxplot_frame_groupby(grouped, subplots=False)
"""
- _setup()
+ _converter._WARN = False
if subplots is True:
naxes = len(grouped)
fig, axes = _subplots(naxes=naxes, squeeze=False,
diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py
index e1f64bed5598d..3818c04649366 100644
--- a/pandas/tests/plotting/test_converter.py
+++ b/pandas/tests/plotting/test_converter.py
@@ -1,20 +1,144 @@
+import subprocess
import pytest
from datetime import datetime, date
import numpy as np
-from pandas import Timestamp, Period, Index
+from pandas import Timestamp, Period, Index, date_range, Series
from pandas.compat import u
+import pandas.core.config as cf
import pandas.util.testing as tm
from pandas.tseries.offsets import Second, Milli, Micro, Day
from pandas.compat.numpy import np_datetime64_compat
converter = pytest.importorskip('pandas.plotting._converter')
+from pandas.plotting import (register_matplotlib_converters,
+ deregister_matplotlib_converters)
def test_timtetonum_accepts_unicode():
assert (converter.time2num("00:01") == converter.time2num(u("00:01")))
+class TestRegistration(object):
+
+ def test_register_by_default(self):
+ # Run in subprocess to ensure a clean state
+ code = ("'import matplotlib.units; "
+ "import pandas as pd; "
+ "units = dict(matplotlib.units.registry); "
+ "assert pd.Timestamp in units)'")
+ call = ['python', '-c', code]
+ assert subprocess.check_call(call) == 0
+
+ def test_warns(self):
+ plt = pytest.importorskip("matplotlib.pyplot")
+ s = Series(range(12), index=date_range('2017', periods=12))
+ _, ax = plt.subplots()
+
+ # Set to the "warning" state, in case this isn't the first test run
+ converter._WARN = True
+ with tm.assert_produces_warning(FutureWarning,
+ check_stacklevel=False) as w:
+ ax.plot(s.index, s.values)
+ plt.close()
+
+ assert len(w) == 1
+ assert "Using an implicitly registered datetime converter" in str(w[0])
+
+ def test_registering_no_warning(self):
+ plt = pytest.importorskip("matplotlib.pyplot")
+ s = Series(range(12), index=date_range('2017', periods=12))
+ _, ax = plt.subplots()
+
+ # Set to the "warn" state, in case this isn't the first test run
+ converter._WARN = True
+ register_matplotlib_converters()
+ with tm.assert_produces_warning(None) as w:
+ ax.plot(s.index, s.values)
+
+ assert len(w) == 0
+
+ def test_pandas_plots_register(self):
+ pytest.importorskip("matplotlib.pyplot")
+ s = Series(range(12), index=date_range('2017', periods=12))
+ # Set to the "warn" state, in case this isn't the first test run
+ converter._WARN = True
+ with tm.assert_produces_warning(None) as w:
+ s.plot()
+
+ assert len(w) == 0
+
+ def test_matplotlib_formatters(self):
+ units = pytest.importorskip("matplotlib.units")
+ assert Timestamp in units.registry
+
+ ctx = cf.option_context("plotting.matplotlib.register_converters",
+ False)
+ with ctx:
+ assert Timestamp not in units.registry
+
+ assert Timestamp in units.registry
+
+ def test_option_no_warning(self):
+ pytest.importorskip("matplotlib.pyplot")
+ ctx = cf.option_context("plotting.matplotlib.register_converters",
+ False)
+ plt = pytest.importorskip("matplotlib.pyplot")
+ s = Series(range(12), index=date_range('2017', periods=12))
+ _, ax = plt.subplots()
+
+ converter._WARN = True
+ # Test without registering first, no warning
+ with ctx:
+ with tm.assert_produces_warning(None) as w:
+ ax.plot(s.index, s.values)
+
+ assert len(w) == 0
+
+ # Now test with registering
+ converter._WARN = True
+ register_matplotlib_converters()
+ with ctx:
+ with tm.assert_produces_warning(None) as w:
+ ax.plot(s.index, s.values)
+
+ assert len(w) == 0
+
+ def test_registry_resets(self):
+ units = pytest.importorskip("matplotlib.units")
+ dates = pytest.importorskip("matplotlib.dates")
+
+ # make a copy, to reset to
+ original = dict(units.registry)
+
+ try:
+ # get to a known state
+ units.registry.clear()
+ date_converter = dates.DateConverter()
+ units.registry[datetime] = date_converter
+ units.registry[date] = date_converter
+
+ register_matplotlib_converters()
+ assert units.registry[date] is not date_converter
+ deregister_matplotlib_converters()
+ assert units.registry[date] is date_converter
+
+ finally:
+ # restore original stater
+ units.registry.clear()
+ for k, v in original.items():
+ units.registry[k] = v
+
+ def test_old_import_warns(self):
+ with tm.assert_produces_warning(FutureWarning) as w:
+ from pandas.tseries import converter
+ converter.register()
+
+ assert len(w)
+ assert ('pandas.plotting.register_matplotlib_converters' in
+ str(w[0].message))
+
+
class TestDateTimeConverter(object):
def setup_method(self, method):
diff --git a/pandas/tseries/converter.py b/pandas/tseries/converter.py
index df603c4d880d8..26d3f3cb85edc 100644
--- a/pandas/tseries/converter.py
+++ b/pandas/tseries/converter.py
@@ -1,6 +1,7 @@
# flake8: noqa
+import warnings
-from pandas.plotting._converter import (register, time2num,
+from pandas.plotting._converter import (time2num,
TimeConverter, TimeFormatter,
PeriodConverter, get_datevalue,
DatetimeConverter,
@@ -9,3 +10,11 @@
MilliSecondLocator, get_finder,
TimeSeries_DateLocator,
TimeSeries_DateFormatter)
+
+
+def register():
+ from pandas.plotting._converter import register as register_
+ msg = ("'pandas.tseries.converter.register' has been moved and renamed to "
+ "'pandas.plotting.register_matplotlib_converters'. ")
+ warnings.warn(msg, FutureWarning, stacklevel=2)
+ register_()