From 8e856d1f145b299a40018422676ebcaba9244236 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 20 Dec 2017 14:21:13 -0800 Subject: [PATCH 01/14] Fix Series[timedelta64]+DatetimeIndex[tz] bugs ser + index lossed timezone closes #13905 index + ser retained timezone but returned a DatetimeIndex --- pandas/core/indexes/datetimes.py | 3 +++ pandas/core/ops.py | 9 +++++++++ pandas/tests/indexes/datetimes/test_arithmetic.py | 15 +++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index ec5c20d341b50..860e17db0bf45 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -855,6 +855,9 @@ def _maybe_update_attributes(self, attrs): return attrs def _add_delta(self, delta): + if isinstance(delta, ABCSeries): + return NotImplemented + from pandas import TimedeltaIndex name = self.name diff --git a/pandas/core/ops.py b/pandas/core/ops.py index e23609b23f529..fec263b1f4ec5 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -709,6 +709,15 @@ def wrapper(left, right, name=name, na_op=na_op): left, right = _align_method_SERIES(left, right) + if is_timedelta64_dtype(left) and isinstance(right, pd.DatetimeIndex): + # GH#13905 + # Defer to DatetimeIndex/TimedeltaIndex operations where timezones + # are handled carefully. + result = op(pd.TimedeltaIndex(left), right) + return construct_result(left, result, + index=left.index, name=left.name, + dtype=result.dtype) + converted = _Op.get_op(left, right, name, na_op) left, right = converted.left, converted.right diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index a46462e91a866..fcf38629b5815 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -363,6 +363,21 @@ def test_datetimeindex_sub_timestamp_overflow(self): with pytest.raises(OverflowError): dtimin - variant + @pytest.mark.parametrize('tz', [None, 'America/Chicago']) + def test_dti_add_series(self, tz): + # GH#13905 + index = pd.DatetimeIndex(['2016-06-28 05:30', '2016-06-28 05:31'], + tz=tz) + ser = pd.Series([pd.Timedelta(seconds=5)] * 2, + index=index) + expected = pd.Series(index + pd.Timedelta(seconds=5), + index=index) + assert expected.dtype == index.dtype + res = ser + index + tm.assert_series_equal(res, expected) + res2 = index + ser + tm.assert_series_equal(res2, expected) + # GH 10699 @pytest.mark.parametrize('klass,assert_func', zip([Series, DatetimeIndex], From 2e824bcd79bd60bad38482e3907611054313ef94 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 21 Dec 2017 08:52:00 -0800 Subject: [PATCH 02/14] set result name, add name checks to test --- pandas/core/ops.py | 4 +++- pandas/tests/indexes/datetimes/test_arithmetic.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index fec263b1f4ec5..f53c6c5c3e0ee 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -714,8 +714,10 @@ def wrapper(left, right, name=name, na_op=na_op): # Defer to DatetimeIndex/TimedeltaIndex operations where timezones # are handled carefully. result = op(pd.TimedeltaIndex(left), right) + name = _maybe_match_name(left, right) + result.name = name # in case name is None, needs to be overridden return construct_result(left, result, - index=left.index, name=left.name, + index=left.index, name=name, dtype=result.dtype) converted = _Op.get_op(left, right, name, na_op) diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index fcf38629b5815..226f983d237f6 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -363,15 +363,20 @@ def test_datetimeindex_sub_timestamp_overflow(self): with pytest.raises(OverflowError): dtimin - variant + @pytest.mark.parametrize('names', [('foo', None, None), + ('baz', 'bar', None), + ('bar', 'bar', 'bar')]) @pytest.mark.parametrize('tz', [None, 'America/Chicago']) - def test_dti_add_series(self, tz): + def test_dti_add_series(self, tz, names): # GH#13905 index = pd.DatetimeIndex(['2016-06-28 05:30', '2016-06-28 05:31'], - tz=tz) + tz=tz, name=names[0]) ser = pd.Series([pd.Timedelta(seconds=5)] * 2, - index=index) + index=index, name=names[1]) expected = pd.Series(index + pd.Timedelta(seconds=5), - index=index) + index=index, name=names[2]) + # passing name arg isn't enough when names[2] is None + expected.name = names[2] assert expected.dtype == index.dtype res = ser + index tm.assert_series_equal(res, expected) From 5471d40c5b88ad706ba7d98860eadf83e6790b91 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 21 Dec 2017 13:17:33 -0800 Subject: [PATCH 03/14] edits per reviewer request --- pandas/tests/indexes/datetimes/test_arithmetic.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index 226f983d237f6..18cd3ed92a5fd 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -375,13 +375,14 @@ def test_dti_add_series(self, tz, names): index=index, name=names[1]) expected = pd.Series(index + pd.Timedelta(seconds=5), index=index, name=names[2]) + # passing name arg isn't enough when names[2] is None expected.name = names[2] assert expected.dtype == index.dtype - res = ser + index - tm.assert_series_equal(res, expected) - res2 = index + ser - tm.assert_series_equal(res2, expected) + result = ser + index + tm.assert_series_equal(result, expected) + result2 = index + ser + tm.assert_series_equal(result2, expected) # GH 10699 From 277b8cbf32e758b216d0906a94ee44df695228e8 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 25 Dec 2017 19:39:43 -0800 Subject: [PATCH 04/14] implement requested kludge --- pandas/core/ops.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index da67b609865d7..f9da86f4ba14e 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -515,7 +515,8 @@ def _convert_to_array(self, values, name=None, other=None): # a datelike elif isinstance(values, pd.DatetimeIndex): - values = values.to_series() + # TODO: why are we casting to_series in the first place? + values = values.to_series(keep_tz=True) # datetime with tz elif (isinstance(ovalues, datetime.datetime) and hasattr(ovalues, 'tzinfo')): @@ -530,6 +531,11 @@ def _convert_to_array(self, values, name=None, other=None): elif inferred_type in ('timedelta', 'timedelta64'): # have a timedelta, convert to to ns here values = to_timedelta(values, errors='coerce', box=False) + if isinstance(other, pd.DatetimeIndex): + # GH#13905 + # Defer to DatetimeIndex/TimedeltaIndex operations where + # timezones are handled carefully. + values = pd.TimedeltaIndex(values) elif inferred_type == 'integer': # py3 compat where dtype is 'm' but is an integer if values.dtype.kind == 'm': @@ -738,17 +744,6 @@ def wrapper(left, right, name=name, na_op=na_op): left, right = _align_method_SERIES(left, right) - if is_timedelta64_dtype(left) and isinstance(right, pd.DatetimeIndex): - # GH#13905 - # Defer to DatetimeIndex/TimedeltaIndex operations where timezones - # are handled carefully. - result = op(pd.TimedeltaIndex(left), right) - name = _maybe_match_name(left, right) - result.name = name # in case name is None, needs to be overridden - return construct_result(left, result, - index=left.index, name=name, - dtype=result.dtype) - converted = _Op.get_op(left, right, name, na_op) left, right = converted.left, converted.right @@ -758,28 +753,37 @@ def wrapper(left, right, name=name, na_op=na_op): na_op = converted.na_op if isinstance(rvalues, ABCSeries): - name = _maybe_match_name(left, rvalues) lvalues = getattr(lvalues, 'values', lvalues) rvalues = getattr(rvalues, 'values', rvalues) # _Op aligns left and right else: - name = left.name if (hasattr(lvalues, 'values') and not isinstance(lvalues, pd.DatetimeIndex)): lvalues = lvalues.values + res_name = _get_series_result_name(left, right) + result = wrap_results(safe_na_op(lvalues, rvalues)) return construct_result( left, result, index=left.index, - name=name, + name=res_name, dtype=dtype, ) return wrapper +def _get_series_result_name(left, right): + # left is always a Series + if isinstance(right, (ABCSeries, pd.Index)): + name = _maybe_match_name(left, right) + else: + name = left.name + return name + + def _comp_method_OBJECT_ARRAY(op, x, y): if isinstance(y, list): y = construct_1d_object_array_from_listlike(y) From 5a5d98cc3e428d89646f17b5256ec3f7101e4744 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 28 Dec 2017 22:31:58 -0800 Subject: [PATCH 05/14] do names hapharzardly per request --- pandas/core/ops.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index f9da86f4ba14e..33ef0a65de886 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -753,16 +753,19 @@ def wrapper(left, right, name=name, na_op=na_op): na_op = converted.na_op if isinstance(rvalues, ABCSeries): + res_name = _maybe_match_name(lvalues, rvalues) lvalues = getattr(lvalues, 'values', lvalues) rvalues = getattr(rvalues, 'values', rvalues) # _Op aligns left and right else: + if isinstance(rvalues, pd.Index): + res_name = _maybe_match_name(left, right) + else: + res_name = left.name if (hasattr(lvalues, 'values') and not isinstance(lvalues, pd.DatetimeIndex)): lvalues = lvalues.values - res_name = _get_series_result_name(left, right) - result = wrap_results(safe_na_op(lvalues, rvalues)) return construct_result( left, @@ -775,15 +778,6 @@ def wrapper(left, right, name=name, na_op=na_op): return wrapper -def _get_series_result_name(left, right): - # left is always a Series - if isinstance(right, (ABCSeries, pd.Index)): - name = _maybe_match_name(left, right) - else: - name = left.name - return name - - def _comp_method_OBJECT_ARRAY(op, x, y): if isinstance(y, list): y = construct_1d_object_array_from_listlike(y) From 5c2e883451c7e58c89c6c8bbd7af58f54ca45fab Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 28 Dec 2017 22:38:21 -0800 Subject: [PATCH 06/14] whatsnew note --- doc/source/whatsnew/v0.23.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 5e55efb4e21fb..9e3ccb78ddbb0 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -361,7 +361,7 @@ Numeric ^^^^^^^ - Bug in :func:`Series.__sub__` subtracting a non-nanosecond ``np.datetime64`` object from a ``Series`` gave incorrect results (:issue:`7996`) -- +- Bug in :func:`Series.__add__` adding Series with dtype ``timedelta64[ns]`` to a timezone-aware ``DatetimeIndex`` incorrectly dropped timezone information (:issue:`13905`) - Categorical From ae3b34d626798327b2e607862e2d27b702c7a86a Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 29 Dec 2017 08:59:55 -0800 Subject: [PATCH 07/14] fix name mismatch from merge mangle --- pandas/core/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index cd1dad22d89ee..c0b9325128fa1 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -776,7 +776,7 @@ def wrapper(left, right, name=name, na_op=na_op): left, result, index=left.index, - name=res_name, + name=name, dtype=dtype, ) From dd2970cd174e0fc6691665de988a913a3b482d95 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 29 Dec 2017 10:59:24 -0800 Subject: [PATCH 08/14] handle names correctly --- pandas/core/indexes/datetimelike.py | 8 ++++++-- pandas/core/ops.py | 17 +++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 40c07376d2522..65a6f0f339194 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -669,7 +669,9 @@ def __add__(self, other): from pandas.core.index import Index from pandas.core.indexes.timedeltas import TimedeltaIndex from pandas.tseries.offsets import DateOffset - if is_timedelta64_dtype(other): + if isinstance(other, ABCSeries): + return NotImplemented + elif is_timedelta64_dtype(other): return self._add_delta(other) elif isinstance(self, TimedeltaIndex) and isinstance(other, Index): if hasattr(other, '_add_delta'): @@ -697,7 +699,9 @@ def __sub__(self, other): from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.timedeltas import TimedeltaIndex from pandas.tseries.offsets import DateOffset - if is_timedelta64_dtype(other): + if isinstance(other, ABCSeries): + return NotImplemented + elif is_timedelta64_dtype(other): return self._add_delta(-other) elif isinstance(self, TimedeltaIndex) and isinstance(other, Index): if not isinstance(other, TimedeltaIndex): diff --git a/pandas/core/ops.py b/pandas/core/ops.py index c0b9325128fa1..46ce454095885 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -758,31 +758,36 @@ def wrapper(left, right, name=name, na_op=na_op): na_op = converted.na_op if isinstance(rvalues, ABCSeries): - name = _maybe_match_name(left, rvalues) lvalues = getattr(lvalues, 'values', lvalues) rvalues = getattr(rvalues, 'values', rvalues) # _Op aligns left and right else: - if isinstance(rvalues, pd.Index): - name = _maybe_match_name(left, rvalues) - else: - name = left.name if (hasattr(lvalues, 'values') and not isinstance(lvalues, pd.DatetimeIndex)): lvalues = lvalues.values + res_name = _get_series_op_result_name(left, right) result = wrap_results(safe_na_op(lvalues, rvalues)) return construct_result( left, result, index=left.index, - name=name, + name=res_name, dtype=dtype, ) return wrapper +def _get_series_op_result_name(left, right): + # `left` is always a Series object + if isinstance(right, (pd.Series, pd.Index)): + name = _maybe_match_name(left, right) + else: + name = left.name + return name + + def _comp_method_OBJECT_ARRAY(op, x, y): if isinstance(y, list): y = construct_1d_object_array_from_listlike(y) From 26c9b510e810943bc874a3b53ce97eba6b6ba369 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 29 Dec 2017 11:48:49 -0800 Subject: [PATCH 09/14] check against ABC classes --- pandas/core/ops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 46ce454095885..d9466f5f47bbe 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -39,7 +39,7 @@ from pandas.core.dtypes.generic import ( ABCSeries, ABCDataFrame, - ABCIndex, + ABCIndex, ABCDatetimeIndex, ABCPeriodIndex) # ----------------------------------------------------------------------------- @@ -763,7 +763,7 @@ def wrapper(left, right, name=name, na_op=na_op): # _Op aligns left and right else: if (hasattr(lvalues, 'values') and - not isinstance(lvalues, pd.DatetimeIndex)): + not isinstance(lvalues, ABCDatetimeIndex)): lvalues = lvalues.values res_name = _get_series_op_result_name(left, right) @@ -781,7 +781,7 @@ def wrapper(left, right, name=name, na_op=na_op): def _get_series_op_result_name(left, right): # `left` is always a Series object - if isinstance(right, (pd.Series, pd.Index)): + if isinstance(right, (ABCSeries, pd.Index)): name = _maybe_match_name(left, right) else: name = left.name From a7a3d6fe9f907508dfbf85857ba9bf17b2fc657f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 29 Dec 2017 12:16:39 -0800 Subject: [PATCH 10/14] check against ABCDatetimeIndex --- pandas/core/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index d9466f5f47bbe..dadc78d2ad7cc 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -514,7 +514,7 @@ def _convert_to_array(self, values, name=None, other=None): values[:] = iNaT # a datelike - elif isinstance(values, pd.DatetimeIndex): + elif isinstance(values, ABCDatetimeIndex): # TODO: why are we casting to_series in the first place? values = values.to_series(keep_tz=True) # datetime with tz From 3e71017cab7a26a082f6e20098cd3ba1ac86e1c1 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 29 Dec 2017 12:17:02 -0800 Subject: [PATCH 11/14] assert_produces_warning(PerformanceWarning) --- .../indexes/datetimes/test_arithmetic.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index 4130be1abeb27..906dca6dbbb53 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -389,12 +389,17 @@ def test_dti_add_offset_array(self, tz, box): # GH#18849 dti = pd.date_range('2017-01-01', periods=2, tz=tz) other = box([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) - res = dti + other + + with tm.assert_produces_warning(PerformanceWarning, + filter_level="always"): + res = dti + other expected = DatetimeIndex([dti[n] + other[n] for n in range(len(dti))], name=dti.name, freq='infer') tm.assert_index_equal(res, expected) - res2 = other + dti + with tm.assert_produces_warning(PerformanceWarning, + filter_level="always"): + res2 = other + dti tm.assert_index_equal(res2, expected) @pytest.mark.parametrize('box', [np.array, pd.Index]) @@ -402,7 +407,8 @@ def test_dti_sub_offset_array(self, tz, box): # GH#18824 dti = pd.date_range('2017-01-01', periods=2, tz=tz) other = box([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) - res = dti - other + with tm.assert_produces_warning(PerformanceWarning): + res = dti - other expected = DatetimeIndex([dti[n] - other[n] for n in range(len(dti))], name=dti.name, freq='infer') tm.assert_index_equal(res, expected) @@ -418,15 +424,19 @@ def test_dti_with_offset_series(self, tz, names): expected_add = pd.Series([dti[n] + other[n] for n in range(len(dti))], name=names[2]) - res = dti + other + with tm.assert_produces_warning(PerformanceWarning, + filter_level="always"): + res = dti + other tm.assert_series_equal(res, expected_add) - res2 = other + dti + with tm.assert_produces_warning(PerformanceWarning, + filter_level="always"): + res2 = other + dti tm.assert_series_equal(res2, expected_add) expected_sub = pd.Series([dti[n] - other[n] for n in range(len(dti))], name=names[2]) - - res3 = dti - other + with tm.assert_produces_warning(PerformanceWarning): + res3 = dti - other tm.assert_series_equal(res3, expected_sub) From 7b831ff25f9d6c9b1082e12aeec37da211f19cfc Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sat, 30 Dec 2017 17:28:43 -0800 Subject: [PATCH 12/14] requested convention edits --- pandas/core/ops.py | 2 +- pandas/tests/indexes/datetimes/test_arithmetic.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 37694fa32c0ea..65f329f7d5009 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -536,7 +536,7 @@ def _convert_to_array(self, values, name=None, other=None): elif inferred_type in ('timedelta', 'timedelta64'): # have a timedelta, convert to to ns here values = to_timedelta(values, errors='coerce', box=False) - if isinstance(other, pd.DatetimeIndex): + if isinstance(other, ABCDatetimeIndex): # GH#13905 # Defer to DatetimeIndex/TimedeltaIndex operations where # timezones are handled carefully. diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index 5a78d9f193ad5..9e458c462c27d 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -369,12 +369,12 @@ def test_datetimeindex_sub_timestamp_overflow(self): @pytest.mark.parametrize('tz', [None, 'America/Chicago']) def test_dti_add_series(self, tz, names): # GH#13905 - index = pd.DatetimeIndex(['2016-06-28 05:30', '2016-06-28 05:31'], - tz=tz, name=names[0]) - ser = pd.Series([pd.Timedelta(seconds=5)] * 2, - index=index, name=names[1]) - expected = pd.Series(index + pd.Timedelta(seconds=5), - index=index, name=names[2]) + index = DatetimeIndex(['2016-06-28 05:30', '2016-06-28 05:31'], + tz=tz, name=names[0]) + ser = Series([Timedelta(seconds=5)] * 2, + index=index, name=names[1]) + expected = Series(index + Timedelta(seconds=5), + index=index, name=names[2]) # passing name arg isn't enough when names[2] is None expected.name = names[2] From 0fc30bbe0be6010d239ce94c244974c7324ccb98 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 31 Dec 2017 10:05:01 -0800 Subject: [PATCH 13/14] requested edit, extend tests to include ser.values --- pandas/core/ops.py | 16 ++++++---------- .../tests/indexes/datetimes/test_arithmetic.py | 6 ++++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 8c8fb4cf24f6a..0229f7c256464 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -768,7 +768,12 @@ def wrapper(left, right, name=name, na_op=na_op): not isinstance(lvalues, ABCDatetimeIndex)): lvalues = lvalues.values - res_name = _get_series_op_result_name(left, right) + if isinstance(right, (ABCSeries, pd.Index)): + # `left` is always a Series object + res_name = _maybe_match_name(left, right) + else: + res_name = left.name + result = wrap_results(safe_na_op(lvalues, rvalues)) return construct_result( left, @@ -781,15 +786,6 @@ def wrapper(left, right, name=name, na_op=na_op): return wrapper -def _get_series_op_result_name(left, right): - # `left` is always a Series object - if isinstance(right, (ABCSeries, pd.Index)): - name = _maybe_match_name(left, right) - else: - name = left.name - return name - - def _comp_method_OBJECT_ARRAY(op, x, y): if isinstance(y, list): y = construct_1d_object_array_from_listlike(y) diff --git a/pandas/tests/indexes/datetimes/test_arithmetic.py b/pandas/tests/indexes/datetimes/test_arithmetic.py index 32e29d925f122..381e2ef3041e7 100644 --- a/pandas/tests/indexes/datetimes/test_arithmetic.py +++ b/pandas/tests/indexes/datetimes/test_arithmetic.py @@ -385,6 +385,12 @@ def test_dti_add_series(self, tz, names): result2 = index + ser tm.assert_series_equal(result2, expected) + expected = index + Timedelta(seconds=5) + result3 = ser.values + index + tm.assert_index_equal(result3, expected) + result4 = index + ser.values + tm.assert_index_equal(result4, expected) + @pytest.mark.parametrize('box', [np.array, pd.Index]) def test_dti_add_offset_array(self, tz, box): # GH#18849 From ae1af46f19f51473e110205c1a72093d43e50274 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 31 Dec 2017 18:23:25 -0800 Subject: [PATCH 14/14] dummy commit to force CI --- pandas/core/indexes/datetimelike.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index ad1f5aa7ad5d7..ee2fdd213dd9a 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -669,6 +669,7 @@ 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 isinstance(other, ABCSeries): return NotImplemented @@ -701,6 +702,7 @@ 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 isinstance(other, ABCSeries): return NotImplemented