Skip to content

Commit

Permalink
Matplotlib 3.3 compatibility fixups (#35393)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAugspurger committed Jul 23, 2020
1 parent d3343d9 commit 00ea10c
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 40 deletions.
2 changes: 1 addition & 1 deletion ci/deps/azure-37-locale.yaml
Expand Up @@ -18,7 +18,7 @@ dependencies:
- ipython
- jinja2
- lxml
- matplotlib <3.3.0
- matplotlib>=3.3.0
- moto
- nomkl
- numexpr
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.1.0.rst
Expand Up @@ -274,6 +274,7 @@ change, as ``fsspec`` will still bring in the same packages as before.
Other enhancements
^^^^^^^^^^^^^^^^^^

- Compatibility with matplotlib 3.3.0 (:issue:`34850`)
- :meth:`IntegerArray.astype` now supports ``datetime64`` dtype (:issue:`32538`)
- :class:`IntegerArray` now implements the ``sum`` operation (:issue:`33172`)
- Added :class:`pandas.errors.InvalidIndexError` (:issue:`34570`).
Expand Down
5 changes: 5 additions & 0 deletions pandas/plotting/_matplotlib/boxplot.py
Expand Up @@ -299,6 +299,11 @@ def plot_group(keys, values, ax):
if fontsize is not None:
ax.tick_params(axis="both", labelsize=fontsize)
if kwds.get("vert", 1):
ticks = ax.get_xticks()
if len(ticks) != len(keys):
i, remainder = divmod(len(ticks), len(keys))
assert remainder == 0, remainder
keys *= i
ax.set_xticklabels(keys, rotation=rot)
else:
ax.set_yticklabels(keys, rotation=rot)
Expand Down
1 change: 1 addition & 0 deletions pandas/plotting/_matplotlib/compat.py
Expand Up @@ -21,3 +21,4 @@ def inner():
_mpl_ge_3_0_0 = _mpl_version("3.0.0", operator.ge)
_mpl_ge_3_1_0 = _mpl_version("3.1.0", operator.ge)
_mpl_ge_3_2_0 = _mpl_version("3.2.0", operator.ge)
_mpl_ge_3_3_0 = _mpl_version("3.3.0", operator.ge)
34 changes: 7 additions & 27 deletions pandas/plotting/_matplotlib/converter.py
Expand Up @@ -16,7 +16,6 @@
from pandas._libs.tslibs.offsets import BaseOffset

from pandas.core.dtypes.common import (
is_datetime64_ns_dtype,
is_float,
is_float_dtype,
is_integer,
Expand Down Expand Up @@ -246,19 +245,6 @@ def get_datevalue(date, freq):
raise ValueError(f"Unrecognizable date '{date}'")


def _dt_to_float_ordinal(dt):
"""
Convert :mod:`datetime` to the Gregorian date as UTC float days,
preserving hours, minutes, seconds and microseconds. Return value
is a :func:`float`.
"""
if isinstance(dt, (np.ndarray, Index, Series)) and is_datetime64_ns_dtype(dt):
base = dates.epoch2num(dt.asi8 / 1.0e9)
else:
base = dates.date2num(dt)
return base


# Datetime Conversion
class DatetimeConverter(dates.DateConverter):
@staticmethod
Expand All @@ -274,15 +260,11 @@ def convert(values, unit, axis):
def _convert_1d(values, unit, axis):
def try_parse(values):
try:
return _dt_to_float_ordinal(tools.to_datetime(values))
return dates.date2num(tools.to_datetime(values))
except Exception:
return values

if isinstance(values, (datetime, pydt.date)):
return _dt_to_float_ordinal(values)
elif isinstance(values, np.datetime64):
return _dt_to_float_ordinal(Timestamp(values))
elif isinstance(values, pydt.time):
if isinstance(values, (datetime, pydt.date, np.datetime64, pydt.time)):
return dates.date2num(values)
elif is_integer(values) or is_float(values):
return values
Expand All @@ -303,12 +285,10 @@ def try_parse(values):

try:
values = tools.to_datetime(values)
if isinstance(values, Index):
values = _dt_to_float_ordinal(values)
else:
values = [_dt_to_float_ordinal(x) for x in values]
except Exception:
values = _dt_to_float_ordinal(values)
pass

values = dates.date2num(values)

return values

Expand Down Expand Up @@ -411,8 +391,8 @@ def __call__(self):
interval = self._get_interval()
freq = f"{interval}L"
tz = self.tz.tzname(None)
st = _from_ordinal(dates.date2num(dmin)) # strip tz
ed = _from_ordinal(dates.date2num(dmax))
st = dmin.replace(tzinfo=None)
ed = dmin.replace(tzinfo=None)
all_dates = date_range(start=st, end=ed, freq=freq, tz=tz).astype(object)

try:
Expand Down
6 changes: 2 additions & 4 deletions pandas/tests/plotting/test_converter.py
Expand Up @@ -27,6 +27,7 @@
pass

pytest.importorskip("matplotlib.pyplot")
dates = pytest.importorskip("matplotlib.dates")


def test_registry_mpl_resets():
Expand Down Expand Up @@ -146,7 +147,7 @@ def test_convert_accepts_unicode(self):

def test_conversion(self):
rs = self.dtc.convert(["2012-1-1"], None, None)[0]
xp = datetime(2012, 1, 1).toordinal()
xp = dates.date2num(datetime(2012, 1, 1))
assert rs == xp

rs = self.dtc.convert("2012-1-1", None, None)
Expand All @@ -155,9 +156,6 @@ def test_conversion(self):
rs = self.dtc.convert(date(2012, 1, 1), None, None)
assert rs == xp

rs = self.dtc.convert(datetime(2012, 1, 1).toordinal(), None, None)
assert rs == xp

rs = self.dtc.convert("2012-1-1", None, None)
assert rs == xp

Expand Down
14 changes: 9 additions & 5 deletions pandas/tests/plotting/test_datetimelike.py
Expand Up @@ -331,7 +331,7 @@ def test_freq_with_no_period_alias(self):
bts = tm.makeTimeSeries(5).asfreq(freq)
_, ax = self.plt.subplots()
bts.plot(ax=ax)
assert ax.get_lines()[0].get_xydata()[0, 0] == bts.index[0].toordinal()

idx = ax.get_lines()[0].get_xdata()
msg = "freq not specified and cannot be inferred"
with pytest.raises(ValueError, match=msg):
Expand Down Expand Up @@ -1279,6 +1279,8 @@ def test_mpl_nopandas(self):
@pytest.mark.slow
def test_irregular_ts_shared_ax_xlim(self):
# GH 2960
from pandas.plotting._matplotlib.converter import DatetimeConverter

ts = tm.makeTimeSeries()[:20]
ts_irregular = ts[[1, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, 17, 18]]

Expand All @@ -1289,8 +1291,8 @@ def test_irregular_ts_shared_ax_xlim(self):

# check that axis limits are correct
left, right = ax.get_xlim()
assert left <= ts_irregular.index.min().toordinal()
assert right >= ts_irregular.index.max().toordinal()
assert left <= DatetimeConverter.convert(ts_irregular.index.min(), "", ax)
assert right >= DatetimeConverter.convert(ts_irregular.index.max(), "", ax)

@pytest.mark.slow
def test_secondary_y_non_ts_xlim(self):
Expand Down Expand Up @@ -1345,6 +1347,8 @@ def test_secondary_y_mixed_freq_ts_xlim(self):
@pytest.mark.slow
def test_secondary_y_irregular_ts_xlim(self):
# GH 3490 - irregular-timeseries with secondary y
from pandas.plotting._matplotlib.converter import DatetimeConverter

ts = tm.makeTimeSeries()[:20]
ts_irregular = ts[[1, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, 17, 18]]

Expand All @@ -1356,8 +1360,8 @@ def test_secondary_y_irregular_ts_xlim(self):
ts_irregular[:5].plot(ax=ax)

left, right = ax.get_xlim()
assert left <= ts_irregular.index.min().toordinal()
assert right >= ts_irregular.index.max().toordinal()
assert left <= DatetimeConverter.convert(ts_irregular.index.min(), "", ax)
assert right >= DatetimeConverter.convert(ts_irregular.index.max(), "", ax)

def test_plot_outofbounds_datetime(self):
# 2579 - checking this does not raise
Expand Down
1 change: 1 addition & 0 deletions pandas/tests/plotting/test_frame.py
Expand Up @@ -1563,6 +1563,7 @@ def test_boxplot(self):
ax.xaxis.get_ticklocs(), np.arange(1, len(numeric_cols) + 1)
)
assert len(ax.lines) == self.bp_n_objects * len(numeric_cols)
tm.close()

axes = series.plot.box(rot=40)
self._check_ticks_props(axes, xrot=40, yrot=0)
Expand Down
10 changes: 7 additions & 3 deletions pandas/tests/plotting/test_series.py
Expand Up @@ -274,12 +274,14 @@ def test_rotation(self):
self._check_ticks_props(axes, xrot=30)

def test_irregular_datetime(self):
from pandas.plotting._matplotlib.converter import DatetimeConverter

rng = date_range("1/1/2000", "3/1/2000")
rng = rng[[0, 1, 2, 3, 5, 9, 10, 11, 12]]
ser = Series(randn(len(rng)), rng)
_, ax = self.plt.subplots()
ax = ser.plot(ax=ax)
xp = datetime(1999, 1, 1).toordinal()
xp = DatetimeConverter.convert(datetime(1999, 1, 1), "", ax)
ax.set_xlim("1/1/1999", "1/1/2001")
assert xp == ax.get_xlim()[0]

Expand Down Expand Up @@ -684,11 +686,13 @@ def test_kind_both_ways(self):
kinds = (
plotting.PlotAccessor._common_kinds + plotting.PlotAccessor._series_kinds
)
_, ax = self.plt.subplots()
for kind in kinds:

_, ax = self.plt.subplots()
s.plot(kind=kind, ax=ax)
self.plt.close()
_, ax = self.plt.subplots()
getattr(s.plot, kind)()
self.plt.close()

@pytest.mark.slow
def test_invalid_plot_data(self):
Expand Down

0 comments on commit 00ea10c

Please sign in to comment.