Skip to content

Commit 1475971

Browse files
author
Chang She
committed
ENH: register converters with matplotlib for better datetime convesion
1 parent 4c860de commit 1475971

File tree

3 files changed

+70
-27
lines changed

3 files changed

+70
-27
lines changed

pandas/tools/plotting.py

+33-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# being a bit too dynamic
22
# pylint: disable=E1101
33
from itertools import izip
4+
import datetime
45

56
import numpy as np
67

@@ -13,6 +14,7 @@
1314
from pandas.tseries.frequencies import get_period_alias, get_base_alias
1415
from pandas.tseries.offsets import DateOffset
1516
import pandas.tseries.tools as datetools
17+
import pandas.lib as lib
1618

1719
def _get_standard_kind(kind):
1820
return {'density' : 'kde'}.get(kind, kind)
@@ -573,24 +575,39 @@ def _post_plot_logic(self):
573575
if self.subplots and self.legend:
574576
self.axes[0].legend(loc='best')
575577

576-
class DatetimeConverter(object):
577-
578-
@classmethod
579-
def convert(cls, values, units, axis):
580-
def try_parse(values):
581-
try:
582-
return datetools.to_datetime(values).toordinal()
583-
except Exception:
578+
try:
579+
import matplotlib.units as units
580+
import matplotlib.dates as dates
581+
582+
class DatetimeConverter(dates.DateConverter):
583+
584+
@staticmethod
585+
def convert(values, unit, axis):
586+
def try_parse(values):
587+
try:
588+
return datetools.to_datetime(values).toordinal()
589+
except Exception:
590+
return values
591+
592+
if isinstance(values, (datetime.datetime, datetime.date)):
593+
return values.toordinal()
594+
elif isinstance(values, (datetime.time)):
595+
return dates.date2num(values)
596+
elif (com.is_integer(values) or com.is_float(values)):
584597
return values
585-
586-
if (com.is_integer(values) or
587-
com.is_float(values)):
598+
elif isinstance(values, str):
599+
return try_parse(values)
600+
elif isinstance(values, Index):
601+
return values.map(try_parse)
602+
elif isinstance(values, (list, tuple, np.ndarray)):
603+
return [try_parse(x) for x in values]
588604
return values
589-
elif isinstance(values, str):
590-
return try_parse(values)
591-
elif isinstance(values, Index):
592-
return values.map(try_parse)
593-
return map(try_parse, values)
605+
606+
units.registry[lib.Timestamp] = DatetimeConverter()
607+
units.registry[datetime.date] = DatetimeConverter()
608+
units.registry[datetime.datetime] = DatetimeConverter()
609+
except ImportError:
610+
pass
594611

595612
class LinePlot(MPLPlot):
596613

@@ -648,10 +665,6 @@ def _make_plot(self):
648665
y = np.ma.masked_where(mask, y)
649666
plotf(ax, x, y, style, label=label, **self.kwds)
650667
ax.grid(self.grid)
651-
idx = getattr(self.data, 'index', None)
652-
if isinstance(idx, DatetimeIndex) or (idx is not None and
653-
idx.inferred_type == 'datetime'):
654-
ax.get_xaxis().converter = DatetimeConverter
655668

656669
def _maybe_convert_index(self, data):
657670
# tsplot converts automatically, but don't want to convert index

pandas/tseries/plotting.py

+17-6
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ def tsplot(series, plotf, **kwargs):
7777
ax.freq = freq
7878
xaxis = ax.get_xaxis()
7979
xaxis.freq = freq
80-
xaxis.converter = DateConverter
8180
ax.legendlabels = [kwargs.get('label', None)]
8281
ax.view_interval = None
8382
ax.date_axis_info = None
@@ -739,12 +738,24 @@ def format_dateaxis(subplot, freq):
739738
subplot.xaxis.set_minor_formatter(minformatter)
740739
pylab.draw_if_interactive()
741740

742-
class DateConverter(object):
743741

744-
@classmethod
745-
def convert(cls, values, units, axis):
746-
if isinstance(values, (int, float, str, datetime, Period)):
742+
import matplotlib.units as units
743+
import matplotlib.dates as dates
744+
745+
class PeriodConverter(dates.DateConverter):
746+
747+
@staticmethod
748+
def convert(values, units, axis):
749+
if not hasattr(axis, 'freq'):
750+
raise TypeError('Axis must have `freq` set to convert to Periods')
751+
valid_types = (str, datetime, Period, pydt.date, pydt.time)
752+
if (isinstance(values, valid_types) or com.is_integer(values) or
753+
com.is_float(values)):
747754
return get_datevalue(values, axis.freq)
748755
if isinstance(values, Index):
749756
return values.map(lambda x: get_datevalue(x, axis.freq))
750-
return map(lambda x: get_datevalue(x, axis.freq), values)
757+
if isinstance(values, (list, tuple, np.ndarray)):
758+
return [get_datevalue(x, axis.freq) for x in values]
759+
return values
760+
761+
units.registry[Period] = PeriodConverter()

pandas/tseries/tests/test_plotting.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from datetime import datetime, timedelta
2+
from datetime import datetime, timedelta, date, time
33

44
import unittest
55
import nose
@@ -559,6 +559,25 @@ def test_from_weekly_resampling(self):
559559
for l in ax.get_lines():
560560
self.assert_(l.get_xdata().freq == 'M')
561561

562+
@slow
563+
def test_irreg_dtypes(self):
564+
#date
565+
idx = [date(2000, 1, 1), date(2000, 1, 5), date(2000, 1, 20)]
566+
df = DataFrame(np.random.randn(len(idx), 3), Index(idx, dtype=object))
567+
_check_plot_works(df.plot)
568+
569+
#np.datetime64
570+
idx = date_range('1/1/2000', periods=10)
571+
idx = idx[[0, 2, 5, 9]].asobject
572+
df = DataFrame(np.random.randn(len(idx), 3), idx)
573+
_check_plot_works(df.plot)
574+
575+
#time
576+
inc = Series(np.random.randint(1, 6, 9)).cumsum().values
577+
idx = [time(1, 1, i) for i in inc]
578+
df = DataFrame(np.random.randn(len(idx), 3), idx)
579+
_check_plot_works(df.plot)
580+
562581
PNG_PATH = 'tmp.png'
563582
def _check_plot_works(f, freq=None, series=None, *args, **kwargs):
564583
import matplotlib.pyplot as plt

0 commit comments

Comments
 (0)