From def22c43f4a96b1bac21296908e47f310c008cb4 Mon Sep 17 00:00:00 2001 From: jreback Date: Wed, 23 Oct 2013 19:49:52 -0400 Subject: [PATCH] ENH: allow astype conversions for timedeltas to other timedelta freqs (still returns a float), related to GH4521 --- doc/source/release.rst | 2 +- doc/source/timeseries.rst | 6 ++++-- doc/source/v0.13.0.txt | 4 +++- pandas/core/common.py | 9 +++++++++ pandas/tests/test_series.py | 14 +++++++++++--- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/doc/source/release.rst b/doc/source/release.rst index 7dc6876d35124..5db612059685b 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -118,7 +118,7 @@ Improvements to existing features by an integer series (:issue`4521`) - A Series of dtype ``timedelta64[ns]`` can now be divided by another ``timedelta64[ns]`` object to yield a ``float64`` dtyped Series. This - is frequency conversion. + is frequency conversion; astyping is also supported. - Timedelta64 support ``fillna/ffill/bfill`` with an integer interpreted as seconds, or a ``timedelta`` (:issue:`3371`) - Box numeric ops on ``timedelta`` Series (:issue:`4984`) diff --git a/doc/source/timeseries.rst b/doc/source/timeseries.rst index 875ba2de93956..83e26d83a9363 100644 --- a/doc/source/timeseries.rst +++ b/doc/source/timeseries.rst @@ -1285,8 +1285,8 @@ It can also construct Series. **frequency conversion** -Timedeltas can be converted to other 'frequencies' by dividing by another timedelta. -These operations yield ``float64`` dtyped Series. +Timedeltas can be converted to other 'frequencies' by dividing by another timedelta, +or by astyping to a specific timedelta type. These operations yield ``float64`` dtyped Series. .. ipython:: python @@ -1297,9 +1297,11 @@ These operations yield ``float64`` dtyped Series. # to days td / np.timedelta64(1,'D') + td.astype('timedelta64[D]') # to seconds td / np.timedelta64(1,'s') + td.astype('timedelta64[s]') Dividing or multiplying a ``timedelta64[ns]`` Series by an integer or integer Series yields another ``timedelta64[ns]`` dtypes Series. diff --git a/doc/source/v0.13.0.txt b/doc/source/v0.13.0.txt index 02f231170ab97..de94d08602750 100644 --- a/doc/source/v0.13.0.txt +++ b/doc/source/v0.13.0.txt @@ -357,7 +357,7 @@ Enhancements to_timedelta(np.arange(5),unit='d') A Series of dtype ``timedelta64[ns]`` can now be divided by another - ``timedelta64[ns]`` object to yield a ``float64`` dtyped Series. This + ``timedelta64[ns]`` object, or astyped to yield a ``float64`` dtyped Series. This is frequency conversion. See :ref:`the docs` for the docs. .. ipython:: python @@ -370,9 +370,11 @@ Enhancements # to days td / np.timedelta64(1,'D') + td.astype('timedelta64[D]') # to seconds td / np.timedelta64(1,'s') + td.astype('timedelta64[s]') Dividing or multiplying a ``timedelta64[ns]`` Series by an integer or integer Series diff --git a/pandas/core/common.py b/pandas/core/common.py index bacd759ee4fd5..d9e8f4164adb4 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -2043,7 +2043,16 @@ def _astype_nansafe(arr, dtype, copy=True): # in py3, timedelta64[ns] are int64 elif (compat.PY3 and dtype not in [_INT64_DTYPE,_TD_DTYPE]) or (not compat.PY3 and dtype != _TD_DTYPE): + + # allow frequency conversions + if dtype.kind == 'm': + mask = isnull(arr) + result = arr.astype(dtype).astype(np.float64) + result[mask] = np.nan + return result + raise TypeError("cannot astype a timedelta from [%s] to [%s]" % (arr.dtype,dtype)) + return arr.astype(_TD_DTYPE) elif (np.issubdtype(arr.dtype, np.floating) and np.issubdtype(dtype, np.integer)): diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index 9292dba651421..6988c81b181de 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -2128,9 +2128,9 @@ def test_constructor_dtype_timedelta64(self): for i in range(3)] + [np.nan ], dtype='m8[ns]' ) self.assert_(td.dtype == 'timedelta64[ns]') - # invalid astypes - for t in ['s', 'D', 'us', 'ms']: - self.assertRaises(TypeError, td.astype, 'm8[%s]' % t) + # these are frequency conversion astypes + #for t in ['s', 'D', 'us', 'ms']: + # self.assertRaises(TypeError, td.astype, 'm8[%s]' % t) # valid astype td.astype('int64') @@ -2371,10 +2371,18 @@ def test_timedelta64_conversions(self): for m in [1, 3, 10]: for unit in ['D','h','m','s','ms','us','ns']: + + # op expected = s1.apply(lambda x: x / np.timedelta64(m,unit)) result = s1 / np.timedelta64(m,unit) assert_series_equal(result, expected) + if m == 1 and unit != 'ns': + + # astype + result = s1.astype("timedelta64[{0}]".format(unit)) + assert_series_equal(result, expected) + # reverse op expected = s1.apply(lambda x: np.timedelta64(m,unit) / x) result = np.timedelta64(m,unit) / s1