-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Matplotlib 3.3 compatibility fixups #35393
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
Changes from all commits
555d343
dcd1c60
981b596
c1f96ab
946f6a9
fd5ca68
5f8cfaf
ce772b1
8015010
0d8b445
0718ae7
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 |
---|---|---|
|
@@ -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, | ||
|
@@ -246,19 +245,6 @@ def get_datevalue(date, freq): | |
raise ValueError(f"Unrecognizable date '{date}'") | ||
|
||
|
||
def _dt_to_float_ordinal(dt): | ||
TomAugspurger marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
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 | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
||
|
@@ -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) | ||
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 the comment is correct that this is really about stripping the tz, then this is cleaner since it doesn't rely on matplotlib's Datetime <-> numeric conversion. |
||
ed = dmin.replace(tzinfo=None) | ||
all_dates = date_range(start=st, end=ed, freq=freq, tz=tz).astype(object) | ||
|
||
try: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() | ||
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 test, and some similar, are invalid now. The relationship between the plotted xy data and ordinals isn't necessarily stable / valuable. |
||
|
||
idx = ax.get_lines()[0].get_xdata() | ||
msg = "freq not specified and cannot be inferred" | ||
with pytest.raises(ValueError, match=msg): | ||
|
@@ -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]] | ||
|
||
|
@@ -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): | ||
|
@@ -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]] | ||
|
||
|
@@ -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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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] | ||
|
||
|
@@ -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() | ||
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. Some strange interaction / issue when plotting on existing axes. IIRC it's from MPL being stricter about things. I didn't investigate too much. 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. Its preferable to keep the reference to the figure and axes and then reuse, rather than depend on the implicit ability of |
||
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): | ||
|
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.
This comes from a change in MPL being stricter about
set_xticks
. This worked for matplotlib 3.2We set 2 x ticklabels despite there being 4.
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.
Yes, this was a deliberate change... matplotlib/matplotlib#17266 The obvious problem if you only give us 2 tick labels is that its ambiguous which of the 4 ticks you wanted labeled.
MPL now has categorical axes which I think were added because pandas has them. Probably too big a project to homogenize, but....