Skip to content

Commit dc430c5

Browse files
committed
Merge pull request #10158 from sinhrks/tdidx_name
BUG: Index.name is lost during timedelta ops
2 parents f929bc4 + a2523be commit dc430c5

File tree

6 files changed

+60
-21
lines changed

6 files changed

+60
-21
lines changed

doc/source/whatsnew/v0.17.0.txt

+4
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ Bug Fixes
6868
- Bug in ``NaT`` raises ``AttributeError`` when accessing to ``daysinmonth``, ``dayofweek`` properties. (:issue:`10096`)
6969

7070
- Bug in getting timezone data with ``dateutil`` on various platforms ( :issue:`9059`, :issue:`8639`, :issue:`9663`, :issue:`10121`)
71+
72+
73+
74+
- Bug in ``DatetimeIndex`` and ``TimedeltaIndex`` names are lost after timedelta arithmetics ( :issue:`9926`)

pandas/core/common.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -3322,10 +3322,17 @@ def save(obj, path): # TODO remove in 0.13
33223322

33233323

33243324
def _maybe_match_name(a, b):
3325-
a_name = getattr(a, 'name', None)
3326-
b_name = getattr(b, 'name', None)
3327-
if a_name == b_name:
3328-
return a_name
3325+
a_has = hasattr(a, 'name')
3326+
b_has = hasattr(b, 'name')
3327+
if a_has and b_has:
3328+
if a.name == b.name:
3329+
return a.name
3330+
else:
3331+
return None
3332+
elif a_has:
3333+
return a.name
3334+
elif b_has:
3335+
return b.name
33293336
return None
33303337

33313338
def _random_state(state=None):

pandas/tests/test_common.py

+21
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,27 @@ def test_random_state():
545545
com._random_state(5.5)
546546

547547

548+
def test_maybe_match_name():
549+
550+
matched = com._maybe_match_name(Series([1], name='x'), Series([2], name='x'))
551+
assert(matched == 'x')
552+
553+
matched = com._maybe_match_name(Series([1], name='x'), Series([2], name='y'))
554+
assert(matched is None)
555+
556+
matched = com._maybe_match_name(Series([1]), Series([2], name='x'))
557+
assert(matched is None)
558+
559+
matched = com._maybe_match_name(Series([1], name='x'), Series([2]))
560+
assert(matched is None)
561+
562+
matched = com._maybe_match_name(Series([1], name='x'), [2])
563+
assert(matched == 'x')
564+
565+
matched = com._maybe_match_name([1], Series([2], name='y'))
566+
assert(matched == 'y')
567+
568+
548569
class TestTake(tm.TestCase):
549570
# standard incompatible fill error
550571
fill_error = re.compile("Incompatible type for fill_value")

pandas/tseries/index.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -653,14 +653,18 @@ def _sub_datelike(self, other):
653653

654654
def _add_delta(self, delta):
655655
from pandas import TimedeltaIndex
656+
name = self.name
657+
656658
if isinstance(delta, (Tick, timedelta, np.timedelta64)):
657659
new_values = self._add_delta_td(delta)
658660
elif isinstance(delta, TimedeltaIndex):
659661
new_values = self._add_delta_tdi(delta)
662+
# update name when delta is Index
663+
name = com._maybe_match_name(self, delta)
660664
else:
661665
new_values = self.astype('O') + delta
662666
tz = 'UTC' if self.tz is not None else None
663-
result = DatetimeIndex(new_values, tz=tz, freq='infer')
667+
result = DatetimeIndex(new_values, tz=tz, name=name, freq='infer')
664668
utc = _utc()
665669
if self.tz is not None and self.tz is not utc:
666670
result = result.tz_convert(self.tz)

pandas/tseries/tdi.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,15 @@ def __setstate__(self, state):
281281
def _add_delta(self, delta):
282282
if isinstance(delta, (Tick, timedelta, np.timedelta64)):
283283
new_values = self._add_delta_td(delta)
284+
name = self.name
284285
elif isinstance(delta, TimedeltaIndex):
285286
new_values = self._add_delta_tdi(delta)
287+
# update name when delta is index
288+
name = com._maybe_match_name(self, delta)
286289
else:
287290
raise ValueError("cannot add the type {0} to a TimedeltaIndex".format(type(delta)))
288291

289-
result = TimedeltaIndex(new_values, freq='infer')
292+
result = TimedeltaIndex(new_values, freq='infer', name=name)
290293
return result
291294

292295
def _evaluate_with_timedelta_like(self, other, op, opstr):

pandas/tseries/tests/test_base.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -634,27 +634,27 @@ def test_dti_dti_deprecated_ops(self):
634634
def test_dti_tdi_numeric_ops(self):
635635

636636
# These are normally union/diff set-like ops
637-
tdi = TimedeltaIndex(['1 days',pd.NaT,'2 days'], name='foo')
638-
dti = date_range('20130101',periods=3, name='bar')
637+
tdi = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo')
638+
dti = date_range('20130101', periods=3, name='bar')
639639
td = Timedelta('1 days')
640640
dt = Timestamp('20130101')
641641

642642
result = tdi - tdi
643643
expected = TimedeltaIndex(['0 days', pd.NaT, '0 days'], name='foo')
644-
tm.assert_index_equal(result, expected, check_names=False) # must be foo
644+
tm.assert_index_equal(result, expected)
645645

646646
result = tdi + tdi
647647
expected = TimedeltaIndex(['2 days', pd.NaT, '4 days'], name='foo')
648-
tm.assert_index_equal(result, expected, check_names=False) # must be foo
648+
tm.assert_index_equal(result, expected)
649649

650-
result = dti - tdi
650+
result = dti - tdi # name will be reset
651651
expected = DatetimeIndex(['20121231', pd.NaT, '20130101'])
652652
tm.assert_index_equal(result, expected)
653653

654654
def test_addition_ops(self):
655655

656656
# with datetimes/timedelta and tdi/dti
657-
tdi = TimedeltaIndex(['1 days',pd.NaT,'2 days'], name='foo')
657+
tdi = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo')
658658
dti = date_range('20130101', periods=3, name='bar')
659659
td = Timedelta('1 days')
660660
dt = Timestamp('20130101')
@@ -669,11 +669,11 @@ def test_addition_ops(self):
669669

670670
result = td + tdi
671671
expected = TimedeltaIndex(['2 days', pd.NaT, '3 days'], name='foo')
672-
tm.assert_index_equal(result, expected, check_names=False) # must be foo
672+
tm.assert_index_equal(result, expected)
673673

674674
result = tdi + td
675675
expected = TimedeltaIndex(['2 days', pd.NaT, '3 days'], name='foo')
676-
tm.assert_index_equal(result,expected, check_names=False) # must be foo
676+
tm.assert_index_equal(result, expected)
677677

678678
# unequal length
679679
self.assertRaises(ValueError, lambda : tdi + dti[0:1])
@@ -685,21 +685,21 @@ def test_addition_ops(self):
685685
# this is a union!
686686
#self.assertRaises(TypeError, lambda : Int64Index([1,2,3]) + tdi)
687687

688-
result = tdi + dti
688+
result = tdi + dti # name will be reset
689689
expected = DatetimeIndex(['20130102', pd.NaT, '20130105'])
690-
tm.assert_index_equal(result,expected)
690+
tm.assert_index_equal(result, expected)
691691

692-
result = dti + tdi
693-
expected = DatetimeIndex(['20130102',pd.NaT,'20130105'])
694-
tm.assert_index_equal(result,expected)
692+
result = dti + tdi # name will be reset
693+
expected = DatetimeIndex(['20130102', pd.NaT, '20130105'])
694+
tm.assert_index_equal(result, expected)
695695

696696
result = dt + td
697697
expected = Timestamp('20130102')
698-
self.assertEqual(result,expected)
698+
self.assertEqual(result, expected)
699699

700700
result = td + dt
701701
expected = Timestamp('20130102')
702-
self.assertEqual(result,expected)
702+
self.assertEqual(result, expected)
703703

704704
def test_value_counts_unique(self):
705705
# GH 7735

0 commit comments

Comments
 (0)