From ff2217591a9ddcfbe91fa025c0df0e0bc90a39a0 Mon Sep 17 00:00:00 2001 From: sinhrks Date: Sat, 14 Jun 2014 05:03:43 +0900 Subject: [PATCH] ENH/API: offsets now accepts datetime64 --- doc/source/v0.14.1.txt | 2 +- pandas/tseries/offsets.py | 16 +------ pandas/tseries/tests/test_offsets.py | 65 +++++++++++++++------------- vb_suite/timeseries.py | 19 ++++++++ 4 files changed, 57 insertions(+), 45 deletions(-) diff --git a/doc/source/v0.14.1.txt b/doc/source/v0.14.1.txt index cfdef3adb1f34..983dbb61c1288 100644 --- a/doc/source/v0.14.1.txt +++ b/doc/source/v0.14.1.txt @@ -126,7 +126,7 @@ Enhancements - +- All offsets ``apply``, ``rollforward`` and ``rollback`` can now handle ``np.datetime64``, previously results in ``ApplyTypeError`` (:issue:`7452`) diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 9cbef50f2d82f..91ae91e92f3c3 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -45,6 +45,8 @@ def wrapper(self, other): return tslib.NaT if type(other) == date: other = datetime(other.year, other.month, other.day) + elif isinstance(other, np.datetime64): + other = as_timestamp(other) result = func(self, other) @@ -555,20 +557,6 @@ def apply(self, other): return as_timestamp(result) - elif isinstance(other, np.datetime64): - date_in = other - np_day = date_in.astype('datetime64[D]') - np_time = date_in - np_day - - np_incr_dt = np.busday_offset(np_day, self.n, roll=roll, - busdaycal=self.busdaycalendar) - result = np_incr_dt + np_time - - if self.offset: - result = result + self.offset - - return as_timestamp(result) - elif isinstance(other, (timedelta, Tick)): return BDay(self.n, offset=self.offset + other, normalize=self.normalize) diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py index 4fc7d281bc473..ac7a8ae410429 100644 --- a/pandas/tseries/tests/test_offsets.py +++ b/pandas/tseries/tests/test_offsets.py @@ -222,19 +222,20 @@ def _check_nanofunc_works(self, offset, funcname, dt, expected): self.assertEqual(func(t1), expected) def test_apply(self): - dt = datetime(2011, 1, 1, 9, 0) + sdt = datetime(2011, 1, 1, 9, 0) + ndt = np.datetime64('2011-01-01 09:00Z') for offset in self.offset_types: - expected = self.expecteds[offset.__name__] - - if offset == Nano: - self._check_nanofunc_works(offset, 'apply', dt, expected) - else: - self._check_offsetfunc_works(offset, 'apply', dt, expected) + for dt in [sdt, ndt]: + expected = self.expecteds[offset.__name__] + if offset == Nano: + self._check_nanofunc_works(offset, 'apply', dt, expected) + else: + self._check_offsetfunc_works(offset, 'apply', dt, expected) - expected = Timestamp(expected.date()) - self._check_offsetfunc_works(offset, 'apply', dt, expected, - normalize=True) + expected = Timestamp(expected.date()) + self._check_offsetfunc_works(offset, 'apply', dt, expected, + normalize=True) def test_rollforward(self): expecteds = self.expecteds.copy() @@ -261,17 +262,19 @@ def test_rollforward(self): 'Micro': Timestamp('2011-01-01 00:00:00')} norm_expected.update(normalized) - dt = datetime(2011, 1, 1, 9, 0) - for offset in self.offset_types: - expected = expecteds[offset.__name__] + sdt = datetime(2011, 1, 1, 9, 0) + ndt = np.datetime64('2011-01-01 09:00Z') - if offset == Nano: - self._check_nanofunc_works(offset, 'rollforward', dt, expected) - else: - self._check_offsetfunc_works(offset, 'rollforward', dt, expected) - expected = norm_expected[offset.__name__] - self._check_offsetfunc_works(offset, 'rollforward', dt, expected, - normalize=True) + for offset in self.offset_types: + for dt in [sdt, ndt]: + expected = expecteds[offset.__name__] + if offset == Nano: + self._check_nanofunc_works(offset, 'rollforward', dt, expected) + else: + self._check_offsetfunc_works(offset, 'rollforward', dt, expected) + expected = norm_expected[offset.__name__] + self._check_offsetfunc_works(offset, 'rollforward', dt, expected, + normalize=True) def test_rollback(self): expecteds = {'BusinessDay': Timestamp('2010-12-31 09:00:00'), @@ -315,18 +318,20 @@ def test_rollback(self): 'Micro': Timestamp('2011-01-01 00:00:00')} norm_expected.update(normalized) - dt = datetime(2011, 1, 1, 9, 0) - for offset in self.offset_types: - expected = expecteds[offset.__name__] + sdt = datetime(2011, 1, 1, 9, 0) + ndt = np.datetime64('2011-01-01 09:00Z') - if offset == Nano: - self._check_nanofunc_works(offset, 'rollback', dt, expected) - else: - self._check_offsetfunc_works(offset, 'rollback', dt, expected) + for offset in self.offset_types: + for dt in [sdt, ndt]: + expected = expecteds[offset.__name__] + if offset == Nano: + self._check_nanofunc_works(offset, 'rollback', dt, expected) + else: + self._check_offsetfunc_works(offset, 'rollback', dt, expected) - expected = norm_expected[offset.__name__] - self._check_offsetfunc_works(offset, 'rollback', - dt, expected, normalize=True) + expected = norm_expected[offset.__name__] + self._check_offsetfunc_works(offset, 'rollback', + dt, expected, normalize=True) def test_onOffset(self): diff --git a/vb_suite/timeseries.py b/vb_suite/timeseries.py index a3d4d4c7d40a5..2b63eeaf99550 100644 --- a/vb_suite/timeseries.py +++ b/vb_suite/timeseries.py @@ -285,14 +285,33 @@ def date_range(start=None, end=None, periods=None, freq=None): setup = common_setup + """ import datetime as dt import pandas as pd +import numpy as np date = dt.datetime(2011,1,1) +dt64 = np.datetime64('2011-01-01 09:00Z') + +day = pd.offsets.Day() +year = pd.offsets.YearBegin() cday = pd.offsets.CustomBusinessDay() cme = pd.offsets.CustomBusinessMonthEnd() """ +timeseries_day_incr = Benchmark("date + day",setup) + +timeseries_day_apply = Benchmark("day.apply(date)",setup) + +timeseries_year_incr = Benchmark("date + year",setup) + +timeseries_year_apply = Benchmark("year.apply(date)",setup) + timeseries_custom_bday_incr = \ Benchmark("date + cday",setup) +timeseries_custom_bday_apply = \ + Benchmark("cday.apply(date)",setup) + +timeseries_custom_bday_apply_dt64 = \ + Benchmark("cday.apply(dt64)",setup) + # Increment by n timeseries_custom_bday_incr_n = \ Benchmark("date + 10 * cday",setup)