Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a32aa1b

Browse files
committed
ENH: support np.datenum64 in dates.py
1 parent 905465b commit a32aa1b

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

lib/matplotlib/dates.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,30 @@ def _to_ordinalf(dt):
243243
_to_ordinalf_np_vectorized = np.vectorize(_to_ordinalf)
244244

245245

246+
def _dt64_to_ordinalf(d):
247+
"""
248+
Convert `numpy.datetime64` or an ndarray of those types to Gregorian
249+
date as UTC float. Roundoff is via float64 precision. Practically:
250+
microseconds for dates between 290301 BC, 294241 AD, milliseconds for
251+
larger dates (see `numpy.datetime64`). Nanoseconds aren't possible
252+
because we do times compared to ``0001-01-01T00:00:00`` (plus one day).
253+
"""
254+
255+
zerod = False
256+
if isinstance(d, np.datetime64):
257+
zerod = True
258+
d = np.atleast_1d(d)
259+
extra = d - d.astype('datetime64[s]')
260+
extra = extra.astype('timedelta64[ns]')
261+
t0 = np.datetime64('0001-01-01T00:00:00').astype('datetime64[s]')
262+
dt = (d.astype('datetime64[s]') - t0).astype(np.float64)
263+
dt += extra.astype(np.float64) / 1.0e9
264+
dt = dt / SEC_PER_DAY + 1.0
265+
if zerod:
266+
dt = dt[0]
267+
return dt
268+
269+
246270
def _from_ordinalf(x, tz=None):
247271
"""
248272
Convert Gregorian float of the date, preserving hours, minutes,
@@ -354,7 +378,7 @@ def date2num(d):
354378
355379
Parameters
356380
----------
357-
d : :class:`datetime` or sequence of :class:`datetime`
381+
d : :class:`datetime` or `datenum64`, or sequences
358382
359383
Returns
360384
-------
@@ -368,6 +392,10 @@ def date2num(d):
368392
Gregorian calendar is assumed; this is not universal practice.
369393
For details see the module docstring.
370394
"""
395+
396+
if ((isinstance(d, np.ndarray) and np.issubdtype(d.dtype, np.datetime64))
397+
or isinstance(d, np.datetime64)):
398+
return _dt64_to_ordinalf(d)
371399
if not cbook.iterable(d):
372400
return _to_ordinalf(d)
373401
else:
@@ -488,8 +516,8 @@ def drange(dstart, dend, delta):
488516
*dend* are :class:`datetime` instances. *delta* is a
489517
:class:`datetime.timedelta` instance.
490518
"""
491-
f1 = _to_ordinalf(dstart)
492-
f2 = _to_ordinalf(dend)
519+
f1 = date2num(dstart)
520+
f2 = date2num(dend)
493521
step = delta.total_seconds() / SEC_PER_DAY
494522

495523
# calculate the difference between dend and dstart in times of delta
@@ -504,7 +532,7 @@ def drange(dstart, dend, delta):
504532
dinterval_end -= delta
505533
num -= 1
506534

507-
f2 = _to_ordinalf(dinterval_end) # new float-endpoint
535+
f2 = date2num(dinterval_end) # new float-endpoint
508536
return np.linspace(f1, f2, num + 1)
509537

510538
### date tickers and formatters ###
@@ -1630,5 +1658,6 @@ def default_units(x, axis):
16301658
return None
16311659

16321660

1661+
units.registry[np.datetime64] = DateConverter()
16331662
units.registry[datetime.date] = DateConverter()
16341663
units.registry[datetime.datetime] = DateConverter()

lib/matplotlib/tests/test_dates.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33

44
from six.moves import map
55

6-
import datetime
7-
import warnings
8-
import tempfile
9-
import pytest
106

7+
import datetime
118
import dateutil
9+
import numpy as np
10+
import pytest
1211
import pytz
12+
import tempfile
13+
import warnings
1314

1415
try:
1516
# mock in python 3.3+
@@ -22,6 +23,52 @@
2223
import matplotlib.dates as mdates
2324

2425

26+
def test_date_numpyx():
27+
# test that numpy dates work properly...
28+
base = datetime.datetime(2017, 1, 1)
29+
time = [base + datetime.timedelta(days=x) for x in range(0, 3)]
30+
timenp = np.array(time, dtype='datetime64[ns]')
31+
data = np.array([0., 2., 1.])
32+
fig = plt.figure(figsize=(10, 2))
33+
ax = fig.add_subplot(1, 1, 1)
34+
h, = ax.plot(time, data)
35+
hnp, = ax.plot(timenp, data)
36+
assert np.array_equal(h.get_xdata(orig=False), hnp.get_xdata(orig=False))
37+
fig = plt.figure(figsize=(10, 2))
38+
ax = fig.add_subplot(1, 1, 1)
39+
h, = ax.plot(data, time)
40+
hnp, = ax.plot(data, timenp)
41+
assert np.array_equal(h.get_ydata(orig=False), hnp.get_ydata(orig=False))
42+
43+
44+
def test_date_date2num_numpy():
45+
t0 = datetime.datetime(2017, 1, 1, 0, 1, 1)
46+
time = mdates.date2num(t0)
47+
tnp = np.array(t0, dtype='datetime64[s]')
48+
nptime = mdates.date2num(tnp)
49+
assert time == nptime
50+
tnp = np.array(t0, dtype='datetime64[us]')
51+
nptime = mdates.date2num(tnp)
52+
assert time == nptime
53+
tnp = np.array(t0, dtype='datetime64[ns]')
54+
nptime = mdates.date2num(tnp)
55+
assert time == nptime
56+
t0 = [datetime.datetime(2017, 1, 1, 0, 1, 1),
57+
datetime.datetime(2017, 1, 1, 1, 1, 1)]
58+
tnp = np.array(t0, dtype='datetime64[ms]')
59+
time = mdates.date2num(t0)
60+
nptime = mdates.date2num(tnp)
61+
assert np.array_equal(time, nptime)
62+
t0 = [[datetime.datetime(2017, 1, 1, 0, 1, 1),
63+
datetime.datetime(2017, 1, 1, 1, 1, 1)],
64+
[datetime.datetime(2017, 1, 1, 2, 1, 1),
65+
datetime.datetime(2017, 1, 1, 3, 1, 1)]]
66+
tnp = np.array(t0, dtype='datetime64[ms]')
67+
time = mdates.date2num(t0)
68+
nptime = mdates.date2num(tnp)
69+
assert np.array_equal(time, nptime)
70+
71+
2572
@image_comparison(baseline_images=['date_empty'], extensions=['png'])
2673
def test_date_empty():
2774
# make sure mpl does the right thing when told to plot dates even

0 commit comments

Comments
 (0)