Skip to content

BUG: Index may ignore specified datetime/timedelta dtypes #13981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.19.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,7 @@ Bug Fixes
- Bug in ``DatetimeIndex`` with nanosecond frequency does not include timestamp specified with ``end`` (:issue:`13672`)

- Bug in ``Index`` raises ``OutOfBoundsDatetime`` if ``datetime`` exceeds ``datetime64[ns]`` bounds, rather than coercing to ``object`` dtype (:issue:`13663`)
- Bug in ``Index`` may ignores specified ``datetime64`` or ``timedelta64`` ``dtype`` (:issue:`13981`)
- Bug in ``RangeIndex`` can be created without no arguments rather than raises ``TypeError`` (:issue:`13793`)
- Bug in ``.value_counts`` raises ``OutOfBoundsDatetime`` if data exceeds ``datetime64[ns]`` bounds (:issue:`13663`)
- Bug in ``DatetimeIndex`` may raise ``OutOfBoundsDatetime`` if input ``np.datetime64`` has other unit than ``ns`` (:issue:`9114`)
Expand Down
16 changes: 10 additions & 6 deletions pandas/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
from pandas.types.missing import isnull, array_equivalent
from pandas.types.common import (_ensure_int64, _ensure_object,
_ensure_platform_int,
is_datetimetz,
is_integer,
is_float,
is_dtype_equal,
is_object_dtype,
is_categorical_dtype,
is_bool_dtype,
is_integer_dtype, is_float_dtype,
is_datetime64_any_dtype,
is_timedelta64_dtype,
needs_i8_conversion,
is_iterator, is_list_like,
is_scalar)
Expand Down Expand Up @@ -162,16 +163,19 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None,
# index-like
elif isinstance(data, (np.ndarray, Index, ABCSeries)):

if (issubclass(data.dtype.type, np.datetime64) or
is_datetimetz(data)):
if (is_datetime64_any_dtype(data) or
(dtype is not None and is_datetime64_any_dtype(dtype)) or
'tz' in kwargs):
from pandas.tseries.index import DatetimeIndex
result = DatetimeIndex(data, copy=copy, name=name, **kwargs)
if dtype is not None and _o_dtype == dtype:
result = DatetimeIndex(data, copy=copy, name=name,
dtype=dtype, **kwargs)
if dtype is not None and is_dtype_equal(_o_dtype, dtype):
return Index(result.to_pydatetime(), dtype=_o_dtype)
else:
return result

elif issubclass(data.dtype.type, np.timedelta64):
elif (is_timedelta64_dtype(data) or
(dtype is not None and is_timedelta64_dtype(dtype))):
from pandas.tseries.tdi import TimedeltaIndex
result = TimedeltaIndex(data, copy=copy, name=name, **kwargs)
if dtype is not None and _o_dtype == dtype:
Expand Down
5 changes: 1 addition & 4 deletions pandas/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,10 +687,7 @@ def get_level_values(self, level):
labels = self.labels[num]
filled = algos.take_1d(unique.values, labels,
fill_value=unique._na_value)
_simple_new = unique._simple_new
values = _simple_new(filled, name=self.names[num],
freq=getattr(unique, 'freq', None),
tz=getattr(unique, 'tz', None))
values = unique._shallow_copy(filled)
return values

def format(self, space=2, sparsify=None, adjoin=True, names=False,
Expand Down
39 changes: 39 additions & 0 deletions pandas/tests/indexes/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,45 @@ def test_constructor_dtypes(self):
self.assertIsInstance(idx, Index)
self.assertEqual(idx.dtype, object)

def test_constructor_dtypes_datetime(self):

for tz in [None, 'UTC', 'US/Eastern', 'Asia/Tokyo']:
idx = pd.date_range('2011-01-01', periods=5, tz=tz)
dtype = idx.dtype

# pass values without timezone, as DatetimeIndex localizes it
for values in [pd.date_range('2011-01-01', periods=5).values,
pd.date_range('2011-01-01', periods=5).asi8]:

for res in [pd.Index(values, tz=tz),
pd.Index(values, dtype=dtype),
pd.Index(list(values), tz=tz),
pd.Index(list(values), dtype=dtype)]:
tm.assert_index_equal(res, idx)

# check compat with DatetimeIndex
for res in [pd.DatetimeIndex(values, tz=tz),
pd.DatetimeIndex(values, dtype=dtype),
pd.DatetimeIndex(list(values), tz=tz),
pd.DatetimeIndex(list(values), dtype=dtype)]:
tm.assert_index_equal(res, idx)

def test_constructor_dtypes_timedelta(self):

idx = pd.timedelta_range('1 days', periods=5)
dtype = idx.dtype

for values in [idx.values, idx.asi8]:

for res in [pd.Index(values, dtype=dtype),
pd.Index(list(values), dtype=dtype)]:
tm.assert_index_equal(res, idx)

# check compat with TimedeltaIndex
for res in [pd.TimedeltaIndex(values, dtype=dtype),
pd.TimedeltaIndex(list(values), dtype=dtype)]:
tm.assert_index_equal(res, idx)

def test_view_with_args(self):

restricted = ['unicodeIndex', 'strIndex', 'catIndex', 'boolIndex',
Expand Down
24 changes: 24 additions & 0 deletions pandas/tests/indexes/test_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,30 @@ def test_from_arrays_index_series_period(self):

tm.assert_index_equal(result, result2)

def test_from_arrays_index_datetimelike_mixed(self):
idx1 = pd.date_range('2015-01-01 10:00', freq='D', periods=3,
tz='US/Eastern')
idx2 = pd.date_range('2015-01-01 10:00', freq='H', periods=3)
idx3 = pd.timedelta_range('1 days', freq='D', periods=3)
idx4 = pd.period_range('2011-01-01', freq='D', periods=3)

result = pd.MultiIndex.from_arrays([idx1, idx2, idx3, idx4])
tm.assert_index_equal(result.get_level_values(0), idx1)
tm.assert_index_equal(result.get_level_values(1), idx2)
tm.assert_index_equal(result.get_level_values(2), idx3)
tm.assert_index_equal(result.get_level_values(3), idx4)

result2 = pd.MultiIndex.from_arrays([pd.Series(idx1),
pd.Series(idx2),
pd.Series(idx3),
pd.Series(idx4)])
tm.assert_index_equal(result2.get_level_values(0), idx1)
tm.assert_index_equal(result2.get_level_values(1), idx2)
tm.assert_index_equal(result2.get_level_values(2), idx3)
tm.assert_index_equal(result2.get_level_values(3), idx4)

tm.assert_index_equal(result, result2)

def test_from_arrays_different_lengths(self):
# GH13599
idx1 = [1, 2, 3]
Expand Down
5 changes: 1 addition & 4 deletions pandas/util/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,7 @@ def _get_ilevel_values(index, level):
unique = index.levels[level]
labels = index.labels[level]
filled = take_1d(unique.values, labels, fill_value=unique._na_value)
values = unique._simple_new(filled,
name=index.names[level],
freq=getattr(unique, 'freq', None),
tz=getattr(unique, 'tz', None))
values = unique._shallow_copy(filled, name=index.names[level])
return values

# instance validation
Expand Down