Skip to content

Tests for TDI issues already fixed #19044

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

Merged
merged 10 commits into from
Jan 5, 2018
5 changes: 4 additions & 1 deletion doc/source/whatsnew/v0.23.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,10 @@ Conversion
- Bug in :class:`FY5253` where ``datetime`` addition and subtraction incremented incorrectly for dates on the year-end but not normalized to midnight (:issue:`18854`)
- Bug in :class:`DatetimeIndex` where adding or subtracting an array-like of ``DateOffset`` objects either raised (``np.array``, ``pd.Index``) or broadcast incorrectly (``pd.Series``) (:issue:`18849`)
- Bug in :class:`Series` floor-division where operating on a scalar ``timedelta`` raises an exception (:issue:`18846`)

- Bug in :class:`Series`` with ``dtype='timedelta64[ns]`` where addition or subtraction of ``TimedeltaIndex`` had results cast to ``dtype='int64'`` (:issue:`17250`)
- Bug in :class:`TimedeltaIndex` where multiplication or division by a ``Series`` would return a ``TimedeltaIndex`` instead of a ``Series`` (issue:`19042`)
- Bug in :class:`Series` with ``dtype='timedelta64[ns]`` where addition or subtraction of ``TimedeltaIndex`` could return a ``Series`` with an incorrect name (issue:`19043`)
-

Indexing
^^^^^^^^
Expand Down
19 changes: 19 additions & 0 deletions pandas/core/indexes/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ def _add_delta(self, delta):
return result

def _evaluate_with_timedelta_like(self, other, op, opstr):
if isinstance(other, ABCSeries):
# GH#19042
return NotImplemented

# allow division by a timedelta
if opstr in ['__div__', '__truediv__', '__floordiv__']:
Expand Down Expand Up @@ -912,6 +915,22 @@ def delete(self, loc):
TimedeltaIndex._add_datetimelike_methods()


def _override_tdi_arith_methods():
# GH#19042
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather just not do this now. remove.

# TODO: Eventually just do this correctly in indexes.base
tdi_mul = TimedeltaIndex.__mul__

def __mul__(self, other):
if isinstance(other, ABCSeries):
return NotImplemented
return tdi_mul(self, other)

TimedeltaIndex.__mul__ = __mul__


_override_tdi_arith_methods()


def _is_convertible_to_index(other):
"""
return a boolean whether I can attempt conversion to a TimedeltaIndex
Expand Down
6 changes: 5 additions & 1 deletion pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,11 @@ def _align_method_SERIES(left, right, align_asobject=False):


def _construct_result(left, result, index, name, dtype):
return left._constructor(result, index=index, name=name, dtype=dtype)
out = left._constructor(result, index=index, name=name, dtype=dtype)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just not pass the name at all to the constructor then (and just set as you are doing)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.


# If `result` has a non-None name and name is None, we need to override.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make this a doc-string instead

out.name = name
return out


def _construct_divmod_result(left, result, index, name, dtype):
Expand Down
9 changes: 5 additions & 4 deletions pandas/tests/indexes/timedeltas/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ def test_numeric_compat(self):
tm.assert_index_equal(result, didx)

result = idx * Series(np.arange(5, dtype='int64'))
tm.assert_index_equal(result, didx)
tm.assert_series_equal(result, Series(didx))

result = idx * Series(np.arange(5, dtype='float64') + 0.1)
tm.assert_index_equal(result, self._holder(np.arange(
5, dtype='float64') * (np.arange(5, dtype='float64') + 0.1)))
rng5 = np.arange(5, dtype='float64')
result = idx * Series(rng5 + 0.1)
expected = Series(self._holder(rng5 * (rng5 + 0.1)))
tm.assert_series_equal(result, expected)

# invalid
pytest.raises(TypeError, lambda: idx * idx)
Expand Down
65 changes: 65 additions & 0 deletions pandas/tests/series/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,71 @@ def test_timedelta_floordiv(self, scalar_td):
expected = Series([0, 0, np.nan])
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize('names', [(None, None, None),
('Egon', 'Venkman', None),
('NCC1701D', 'NCC1701D', 'NCC1701D')])
def test_td64_series_with_tdi(self, names):
# GH#17250 make sure result dtype is correct
# GH#19043 make sure names are propogated correctly
tdi = pd.TimedeltaIndex(['0 days', '1 day'], name=names[0])
ser = Series([Timedelta(hours=3), Timedelta(hours=4)], name=names[1])
expected = Series([Timedelta(hours=3), Timedelta(days=1, hours=4)],
name=names[2])

result = tdi + ser
tm.assert_series_equal(result, expected)
assert result.dtype == 'timedelta64[ns]'

result = ser + tdi
tm.assert_series_equal(result, expected)
assert result.dtype == 'timedelta64[ns]'

expected = Series([Timedelta(hours=-3), Timedelta(days=1, hours=-4)],
name=names[2])

result = tdi - ser
tm.assert_series_equal(result, expected)
assert result.dtype == 'timedelta64[ns]'

result = ser - tdi
tm.assert_series_equal(result, -expected)
assert result.dtype == 'timedelta64[ns]'

@pytest.mark.parametrize('names', [(None, None, None),
('Egon', 'Venkman', None),
('NCC1701D', 'NCC1701D', 'NCC1701D')])
def test_tdi_mul_int_series(self, names):
# GH#19042
tdi = pd.TimedeltaIndex(['0days', '1day', '2days', '3days', '4days'],
name=names[0])
ser = Series([0, 1, 2, 3, 4], dtype=np.int64, name=names[1])

expected = Series(['0days', '1day', '4days', '9days', '16days'],
dtype='timedelta64[ns]',
name=names[2])

result = tdi * ser
tm.assert_series_equal(result, expected)

result = ser * tdi
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize('names', [(None, None, None),
('Egon', 'Venkman', None),
('NCC1701D', 'NCC1701D', 'NCC1701D')])
def test_tdi_div_float_series(self, names):
# GH#19042
tdi = pd.TimedeltaIndex(['0days', '1day', '2days', '3days', '4days'],
name=names[0])
ser = Series([1.5, 3, 4.5, 6, 7.5], dtype=np.float64, name=names[1])

expected = Series([tdi[n] / ser[n] for n in range(len(ser))],
dtype='timedelta64[ns]',
name=names[2])

result = tdi / ser
tm.assert_series_equal(result, expected)


class TestDatetimeSeriesArithmetic(object):
@pytest.mark.parametrize(
Expand Down