FIX: translate timedeltas in _to_ordinalf by jklymak · Pull Request #12863 · matplotlib/matplotlib · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions lib/matplotlib/artist.py
10 changes: 8 additions & 2 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2526,10 +2526,16 @@ def broken_barh(self, xranges, yrange, **kwargs):
self._process_unit_info(xdata=xdata,
ydata=ydata,
kwargs=kwargs)
xranges = self.convert_xunits(xranges)

xnew = []
for ind in range(len(xranges)):
xr = [[],[]]
xr[0] = self.convert_xunits(xranges[ind][0])
xr[1] = self.convert_xunits_delta(xranges[ind][1])
xnew.append(xr)
yrange = self.convert_yunits(yrange)

col = mcoll.BrokenBarHCollection(xranges, yrange, **kwargs)
col = mcoll.BrokenBarHCollection(xnew, yrange, **kwargs)
self.add_collection(col, autolim=True)
self.autoscale_view()

Expand Down
13 changes: 13 additions & 0 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ def cla(self):
self.reset_ticks()

self.converter = None
self.converter_delta = None
self.units = None
self.set_units(None)
self.stale = True
Expand Down Expand Up @@ -1490,6 +1491,18 @@ def convert_units(self, x):
ret = self.converter.convert(x, self.units, self)
return ret

def convert_units_delta(self, dx):
# If dx is already a number, doesn't need converting
if (munits.ConversionInterface.is_numlike(dx) or
self.converter is None):
return dx

if hasattr(self.converter, 'convert_delta'):
return self.converter.convert_delta(dx, self.units, self)
else:
return self.converter.convert(dx, self.units, self)


def set_units(self, u):
"""
Set the units for axis.
Expand Down
111 changes: 101 additions & 10 deletions lib/matplotlib/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,21 @@ def _to_ordinalf(dt):
return base


def _td_to_ordinalf(dt):
if isinstance(dt, datetime.timedelta):
base = dt / datetime.timedelta(days=1)
return base


# a version of _to_ordinalf that can operate on numpy arrays
_to_ordinalf_np_vectorized = np.vectorize(_to_ordinalf)
# a version of _to_ordinalf that can operate on numpy arrays
_td_to_ordinalf_np_vectorized = np.vectorize(_td_to_ordinalf)


def _td64_to_ordinalf(d):
print('d', d)
return d / np.timedelta64(1, 'D')


def _dt64_to_ordinalf(d):
Expand Down Expand Up @@ -271,6 +284,16 @@ def _dt64_to_ordinalf(d):
return dt


def _dt64_to_ordinalf_iterable(d):
return np.fromiter((_dt64_to_ordinalf(dd) for dd in d),
float, count=len(d))


def _td64_to_ordinalf_iterable(d):
return np.fromiter((_td64_to_ordinalf(dd) for dd in d),
float, count=len(d))


def _from_ordinalf(x, tz=None):
"""
Convert Gregorian float of the date, preserving hours, minutes,
Expand Down Expand Up @@ -405,22 +428,86 @@ def date2num(d):
Gregorian calendar is assumed; this is not universal practice.
For details see the module docstring.
"""

if hasattr(d, "values"):
# this unpacks pandas series or dataframes...
d = d.values
if not np.iterable(d):
if (isinstance(d, np.datetime64) or (isinstance(d, np.ndarray) and
np.issubdtype(d.dtype, np.datetime64))):
return _dt64_to_ordinalf(d)
return _to_ordinalf(d)

else:
d = np.asarray(d)
if np.issubdtype(d.dtype, np.datetime64):
if not np.iterable(d) and not isinstance(d, np.ndarray):
# single value logic...
if (isinstance(d, np.datetime64) or isinstance(d, np.timedelta64)):
return _dt64_to_ordinalf(d)
if not d.size:
return d
else:
return _to_ordinalf(d)

elif (isinstance(d, np.ndarray) and
(np.issubdtype(d.dtype, np.datetime64) or
np.issubdtype(d.dtype, np.timedelta64))):
# array with all one type of datetime64 object.
return _dt64_to_ordinalf(d)

elif len(d):
# this is a list or tuple...
if (isinstance(d[0], np.datetime64) or
isinstance(d[0], np.timedelta64)):
return _dt64_to_ordinalf_iterable(d)
return _to_ordinalf_np_vectorized(d)
elif hasattr(d, 'size') and not d.size:
# this elif doesn't get tested, but leaving here in case anyone
# needs it.
return d
else:
return []


def timedelta2num(d):
"""
Convert datetime objects to Matplotlib dates.

Parameters
----------
d : `datetime.datetime` or `numpy.datetime64` or sequences of these

Returns
-------
float or sequence of floats
Number of days (fraction part represents hours, minutes, seconds, ms)
since 0001-01-01 00:00:00 UTC, plus one.

Notes
-----
The addition of one here is a historical artifact. Also, note that the
Gregorian calendar is assumed; this is not universal practice.
For details see the module docstring.
"""

if hasattr(d, "values"):
# this unpacks pandas series or dataframes...
d = d.values

if not np.iterable(d) and not isinstance(d, np.ndarray):
# single value logic...
if isinstance(d, np.timedelta64):
return _td64_to_ordinalf(d)
else:
return _td_to_ordinalf(d)

elif (isinstance(d, np.ndarray) and
np.issubdtype(d.dtype, np.timedelta64)):
# array with all one type of datetime64 object.
return _td64_to_ordinalf(d)

elif len(d):
# this is a list or tuple...
if isinstance(d[0], np.timedelta64):
return _td64_to_ordinalf_iterable(d)
return _td_to_ordinalf_np_vectorized(d)
elif hasattr(d, 'size') and not d.size:
# this elif doesn't get tested, but leaving here in case anyone
# needs it.
return d
else:
return []


def julian2num(j):
Expand Down Expand Up @@ -1806,6 +1893,10 @@ def convert(value, unit, axis):
"""
return date2num(value)

@staticmethod
def convert_delta(value, units, axis):
return timedelta2num(value)

@staticmethod
def default_units(x, axis):
"""
Expand Down
33 changes: 33 additions & 0 deletions lib/matplotlib/tests/test_dates.py