From c64b56bc1357d7036b689c5787311d0386d0a53b Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Mon, 9 Jan 2017 19:40:25 -0500 Subject: [PATCH] BUG: use more generic type inference for fast plotting xref #15073 --- doc/source/whatsnew/v0.20.0.txt | 1 + pandas/tests/types/test_dtypes.py | 11 +++++--- pandas/tests/types/test_inference.py | 36 +++++++++++++++++++++++++ pandas/tseries/tests/test_converter.py | 23 ++++++++++++++-- pandas/tseries/tests/test_timeseries.py | 2 +- pandas/tseries/tools.py | 16 +++++------ pandas/types/common.py | 7 +++-- 7 files changed, 80 insertions(+), 16 deletions(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index c82dc370e3e71..a13b97266811a 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -248,6 +248,7 @@ Other API Changes - ``pd.read_csv()`` will now issue a ``ParserWarning`` whenever there are conflicting values provided by the ``dialect`` parameter and the user (:issue:`14898`) - ``pd.read_csv()`` will now raise a ``ValueError`` for the C engine if the quote character is larger than than one byte (:issue:`11592`) - ``inplace`` arguments now require a boolean value, else a ``ValueError`` is thrown (:issue:`14189`) +- ``pandas.api.types.is_datetime64_ns_dtype`` will now report ``True`` on a tz-aware dtype, similar to ``pandas.api.types.is_datetime64_any_dtype`` .. _whatsnew_0200.deprecations: diff --git a/pandas/tests/types/test_dtypes.py b/pandas/tests/types/test_dtypes.py index a2b0a9ebfa6cc..f190c85404ff9 100644 --- a/pandas/tests/types/test_dtypes.py +++ b/pandas/tests/types/test_dtypes.py @@ -11,7 +11,8 @@ is_datetime64tz_dtype, is_datetimetz, is_period_dtype, is_period, is_dtype_equal, is_datetime64_ns_dtype, - is_datetime64_dtype, is_string_dtype, + is_datetime64_dtype, + is_datetime64_any_dtype, is_string_dtype, _coerce_to_dtype) import pandas.util.testing as tm @@ -132,8 +133,12 @@ def test_coerce_to_dtype(self): DatetimeTZDtype('ns', 'Asia/Tokyo')) def test_compat(self): - self.assertFalse(is_datetime64_ns_dtype(self.dtype)) - self.assertFalse(is_datetime64_ns_dtype('datetime64[ns, US/Eastern]')) + self.assertTrue(is_datetime64tz_dtype(self.dtype)) + self.assertTrue(is_datetime64tz_dtype('datetime64[ns, US/Eastern]')) + self.assertTrue(is_datetime64_any_dtype(self.dtype)) + self.assertTrue(is_datetime64_any_dtype('datetime64[ns, US/Eastern]')) + self.assertTrue(is_datetime64_ns_dtype(self.dtype)) + self.assertTrue(is_datetime64_ns_dtype('datetime64[ns, US/Eastern]')) self.assertFalse(is_datetime64_dtype(self.dtype)) self.assertFalse(is_datetime64_dtype('datetime64[ns, US/Eastern]')) diff --git a/pandas/tests/types/test_inference.py b/pandas/tests/types/test_inference.py index 8180757d9e706..5c35112d0fe19 100644 --- a/pandas/tests/types/test_inference.py +++ b/pandas/tests/types/test_inference.py @@ -22,6 +22,10 @@ from pandas.types import inference from pandas.types.common import (is_timedelta64_dtype, is_timedelta64_ns_dtype, + is_datetime64_dtype, + is_datetime64_ns_dtype, + is_datetime64_any_dtype, + is_datetime64tz_dtype, is_number, is_integer, is_float, @@ -805,6 +809,38 @@ def test_is_float(self): self.assertFalse(is_float(np.timedelta64(1, 'D'))) self.assertFalse(is_float(Timedelta('1 days'))) + def test_is_datetime_dtypes(self): + + ts = pd.date_range('20130101', periods=3) + tsa = pd.date_range('20130101', periods=3, tz='US/Eastern') + + self.assertTrue(is_datetime64_dtype('datetime64')) + self.assertTrue(is_datetime64_dtype('datetime64[ns]')) + self.assertTrue(is_datetime64_dtype(ts)) + self.assertFalse(is_datetime64_dtype(tsa)) + + self.assertFalse(is_datetime64_ns_dtype('datetime64')) + self.assertTrue(is_datetime64_ns_dtype('datetime64[ns]')) + self.assertTrue(is_datetime64_ns_dtype(ts)) + self.assertTrue(is_datetime64_ns_dtype(tsa)) + + self.assertTrue(is_datetime64_any_dtype('datetime64')) + self.assertTrue(is_datetime64_any_dtype('datetime64[ns]')) + self.assertTrue(is_datetime64_any_dtype(ts)) + self.assertTrue(is_datetime64_any_dtype(tsa)) + + self.assertFalse(is_datetime64tz_dtype('datetime64')) + self.assertFalse(is_datetime64tz_dtype('datetime64[ns]')) + self.assertFalse(is_datetime64tz_dtype(ts)) + self.assertTrue(is_datetime64tz_dtype(tsa)) + + for tz in ['US/Eastern', 'UTC']: + dtype = 'datetime64[ns, {}]'.format(tz) + self.assertFalse(is_datetime64_dtype(dtype)) + self.assertTrue(is_datetime64tz_dtype(dtype)) + self.assertTrue(is_datetime64_ns_dtype(dtype)) + self.assertTrue(is_datetime64_any_dtype(dtype)) + def test_is_timedelta(self): self.assertTrue(is_timedelta64_dtype('timedelta64')) self.assertTrue(is_timedelta64_dtype('timedelta64[ns]')) diff --git a/pandas/tseries/tests/test_converter.py b/pandas/tseries/tests/test_converter.py index 37d9c35639c32..f6cf11c871bba 100644 --- a/pandas/tseries/tests/test_converter.py +++ b/pandas/tseries/tests/test_converter.py @@ -3,10 +3,10 @@ import nose import numpy as np -from pandas import Timestamp, Period +from pandas import Timestamp, Period, Index from pandas.compat import u import pandas.util.testing as tm -from pandas.tseries.offsets import Second, Milli, Micro +from pandas.tseries.offsets import Second, Milli, Micro, Day from pandas.compat.numpy import np_datetime64_compat try: @@ -62,6 +62,25 @@ def test_conversion(self): np_datetime64_compat('2012-01-02 00:00:00+0000')]), None, None) self.assertEqual(rs[0], xp) + # we have a tz-aware date (constructed to that when we turn to utc it + # is the same as our sample) + ts = (Timestamp('2012-01-01') + .tz_localize('UTC') + .tz_convert('US/Eastern') + ) + rs = self.dtc.convert(ts, None, None) + self.assertEqual(rs, xp) + + rs = self.dtc.convert(ts.to_pydatetime(), None, None) + self.assertEqual(rs, xp) + + rs = self.dtc.convert(Index([ts - Day(1), ts]), None, None) + self.assertEqual(rs[1], xp) + + rs = self.dtc.convert(Index([ts - Day(1), ts]).to_pydatetime(), + None, None) + self.assertEqual(rs[1], xp) + def test_conversion_float(self): decimals = 9 diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py index 1834c56e59bb9..b7e77c2eb770a 100644 --- a/pandas/tseries/tests/test_timeseries.py +++ b/pandas/tseries/tests/test_timeseries.py @@ -2404,7 +2404,7 @@ def test_to_datetime_tz_psycopg2(self): i = pd.DatetimeIndex([ '2000-01-01 08:00:00+00:00' ], tz=psycopg2.tz.FixedOffsetTimezone(offset=-300, name=None)) - self.assertFalse(is_datetime64_ns_dtype(i)) + self.assertTrue(is_datetime64_ns_dtype(i)) # tz coerceion result = pd.to_datetime(i, errors='coerce') diff --git a/pandas/tseries/tools.py b/pandas/tseries/tools.py index 21e1c9744aa88..f746409aadfc9 100644 --- a/pandas/tseries/tools.py +++ b/pandas/tseries/tools.py @@ -309,7 +309,14 @@ def _convert_listlike(arg, box, format, name=None, tz=tz): arg = np.array(arg, dtype='O') # these are shortcutable - if is_datetime64_ns_dtype(arg): + if is_datetime64tz_dtype(arg): + if not isinstance(arg, DatetimeIndex): + return DatetimeIndex(arg, tz=tz, name=name) + if utc: + arg = arg.tz_convert(None).tz_localize('UTC') + return arg + + elif is_datetime64_ns_dtype(arg): if box and not isinstance(arg, DatetimeIndex): try: return DatetimeIndex(arg, tz=tz, name=name) @@ -318,13 +325,6 @@ def _convert_listlike(arg, box, format, name=None, tz=tz): return arg - elif is_datetime64tz_dtype(arg): - if not isinstance(arg, DatetimeIndex): - return DatetimeIndex(arg, tz=tz, name=name) - if utc: - arg = arg.tz_convert(None).tz_localize('UTC') - return arg - elif unit is not None: if format is not None: raise ValueError("cannot specify both format and unit") diff --git a/pandas/types/common.py b/pandas/types/common.py index 96eb6d6968bfb..e58e0826ea49a 100644 --- a/pandas/types/common.py +++ b/pandas/types/common.py @@ -187,8 +187,11 @@ def is_datetime64_ns_dtype(arr_or_dtype): try: tipo = _get_dtype(arr_or_dtype) except TypeError: - return False - return tipo == _NS_DTYPE + if is_datetime64tz_dtype(arr_or_dtype): + tipo = _get_dtype(arr_or_dtype.dtype) + else: + return False + return tipo == _NS_DTYPE or getattr(tipo, 'base', None) == _NS_DTYPE def is_timedelta64_ns_dtype(arr_or_dtype):