Skip to content

WIP/TST: add dt64tz to indices fixture #31236

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
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
76 changes: 75 additions & 1 deletion pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
is_list_like,
is_period_dtype,
is_scalar,
is_timedelta64_dtype,
needs_i8_conversion,
)
from pandas.core.dtypes.concat import concat_compat
from pandas.core.dtypes.generic import ABCIndex, ABCIndexClass, ABCSeries
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna

from pandas.core import algorithms
from pandas.core import algorithms, ops
from pandas.core.accessor import PandasDelegate
from pandas.core.arrays import DatetimeArray, ExtensionArray, TimedeltaArray
from pandas.core.arrays.datetimelike import DatetimeLikeArrayMixin
Expand Down Expand Up @@ -126,6 +127,79 @@ def __array_wrap__(self, result, context=None):
attrs["freq"] = "infer"
return Index(result, **attrs)

def __array_ufunc__(self, ufunc, method: str, *inputs, **kwargs):
result = ops.maybe_dispatch_ufunc_to_dunder_op(
self, ufunc, method, *inputs, **kwargs
)
if result is not NotImplemented:
return result

# unary ufuncs
if (
len(inputs) == 1
and inputs[0] is self
and not kwargs
and method == "__call__"
):
if ufunc.__name__ in ["isfinite"]:
return self.notna()
if ufunc.__name__ == "negative":
return -self
if ufunc.__name__ == "absolute":
if is_timedelta64_dtype(self.dtype):
return abs(self)

# binary ufuncs
if len(inputs) == 2 and not kwargs and method == "__call__":
from pandas import DatetimeIndex, TimedeltaIndex

new_inputs = [inputs[0], inputs[1]]
for i, val in enumerate(new_inputs):
if isinstance(val, np.ndarray) and val.dtype.kind == "m":
val = TimedeltaIndex(val)
new_inputs[i] = val
if isinstance(val, np.ndarray) and val.dtype.kind == "M":
val = DatetimeIndex(val)
new_inputs[i] = val

if ufunc.__name__ == "subtract":
if not isinstance(inputs[0], DatetimeIndexOpsMixin):
result = new_inputs[1].__rsub__(new_inputs[0])
else:
result = new_inputs[0].__sub__(new_inputs[1])

if result is NotImplemented:
# raise explicitly or else we get recursionerror
raise TypeError("cannot use operands with types")
return result

if ufunc.__name__ == "add":
if not isinstance(inputs[0], DatetimeIndexOpsMixin):
result = new_inputs[1].__radd__(new_inputs[0])
else:
result = new_inputs[0].__add__(new_inputs[1])

if result is NotImplemented:
# raise explicitly or else we get recursionerror
raise TypeError("cannot use operands with types")
return result

if (
len(inputs) == 2
and len(kwargs) == 1
and "out" in kwargs
and method == "__call__"
):
out = kwargs["out"]
if ufunc.__name__ == "subtract":
out[:] = inputs[0] - inputs[1]
return
if ufunc.__name__ == "add":
out[:] = inputs[0] + inputs[1]
return

return NotImplemented

# ------------------------------------------------------------------------

def equals(self, other) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ def __array_ufunc__(
result = getattr(ufunc, method)(*inputs, **kwargs)

name: Optional[Hashable]
if len(set(names)) == 1:
if len(set(names)) == 1: # TODO: we have a func in ops for this
name = names[0]
else:
name = None
Expand Down
1 change: 1 addition & 0 deletions pandas/tests/arithmetic/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,7 @@ def test_parr_ops_errors(self, ng, func, box_with_array):
)
obj = tm.box_expected(idx, box_with_array)
msg = (
"cannot use operands with types|"
r"unsupported operand type\(s\)|can only concatenate|"
r"must be str|object to str implicitly"
)
Expand Down
10 changes: 10 additions & 0 deletions pandas/tests/indexes/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from pandas._libs.tslib import iNaT

from pandas.core.dtypes.common import is_datetime64tz_dtype
from pandas.core.dtypes.dtypes import CategoricalDtype

import pandas as pd
Expand Down Expand Up @@ -301,6 +302,9 @@ def test_ensure_copied_data(self, indices):

index_type = type(indices)
result = index_type(indices.values, copy=True, **init_kwargs)
if isinstance(indices, DatetimeIndex) and indices.tz is not None:
result = result.tz_localize("UTC").tz_convert(indices.tz)

tm.assert_index_equal(indices, result)
tm.assert_numpy_array_equal(
indices._ndarray_values, result._ndarray_values, check_same="copy"
Expand Down Expand Up @@ -467,6 +471,9 @@ def test_intersection_base(self, indices):
# GH 10149
cases = [klass(second.values) for klass in [np.array, Series, list]]
for case in cases:
if is_datetime64tz_dtype(indices.dtype):
# the second.values will drop tz, so this would be wrong
continue
result = first.intersection(case)
assert tm.equalContents(result, second)

Expand All @@ -485,6 +492,9 @@ def test_union_base(self, indices):
# GH 10149
cases = [klass(second.values) for klass in [np.array, Series, list]]
for case in cases:
if is_datetime64tz_dtype(indices.dtype):
# the second.values will drop tz, so this would be wrong
continue
if not isinstance(indices, CategoricalIndex):
result = first.union(case)
assert tm.equalContents(result, everything)
Expand Down
1 change: 1 addition & 0 deletions pandas/tests/indexes/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"unicode": tm.makeUnicodeIndex(100),
"string": tm.makeStringIndex(100),
"datetime": tm.makeDateIndex(100),
"datetime-tz": tm.makeDateIndex(100, tz="US/Pacific"),
"period": tm.makePeriodIndex(100),
"timedelta": tm.makeTimedeltaIndex(100),
"int": tm.makeIntIndex(100),
Expand Down
8 changes: 6 additions & 2 deletions pandas/tests/indexes/test_numpy_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,12 @@ def test_numpy_ufuncs_other(indices, func):

elif isinstance(idx, PeriodIndex):
# raise TypeError or ValueError (PeriodIndex)
with pytest.raises(Exception):
func(idx)
if func in [np.isfinite]:
result = func(idx)
assert isinstance(result, np.ndarray)
else:
with pytest.raises(Exception):
func(idx)

elif isinstance(idx, (Float64Index, Int64Index, UInt64Index)):
# Results in bool array
Expand Down