diff --git a/doc/source/api.rst b/doc/source/api.rst index 94d7eb5ec8e3b..811301a6bbbca 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -1176,7 +1176,7 @@ Conversion DatetimeIndex.to_datetime DatetimeIndex.to_period DatetimeIndex.to_pydatetime - + DatetimeIndex.to_series GroupBy ------- diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 4a914827fa3aa..e448c96682084 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2033,6 +2033,8 @@ def _sanitize_column(self, key, value): value = com._asarray_tuplesafe(value) elif isinstance(value, PeriodIndex): value = value.asobject + elif isinstance(value, DatetimeIndex): + value = value._to_embed(keep_tz=True).copy() elif value.ndim == 2: value = value.copy().T else: diff --git a/pandas/core/index.py b/pandas/core/index.py index 46e1fef9984f6..3d821f37e41b5 100644 --- a/pandas/core/index.py +++ b/pandas/core/index.py @@ -266,13 +266,28 @@ def copy(self, names=None, name=None, dtype=None, deep=False): new_index = new_index.astype(dtype) return new_index - def to_series(self): + def to_series(self, keep_tz=False): """ - return a series with both index and values equal to the index keys + Create a Series with both index and values equal to the index keys useful with map for returning an indexer based on an index + + Parameters + ---------- + keep_tz : optional, defaults False. + applies only to a DatetimeIndex + + Returns + ------- + Series : dtype will be based on the type of the Index values. """ + import pandas as pd - return pd.Series(self.values, index=self, name=self.name) + values = self._to_embed(keep_tz) + return pd.Series(values, index=self, name=self.name) + + def _to_embed(self, keep_tz=False): + """ return an array repr of this object, potentially casting to object """ + return self.values def astype(self, dtype): return Index(self.values.astype(dtype), name=self.name, diff --git a/pandas/core/series.py b/pandas/core/series.py index 06a6e599840b7..cd5b8ed5e4efd 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -162,7 +162,8 @@ def __init__(self, data=None, index=None, dtype=None, name=None, # need to copy to avoid aliasing issues if name is None: name = data.name - data = data.values + + data = data._to_embed(keep_tz=True) copy = True elif isinstance(data, pa.Array): pass diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 7d3280b78fbf1..af7dc780e88fe 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -2092,6 +2092,33 @@ def test_set_index_cast_datetimeindex(self): idf = df.set_index('A') tm.assert_isinstance(idf.index, DatetimeIndex) + # don't cast a DatetimeIndex WITH a tz, leave as object + # GH 6032 + i = pd.DatetimeIndex(pd.tseries.tools.to_datetime(['2013-1-1 13:00','2013-1-2 14:00'], errors="raise")).tz_localize('US/Pacific') + df = DataFrame(np.random.randn(2,1),columns=['A']) + + expected = Series(i) + self.assertTrue(expected.dtype == object) + self.assertTrue(i.equals(expected.values.values)) + + df['B'] = i + result = df['B'] + assert_series_equal(result, expected) + + result = i.to_series(keep_tz=True) + assert_series_equal(result.reset_index(drop=True), expected) + + df['C'] = i.to_series().reset_index(drop=True) + result = df['C'] + comp = DatetimeIndex(expected.values).copy() + comp.tz = None + self.assert_numpy_array_equal(result.values, comp.values) + + # list of datetimes with a tz + df['D'] = i.to_pydatetime() + result = df['D'] + assert_series_equal(result, expected) + def test_set_index_multiindexcolumns(self): columns = MultiIndex.from_tuples([('foo', 1), ('foo', 2), ('bar', 1)]) df = DataFrame(np.random.randn(3, 3), columns=columns) diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 5831d0ce13c9d..e99c7c270e43c 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -721,6 +721,38 @@ def _get_time_micros(self): values = self._local_timestamps() return tslib.get_time_micros(values) + def to_series(self, keep_tz=False): + """ + Create a Series with both index and values equal to the index keys + useful with map for returning an indexer based on an index + + Parameters + ---------- + keep_tz : optional, defaults False. + return the data keeping the timezone. + + If keep_tz is True: + + If the timezone is not set or is UTC, the resulting + Series will have a datetime64[ns] dtype. + Otherwise the Series will have an object dtype. + + If keep_tz is False: + + Series will have a datetime64[ns] dtype. + + Returns + ------- + Series + """ + return super(DatetimeIndex, self).to_series(keep_tz=keep_tz) + + def _to_embed(self, keep_tz=False): + """ return an array repr of this object, potentially casting to object """ + if keep_tz and self.tz is not None and str(self.tz) != 'UTC': + return self.asobject + return self.values + @property def asobject(self): """