-
-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API: Restore implicit converter registration #18307
Changes from 6 commits
220d554
fba98b1
ba88a04
16c33f1
3766b78
5be9793
9364f60
39602f9
eb6388e
444bc4d
ec7f7e1
58bf2f6
5ee385e
ca6c945
77f76d2
ecf4154
61fbdbb
5e16157
d6798d1
576d244
04d8aa5
f061148
1112b28
897c7ea
2cf0601
ee7b457
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,6 @@ | |
'ipython', | ||
'jinja2' | ||
'lxml', | ||
'matplotlib', | ||
'numexpr', | ||
'openpyxl', | ||
'py', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,50 @@ 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. | ||
|
||
.. code-block:: python | ||
|
||
>>> from pandas.tseries import converter | ||
>>> converter.register() | ||
|
||
This caused problems for some users, so we're temporarily reverting that change; | ||
pandas will again register the converters on import. Using the converters | ||
without explicitly registering the converters will cause a ``FutureWarning``: | ||
|
||
.. code-block:: python | ||
|
||
>>> import pandas as pd | ||
>>> import matplotlib.pyplot as plt | ||
>>> fig, ax = plt.subplots() | ||
>>> ax.plot(pd.Series(range(12), index=pd.date_range('2017', periods=12))) | ||
FutureWarning: 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. | ||
|
||
To register the converters: | ||
>>> from pandas.tseries import converter | ||
>>> converter.register() | ||
|
||
As the error message says, you'll need to register the converters if you intend | ||
to use them with matplotlib plotting functions. Pandas plotting functions, such | ||
as ``Series.plot``, will register them for you calling ``converter.register()`` | ||
first is not necessary. | ||
|
||
Finally, control the formatters, we've added a new option: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "to" control |
||
``pd.options.plotting.matplotlib.register_formatters``. By default, they are | ||
registered. Toggling this to ``False`` removes pandas' formatters (:issue:`18301`) | ||
|
||
.. _whatsnew_0211.enhancements: | ||
|
||
New features | ||
|
@@ -30,7 +74,7 @@ Other Enhancements | |
Deprecations | ||
~~~~~~~~~~~~ | ||
|
||
- | ||
- | ||
- | ||
- | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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_formatters'): | ||
_converter.register(warn=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 | ||
|
||
|
@@ -2059,7 +2057,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) | ||
|
@@ -2155,7 +2153,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, | ||
|
@@ -2289,6 +2287,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) | ||
|
||
|
@@ -2352,7 +2352,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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this one doesn't need to be replaced with _converter.WARN = False ? |
||
_converter._WARN = False | ||
if subplots is True: | ||
naxes = len(grouped) | ||
fig, axes = _subplots(naxes=naxes, squeeze=False, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,9 @@ | |
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 | ||
|
@@ -15,6 +16,75 @@ def test_timtetonum_accepts_unicode(): | |
assert (converter.time2num("00:01") == converter.time2num(u("00:01"))) | ||
|
||
|
||
class TestRegistration(object): | ||
|
||
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 | ||
converter.register() | ||
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_formatters", | ||
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_formatters", | ||
False) | ||
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 | ||
converter.register() | ||
with ctx: | ||
with tm.assert_produces_warning(None) as w: | ||
ax.plot(s.index, s.values) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you did set the option to False, it should not warn? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, forget, that is what is being tested :-) (and they have been registered manually afterwards) Should we test what happens if you do not register manually after you set the option? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean set the option to True or False? I think my latest commit does that. After setting it to False, I think we should not warn even if they don't register manually (which is what my tests checks hopefully) |
||
|
||
assert len(w) == 0 | ||
|
||
|
||
class TestDateTimeConverter(object): | ||
|
||
def setup_method(self, method): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"for you calling" -> "for you, and calling"