Skip to content
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

datetimelike indexes add/sub zero-dim integer arrays #19013

Merged
merged 5 commits into from
Dec 31, 2017
Merged
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
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.23.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ Numeric
^^^^^^^

- Bug in :func:`Series.__sub__` subtracting a non-nanosecond ``np.datetime64`` object from a ``Series`` gave incorrect results (:issue:`7996`)
-
- Bug in :class:`DatetimeIndex`, :class:`TimedeltaIndex` addition and subtraction of zero-dimensional integer arrays gave incorrect results (:issue:`19012`)
-

Categorical
Expand Down
18 changes: 14 additions & 4 deletions pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,8 @@ def __add__(self, other):
from pandas.core.index import Index
from pandas.core.indexes.timedeltas import TimedeltaIndex
from pandas.tseries.offsets import DateOffset

other = lib.item_from_zerodim(other)
if is_timedelta64_dtype(other):
return self._add_delta(other)
elif isinstance(self, TimedeltaIndex) and isinstance(other, Index):
Expand All @@ -689,6 +691,7 @@ def __add__(self, other):
return self._add_datelike(other)
else: # pragma: no cover
return NotImplemented

cls.__add__ = __add__
cls.__radd__ = __add__

Expand All @@ -697,6 +700,8 @@ def __sub__(self, other):
from pandas.core.indexes.datetimes import DatetimeIndex
from pandas.core.indexes.timedeltas import TimedeltaIndex
from pandas.tseries.offsets import DateOffset

other = lib.item_from_zerodim(other)
if is_timedelta64_dtype(other):
return self._add_delta(-other)
elif isinstance(self, TimedeltaIndex) and isinstance(other, Index):
Expand Down Expand Up @@ -724,6 +729,7 @@ def __sub__(self, other):

else: # pragma: no cover
return NotImplemented

cls.__sub__ = __sub__

def __rsub__(self, other):
Expand All @@ -737,8 +743,10 @@ def _add_delta(self, other):
return NotImplemented

def _add_delta_td(self, other):
# add a delta of a timedeltalike
# return the i8 result view
"""
Add a delta of a timedeltalike
return the i8 result view
"""

inc = delta_to_nanoseconds(other)
new_values = checked_add_with_arr(self.asi8, inc,
Expand All @@ -748,8 +756,10 @@ def _add_delta_td(self, other):
return new_values.view('i8')

def _add_delta_tdi(self, other):
# add a delta of a TimedeltaIndex
# return the i8 result view
"""
Add a delta of a TimedeltaIndex
return the i8 result view
"""

# delta operation
if not len(self) == len(other):
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/indexes/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import numpy as np

import pandas.util.testing as tm
from pandas.core.indexes.api import Index, MultiIndex
Expand All @@ -22,3 +23,9 @@
ids=lambda x: type(x).__name__)
def indices(request):
return request.param


@pytest.fixture(params=[1, np.array(1, dtype=np.int64)])
def one(request):
# zero-dim integer array behaves like an integer
return request.param
17 changes: 9 additions & 8 deletions pandas/tests/indexes/datetimes/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,36 +58,37 @@ def test_dti_radd_timestamp_raises(self):
# -------------------------------------------------------------
# Binary operations DatetimeIndex and int

def test_dti_add_int(self, tz):
def test_dti_add_int(self, tz, one):
# Variants of `one` for #19012
rng = pd.date_range('2000-01-01 09:00', freq='H',
periods=10, tz=tz)
result = rng + 1
result = rng + one
expected = pd.date_range('2000-01-01 10:00', freq='H',
periods=10, tz=tz)
tm.assert_index_equal(result, expected)

def test_dti_iadd_int(self, tz):
def test_dti_iadd_int(self, tz, one):
rng = pd.date_range('2000-01-01 09:00', freq='H',
periods=10, tz=tz)
expected = pd.date_range('2000-01-01 10:00', freq='H',
periods=10, tz=tz)
rng += 1
rng += one
tm.assert_index_equal(rng, expected)

def test_dti_sub_int(self, tz):
def test_dti_sub_int(self, tz, one):
rng = pd.date_range('2000-01-01 09:00', freq='H',
periods=10, tz=tz)
result = rng - 1
result = rng - one
expected = pd.date_range('2000-01-01 08:00', freq='H',
periods=10, tz=tz)
tm.assert_index_equal(result, expected)

def test_dti_isub_int(self, tz):
def test_dti_isub_int(self, tz, one):
rng = pd.date_range('2000-01-01 09:00', freq='H',
periods=10, tz=tz)
expected = pd.date_range('2000-01-01 08:00', freq='H',
periods=10, tz=tz)
rng -= 1
rng -= one
tm.assert_index_equal(rng, expected)

# -------------------------------------------------------------
Expand Down
14 changes: 8 additions & 6 deletions pandas/tests/indexes/period/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,21 @@ def test_add_iadd(self):
period.IncompatibleFrequency, msg):
rng += delta

# int
def test_pi_add_int(self, one):
# Variants of `one` for #19012
rng = pd.period_range('2000-01-01 09:00', freq='H', periods=10)
result = rng + 1
result = rng + one
expected = pd.period_range('2000-01-01 10:00', freq='H', periods=10)
tm.assert_index_equal(result, expected)
rng += 1
rng += one
tm.assert_index_equal(rng, expected)

def test_sub(self):
@pytest.mark.parametrize('five', [5, np.array(5, dtype=np.int64)])
def test_sub(self, five):
rng = period_range('2007-01', periods=50)

result = rng - 5
exp = rng + (-5)
result = rng - five
exp = rng + (-five)
tm.assert_index_equal(result, exp)

def test_sub_isub(self):
Expand Down
17 changes: 9 additions & 8 deletions pandas/tests/indexes/timedeltas/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,29 @@ def test_ufunc_coercions(self):
# -------------------------------------------------------------
# Binary operations TimedeltaIndex and integer

def test_tdi_add_int(self):
def test_tdi_add_int(self, one):
# Variants of `one` for #19012
rng = timedelta_range('1 days 09:00:00', freq='H', periods=10)
result = rng + 1
result = rng + one
expected = timedelta_range('1 days 10:00:00', freq='H', periods=10)
tm.assert_index_equal(result, expected)

def test_tdi_iadd_int(self):
def test_tdi_iadd_int(self, one):
rng = timedelta_range('1 days 09:00:00', freq='H', periods=10)
expected = timedelta_range('1 days 10:00:00', freq='H', periods=10)
rng += 1
rng += one
tm.assert_index_equal(rng, expected)

def test_tdi_sub_int(self):
def test_tdi_sub_int(self, one):
rng = timedelta_range('1 days 09:00:00', freq='H', periods=10)
result = rng - 1
result = rng - one
expected = timedelta_range('1 days 08:00:00', freq='H', periods=10)
tm.assert_index_equal(result, expected)

def test_tdi_isub_int(self):
def test_tdi_isub_int(self, one):
rng = timedelta_range('1 days 09:00:00', freq='H', periods=10)
expected = timedelta_range('1 days 08:00:00', freq='H', periods=10)
rng -= 1
rng -= one
tm.assert_index_equal(rng, expected)

# -------------------------------------------------------------
Expand Down