Repository: pandas-dev/pandas

Issue:
API: DateTimeIndex slicing removes name label
Shouldn't it carry all possible properties instead?

Steps to reproduce:

``` python

import pandas as pd


st = pd.Timestamp('2013-07-01 00:00:00', tz='America/Los_Angeles')
et = pd.Timestamp('2013-07-02 00:00:00', tz='America/Los_Angeles')

dr = pd.date_range(st, et, freq='H', name='timebucket')
assert dr.name == 'timebucket'
dr = dr[1:]
assert dr.name == None
```



Discussion:
related to #4039 I think, cc @jtratner

hm weird. looks like the overridden `__getitem__` is not being called...

```
In [9]: paste
st = pd.Timestamp('2013-07-01 00:00:00', tz='America/Los_Angeles')
et = pd.Timestamp('2013-07-02 00:00:00', tz='America/Los_Angeles')

dr = pd.date_range(st, et, freq='H', name='timebucket')

## -- End pasted text --

In [10]: dr[1:]
Out[10]:
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-07-01 01:00:00, ..., 2013-07-02 00:00:00]
Length: 24, Freq: H, Timezone: America/Los_Angeles

In [11]: dr[1:].name

In [12]: dr.__getitem__(slice(1, None)).name
Out[12]: 'timebucket'
```

I think is a short cut I put in, something like getitem_slice in tseries/index

same issue for `Index(['a','b'])` as well

problem is `__getslice__` i htink

http://docs.python.org/2/reference/datamodel.html#additional-methods-for-emulation-of-sequence-types

passes when i define that in terms of `__getitem__`

pr coming


Resolving patch:
diff --git a/pandas/core/index.py b/pandas/core/index.py
--- a/pandas/core/index.py
+++ b/pandas/core/index.py
@@ -14,7 +14,6 @@
 from pandas.util.decorators import cache_readonly
 from pandas.core.common import isnull
 import pandas.core.common as com
-from pandas.util import py3compat
 from pandas.core.config import get_option
 
 
@@ -908,7 +907,7 @@ def reindex(self, target, method=None, level=None, limit=None,
 
             if self.equals(target):
                 indexer = None
-                
+
                 # to avoid aliasing an existing index
                 if copy_if_needed and target.name != self.name and self.name is not None:
                     if target.name is None:
@@ -1215,7 +1214,7 @@ def slice_locs(self, start=None, end=None):
         else:
             try:
                 start_slice = self.get_loc(start)
-                
+
                 if not is_unique:
 
                     # get_loc will return a boolean array for non_uniques
@@ -2794,6 +2793,7 @@ def _get_consensus_names(indexes):
         return list(list(consensus_names)[0])
     return [None] * indexes[0].nlevels
 
+
 def _maybe_box(idx):
     from pandas.tseries.api import DatetimeIndex, PeriodIndex
     klasses = DatetimeIndex, PeriodIndex
diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py
--- a/pandas/tseries/index.py
+++ b/pandas/tseries/index.py
@@ -1013,6 +1013,7 @@ def __array_finalize__(self, obj):
 
         self.offset = getattr(obj, 'offset', None)
         self.tz = getattr(obj, 'tz', None)
+        self.name = getattr(obj, 'name', None)
 
     def intersection(self, other):
         """
@@ -1069,7 +1070,7 @@ def intersection(self, other):
             return self._view_like(left_chunk)
 
     def _partial_date_slice(self, reso, parsed, use_lhs=True, use_rhs=True):
-        
+
         is_monotonic = self.is_monotonic
 
         if reso == 'year':
@@ -1236,7 +1237,7 @@ def slice_locs(self, start=None, end=None):
                         start_loc = self._get_string_slice(start).start
                     else:
                         start_loc = 0
-                        
+
                     if end:
                         end_loc = self._get_string_slice(end).stop
                     else:
@@ -1254,7 +1255,7 @@ def slice_locs(self, start=None, end=None):
                         start_loc = self._get_string_slice(start,use_rhs=False)
                     else:
                         start_loc = np.arange(len(self))
-                        
+
                     if end:
                         end_loc = self._get_string_slice(end,use_lhs=False)
                     else:
diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py
--- a/pandas/tseries/period.py
+++ b/pandas/tseries/period.py
@@ -1060,7 +1060,7 @@ def _format_native_types(self, na_rep=u'NaT', **kwargs):
         values = np.array(list(self),dtype=object)
         mask = isnull(self.values)
         values[mask] = na_rep
-        
+
         imask = -mask
         values[imask] = np.array([ u'%s' % dt for dt in values[imask] ])
         return values.tolist()
@@ -1070,6 +1070,7 @@ def __array_finalize__(self, obj):
             return self.item()
 
         self.freq = getattr(obj, 'freq', None)
+        self.name = getattr(obj, 'name', None)
 
     def __repr__(self):
         output = str(self.__class__) + '\n'


Test patch:
diff --git a/pandas/tests/test_index.py b/pandas/tests/test_index.py
--- a/pandas/tests/test_index.py
+++ b/pandas/tests/test_index.py
@@ -13,7 +13,6 @@
 from pandas.core.index import Index, Int64Index, MultiIndex
 from pandas.util.testing import assert_almost_equal
 from pandas.util import py3compat
-import pandas.core.common as com
 
 import pandas.util.testing as tm
 import pandas.core.config as cf
@@ -551,6 +550,10 @@ def test_get_level_values(self):
         result = self.strIndex.get_level_values(0)
         self.assert_(result.equals(self.strIndex))
 
+    def test_slice_keep_name(self):
+        idx = Index(['a', 'b'], name='asdf')
+        self.assertEqual(idx.name, idx[1:].name)
+
 
 class TestInt64Index(unittest.TestCase):
     _multiprocess_can_split_ = True
@@ -918,6 +921,10 @@ def test_bytestring_with_unicode(self):
         else:
             str(idx)
 
+    def test_slice_keep_name(self):
+        idx = Int64Index([1, 2], name='asdf')
+        self.assertEqual(idx.name, idx[1:].name)
+
 
 class TestMultiIndex(unittest.TestCase):
     _multiprocess_can_split_ = True
@@ -1537,7 +1544,7 @@ def test_diff(self):
         self.assert_(first.equals(result))
         self.assertEqual(first.names, result.names)
 
-        # name from non-empty array 
+        # name from non-empty array
         result = first.diff([('foo', 'one')])
         expected = pd.MultiIndex.from_tuples([('bar', 'one'), ('baz', 'two'), ('foo', 'two'),
                                               ('qux', 'one'), ('qux', 'two')])
@@ -1789,14 +1796,18 @@ def test_bytestring_with_unicode(self):
         else:
             str(idx)
 
+    def test_slice_keep_name(self):
+        x = MultiIndex.from_tuples([('a', 'b'), (1, 2), ('c', 'd')],
+                                   names=['x', 'y'])
+        self.assertEqual(x[1:].names, x.names)
+
 
 def test_get_combined_index():
     from pandas.core.index import _get_combined_index
     result = _get_combined_index([])
     assert(result.equals(Index([])))
 
+
 if __name__ == '__main__':
-    import nose
     nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
-                   # '--with-coverage', '--cover-package=pandas.core'],
                    exit=False)
diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py
--- a/pandas/tseries/tests/test_period.py
+++ b/pandas/tseries/tests/test_period.py
@@ -27,6 +27,7 @@
 from pandas import Series, TimeSeries, DataFrame
 from pandas.util.testing import assert_series_equal, assert_almost_equal
 import pandas.util.testing as tm
+from numpy.testing import assert_array_equal
 
 
 class TestPeriodProperties(TestCase):
@@ -464,6 +465,7 @@ def test_constructor_infer_freq(self):
 
         self.assertRaises(ValueError, Period, '2007-01-01 07:10:15.123456')
 
+
 def noWrap(item):
     return item
 
@@ -1723,11 +1725,11 @@ def test_badinput(self):
 
     def test_negative_ordinals(self):
         p = Period(ordinal=-1000, freq='A')
-
         p = Period(ordinal=0, freq='A')
 
-        idx = PeriodIndex(ordinal=[-1, 0, 1], freq='A')
-        idx = PeriodIndex(ordinal=np.array([-1, 0, 1]), freq='A')
+        idx1 = PeriodIndex(ordinal=[-1, 0, 1], freq='A')
+        idx2 = PeriodIndex(ordinal=np.array([-1, 0, 1]), freq='A')
+        assert_array_equal(idx1,idx2)
 
     def test_dti_to_period(self):
         dti = DatetimeIndex(start='1/1/2005', end='12/1/2005', freq='M')
@@ -1947,7 +1949,7 @@ def test_fields(self):
 
         end_intv = Period('2006-12-31', 'W')
         i1 = PeriodIndex(end=end_intv, periods=10)
-        self._check_all_fields(pi)
+        self._check_all_fields(i1)
 
     def _check_all_fields(self, periodindex):
         fields = ['year', 'month', 'day', 'hour', 'minute',
@@ -2047,6 +2049,11 @@ def test_pickle_freq(self):
         new_prng = pickle.loads(pickle.dumps(prng))
         self.assertEqual(new_prng.freq,'M')
 
+    def test_slice_keep_name(self):
+        idx = period_range('20010101', periods=10, freq='D', name='bob')
+        self.assertEqual(idx.name, idx[1:].name)
+
+
 def _permute(obj):
     return obj.take(np.random.permutation(len(obj)))
 
@@ -2152,8 +2159,6 @@ def test_greaterEqual(self):
 
     def test_greaterEqual_Raises_Value(self):
         self.assertRaises(ValueError, self.january1.__ge__, self.day)
-
-    def test_greaterEqual_Raises_Value(self):
         self.assertRaises(TypeError, self.january1.__ge__, 1)
 
     def test_smallerEqual(self):
@@ -2179,6 +2184,7 @@ def test_sort(self):
         correctPeriods = [self.january1, self.february, self.march]
         self.assertEqual(sorted(periods), correctPeriods)
 
+
 if __name__ == '__main__':
     import nose
     nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py
--- a/pandas/tseries/tests/test_timeseries.py
+++ b/pandas/tseries/tests/test_timeseries.py
@@ -32,8 +32,10 @@
 import pandas.index as _index
 
 import cPickle as pickle
+from pandas import read_pickle
 import pandas.core.datetools as dt
 from numpy.random import rand
+from numpy.testing import assert_array_equal
 from pandas.util.testing import assert_frame_equal
 import pandas.util.py3compat as py3compat
 from pandas.core.datetools import BDay
@@ -260,11 +262,13 @@ def test_indexing(self):
         # this is a single date, so will raise
         self.assertRaises(KeyError, df.__getitem__, df.index[2],)
 
+
 def assert_range_equal(left, right):
     assert(left.equals(right))
     assert(left.freq == right.freq)
     assert(left.tz == right.tz)
 
+
 class TestTimeSeries(unittest.TestCase):
     _multiprocess_can_split_ = True
 
@@ -1295,7 +1299,6 @@ def test_to_period(self):
     def test_to_period_tz(self):
         _skip_if_no_pytz()
         from dateutil.tz import tzlocal
-        from pandas.tseries.period import period_range
         from pytz import utc as UTC
 
         xp = date_range('1/1/2000', '4/1/2000').to_period()
@@ -1949,7 +1952,15 @@ def test_does_not_convert_mixed_integer(self):
         joined = cols.join(df.columns)
         self.assertEqual(cols.dtype, np.dtype('O'))
         self.assertEqual(cols.dtype, joined.dtype)
-        self.assert_(np.array_equal(cols.values, joined.values))
+        assert_array_equal(cols.values, joined.values)
+
+    def test_slice_keeps_name(self):
+        # GH4226
+        st = pd.Timestamp('2013-07-01 00:00:00', tz='America/Los_Angeles')
+        et = pd.Timestamp('2013-07-02 00:00:00', tz='America/Los_Angeles')
+        dr = pd.date_range(st, et, freq='H', name='timebucket')
+        self.assertEqual(dr[1:].name, dr.name)
+
 
 class TestLegacySupport(unittest.TestCase):
     _multiprocess_can_split_ = True
@@ -1971,7 +1982,6 @@ def setUpClass(cls):
 
     def test_pass_offset_warn(self):
         from StringIO import StringIO
-        import sys
         buf = StringIO()
 
         sys.stderr = buf
@@ -2064,7 +2074,7 @@ def test_unpickle_daterange(self):
         pth, _ = os.path.split(os.path.abspath(__file__))
         filepath = os.path.join(pth, 'data', 'daterange_073.pickle')
 
-        rng = com.load(filepath)
+        rng = read_pickle(filepath)
         self.assert_(type(rng[0]) == datetime)
         self.assert_(isinstance(rng.offset, offsets.BDay))
         self.assert_(rng.values.dtype == object)
@@ -2935,6 +2945,7 @@ def test_hash_equivalent(self):
         stamp = Timestamp(datetime(2011, 1, 1))
         self.assertEquals(d[stamp], 5)
 
+
 if __name__ == '__main__':
     nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
                    exit=False)