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

Skip to content

Commit f8344a1

Browse files
committed
Fix support for datetime axes in 2d plots.
Fixes the error raisesd when 2d datetime coordinates are passed to contour/contourf and the errors when 1d or 2d datetime coordinates are passed to pcolor/pcolormesh.
1 parent 954b752 commit f8344a1

File tree

10 files changed

+160
-3
lines changed

10 files changed

+160
-3
lines changed

CHANGELOG

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
2013-07-12 Added support for datetime axes to 2d plots. Axis values are passed
2+
through Axes.convert_xunits/Axes.convert_yunits before being used by
3+
contour/contourf, pcolormesh and pcolor.
4+
5+
2013-07-12 Allowed matplotlib.dates.date2num, matplotlib.dates.num2date,
6+
and matplotlib.dates.datestr2num to accept n-d inputs. Also
7+
factored in support for n-d arrays to matplotlib.dates.DateConverter
8+
and matplotlib.units.Registry.
9+
110
2013-06-26 Refactored the axes module: the axes module is now a folder,
211
containing the following submodule:
312
- _subplots.py, containing all the subplots helper methods

doc/users/whats_new.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,32 @@ revision, see the :ref:`github-stats`.
1818
.. contents:: Table of Contents
1919
:depth: 3
2020

21+
.. _whats-new-1-4:
22+
23+
new in matplotlib-1.4
24+
=====================
25+
26+
New plotting features
27+
---------------------
28+
29+
Support for datetime axes in 2d plots
30+
`````````````````````````````````````
31+
Andrew Dawson added support for datetime axes to
32+
:func:`~matplotlib.pyplot.contour`, :func:`~matplotlib.pyplot.contourf`,
33+
:func:`~matplotlib.pyplot.pcolormesh` and :func:`~matplotlib.pyplot.pcolor`.
34+
35+
36+
Date handling
37+
-------------
38+
39+
n-d array support for date conversion
40+
``````````````````````````````````````
41+
Andrew Dawson added support for n-d array handling to
42+
:func:`matplotlib.dates.num2date`, :func:`matplotlib.dates.date2num`
43+
and :func:`matplotlib.dates.datestr2num`. Support is also added to the unit
44+
conversion interfaces :class:`matplotlib.dates.DateConverter` and
45+
:class:`matplotlib.units.Registry`.
46+
2147
.. _whats-new-1-3:
2248

2349
new in matplotlib-1.3

lib/matplotlib/axes/_axes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4472,10 +4472,16 @@ def pcolor(self, *args, **kwargs):
44724472
X, Y, C = self._pcolorargs('pcolor', *args, allmatch=False)
44734473
Ny, Nx = X.shape
44744474

4475+
# unit conversion allows e.g. datetime objects as axis values
4476+
self._process_unit_info(xdata=X, ydata=Y, kwargs=kwargs)
4477+
X = self.convert_xunits(X)
4478+
Y = self.convert_yunits(Y)
4479+
44754480
# convert to MA, if necessary.
44764481
C = ma.asarray(C)
44774482
X = ma.asarray(X)
44784483
Y = ma.asarray(Y)
4484+
44794485
mask = ma.getmaskarray(X) + ma.getmaskarray(Y)
44804486
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
44814487
mask[0:-1, 1:] + mask[1:, 0:-1])
@@ -4668,6 +4674,11 @@ def pcolormesh(self, *args, **kwargs):
46684674
X = X.ravel()
46694675
Y = Y.ravel()
46704676

4677+
# unit conversion allows e.g. datetime objects as axis values
4678+
self._process_unit_info(xdata=X, ydata=Y, kwargs=kwargs)
4679+
X = self.convert_xunits(X)
4680+
Y = self.convert_yunits(Y)
4681+
46714682
coords = np.zeros(((Nx * Ny), 2), dtype=float)
46724683
coords[:, 0] = X
46734684
coords[:, 1] = Y

lib/matplotlib/dates.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ def _to_ordinalf(dt):
203203
return base
204204

205205

206+
# a version of _to_ordinalf that can operate on numpy arrays
207+
_to_ordinalf_np_vectorized = np.vectorize(_to_ordinalf)
208+
209+
206210
def _from_ordinalf(x, tz=None):
207211
"""
208212
Convert Gregorian float of the date, preserving hours, minutes,
@@ -229,6 +233,10 @@ def _from_ordinalf(x, tz=None):
229233
return dt
230234

231235

236+
# a version of _from_ordinalf that can operate on numpy arrays
237+
_from_ordinalf_np_vectorized = np.vectorize(_from_ordinalf)
238+
239+
232240
class strpdate2num:
233241
"""
234242
Use this class to parse date strings to matplotlib datenums when
@@ -246,6 +254,10 @@ def __call__(self, s):
246254
return date2num(datetime.datetime(*time.strptime(s, self.fmt)[:6]))
247255

248256

257+
# a version of dateutil.parser.parse that can operate on nump0y arrays
258+
_dateutil_parser_parse_np_vectorized = np.vectorize(dateutil.parser.parse)
259+
260+
249261
def datestr2num(d):
250262
"""
251263
Convert a date string to a datenum using
@@ -256,7 +268,10 @@ def datestr2num(d):
256268
dt = dateutil.parser.parse(d)
257269
return date2num(dt)
258270
else:
259-
return date2num([dateutil.parser.parse(s) for s in d])
271+
d = np.asarray(d)
272+
if not d.size:
273+
return d
274+
return date2num(_dateutil_parser_parse_np_vectorized(d))
260275

261276

262277
def date2num(d):
@@ -273,7 +288,10 @@ def date2num(d):
273288
if not cbook.iterable(d):
274289
return _to_ordinalf(d)
275290
else:
276-
return np.asarray([_to_ordinalf(val) for val in d])
291+
d = np.asarray(d)
292+
if not d.size:
293+
return d
294+
return _to_ordinalf_np_vectorized(d)
277295

278296

279297
def julian2num(j):
@@ -310,7 +328,10 @@ def num2date(x, tz=None):
310328
if not cbook.iterable(x):
311329
return _from_ordinalf(x, tz)
312330
else:
313-
return [_from_ordinalf(val, tz) for val in x]
331+
x = np.asarray(x)
332+
if not x.size:
333+
return x
334+
return _from_ordinalf_np_vectorized(x, tz).tolist()
314335

315336

316337
def drange(dstart, dend, delta):
@@ -1292,6 +1313,9 @@ def convert(value, unit, axis):
12921313
@staticmethod
12931314
def default_units(x, axis):
12941315
'Return the tzinfo instance of *x* or of its first element, or None'
1316+
if isinstance(x, np.ndarray):
1317+
x = x.ravel()
1318+
12951319
try:
12961320
x = x[0]
12971321
except (TypeError, IndexError):

lib/matplotlib/tests/test_axes.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from nose.tools import assert_equal
22
from nose.tools import assert_raises
3+
import datetime
34

45
import numpy as np
56
from numpy import ma
@@ -663,6 +664,59 @@ def test_pcolormesh():
663664
ax = fig.add_subplot(133)
664665
ax.pcolormesh(Qx,Qz,Z, shading="gouraud")
665666

667+
668+
@image_comparison(baseline_images=['pcolormesh_datetime_axis'],
669+
extensions=['png'], remove_text=False)
670+
def test_pcolormesh_datetime_axis():
671+
fig = plt.figure()
672+
fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15)
673+
base = datetime.datetime(2013, 1, 1)
674+
x = np.array([base + datetime.timedelta(days=d) for d in range(21)])
675+
y = np.arange(21)
676+
z1, z2 = np.meshgrid(np.arange(20), np.arange(20))
677+
z = z1 * z2
678+
plt.subplot(221)
679+
plt.pcolormesh(x[:-1], y[:-1], z)
680+
plt.subplot(222)
681+
plt.pcolormesh(x, y, z)
682+
x = np.repeat(x[np.newaxis], 21, axis=0)
683+
y = np.repeat(y[:, np.newaxis], 21, axis=1)
684+
plt.subplot(223)
685+
plt.pcolormesh(x[:-1, :-1], y[:-1, :-1], z)
686+
plt.subplot(224)
687+
plt.pcolormesh(x, y, z)
688+
for ax in fig.get_axes():
689+
for label in ax.get_xticklabels():
690+
label.set_ha('right')
691+
label.set_rotation(30)
692+
693+
694+
@image_comparison(baseline_images=['pcolor_datetime_axis'],
695+
extensions=['png'], remove_text=False)
696+
def test_pcolor_datetime_axis():
697+
fig = plt.figure()
698+
fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15)
699+
base = datetime.datetime(2013, 1, 1)
700+
x = np.array([base + datetime.timedelta(days=d) for d in range(21)])
701+
y = np.arange(21)
702+
z1, z2 = np.meshgrid(np.arange(20), np.arange(20))
703+
z = z1 * z2
704+
plt.subplot(221)
705+
plt.pcolor(x[:-1], y[:-1], z)
706+
plt.subplot(222)
707+
plt.pcolor(x, y, z)
708+
x = np.repeat(x[np.newaxis], 21, axis=0)
709+
y = np.repeat(y[:, np.newaxis], 21, axis=1)
710+
plt.subplot(223)
711+
plt.pcolor(x[:-1, :-1], y[:-1, :-1], z)
712+
plt.subplot(224)
713+
plt.pcolor(x, y, z)
714+
for ax in fig.get_axes():
715+
for label in ax.get_xticklabels():
716+
label.set_ha('right')
717+
label.set_rotation(30)
718+
719+
666720
def test_pcolorargs():
667721
n = 12
668722
x = np.linspace(-1.5, 1.5, n)

lib/matplotlib/tests/test_contour.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import datetime
2+
13
import numpy as np
24

35
from matplotlib.testing.decorators import cleanup, image_comparison
@@ -177,6 +179,32 @@ def test_given_colors_levels_and_extends():
177179
plt.colorbar()
178180

179181

182+
@image_comparison(baseline_images=['contour_datetime_axis'],
183+
extensions=['png'], remove_text=False)
184+
def test_contour_datetime_axis():
185+
fig = plt.figure()
186+
fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15)
187+
base = datetime.datetime(2013, 1, 1)
188+
x = np.array([base + datetime.timedelta(days=d) for d in range(20)])
189+
y = np.arange(20)
190+
z1, z2 = np.meshgrid(np.arange(20), np.arange(20))
191+
z = z1 * z2
192+
plt.subplot(221)
193+
plt.contour(x, y, z)
194+
plt.subplot(222)
195+
plt.contourf(x, y, z)
196+
x = np.repeat(x[np.newaxis], 20, axis=0)
197+
y = np.repeat(y[:, np.newaxis], 20, axis=1)
198+
plt.subplot(223)
199+
plt.contour(x, y, z)
200+
plt.subplot(224)
201+
plt.contourf(x, y, z)
202+
for ax in fig.get_axes():
203+
for label in ax.get_xticklabels():
204+
label.set_ha('right')
205+
label.set_rotation(30)
206+
207+
180208
if __name__ == '__main__':
181209
import nose
182210
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

lib/matplotlib/units.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def default_units(x, axis):
4444
"""
4545
from __future__ import print_function
4646
from matplotlib.cbook import iterable, is_numlike
47+
import numpy as np
4748

4849

4950
class AxisInfo:
@@ -132,6 +133,10 @@ def get_converter(self, x):
132133
if classx is not None:
133134
converter = self.get(classx)
134135

136+
if isinstance(x, np.ndarray) and x.size:
137+
converter = self.get_converter(x.ravel()[0])
138+
return converter
139+
135140
if converter is None and iterable(x):
136141
for thisx in x:
137142
# Make sure that recursing might actually lead to a solution,

0 commit comments

Comments
 (0)