Repository: pandas-dev/pandas

Issue:
Allow Timeseries-like shortcuts in dataframes __getitem__
``` python
In [17]: s=pd.date_range("2001-1-1", periods=12)
In [20]: ts['2001']
Out[20]: 
2001-01-01     0
2001-01-02     1
2001-01-03     2
2001-01-04     3
2001-01-05     4
2001-01-06     5
2001-01-07     6
2001-01-08     7
2001-01-09     8
2001-01-10     9
2001-01-11    10
2001-01-12    11
Freq: D, dtype: int64

In [24]: df=pd.DataFrame(range(len(s)),index=s)
In [25]: df['2001']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
KeyError: u'no item named 2001'

In [27]: df.index is ts.index
Out[27]: True
```

related #2091



Discussion:

Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -28,7 +28,7 @@
 from pandas.core.generic import NDFrame
 from pandas.core.index import Index, MultiIndex, _ensure_index
 from pandas.core.indexing import (_NDFrameIndexer, _maybe_droplevels,
-                                  _is_index_slice, _check_bool_indexer,
+                                  _convert_to_index_sliceable, _check_bool_indexer,
                                   _maybe_convert_indices)
 from pandas.core.internals import BlockManager, make_block, form_blocks
 from pandas.core.series import Series, _radd_compat
@@ -1864,10 +1864,13 @@ def iget_value(self, i, j):
         return self.iat[i,j]
 
     def __getitem__(self, key):
-        if isinstance(key, slice):
-            # slice rows
-            return self._getitem_slice(key)
-        elif isinstance(key, (np.ndarray, list)):
+
+        # see if we can slice the rows
+        indexer = _convert_to_index_sliceable(self, key)
+        if indexer is not None:
+            return self._getitem_slice(indexer)
+
+        if isinstance(key, (np.ndarray, list)):
             # either boolean or fancy integer index
             return self._getitem_array(key)
         elif isinstance(key, DataFrame):
@@ -1879,14 +1882,7 @@ def __getitem__(self, key):
             return self._get_item_cache(key)
 
     def _getitem_slice(self, key):
-        idx_type = self.index.inferred_type
-        if idx_type == 'floating':
-            indexer = self.ix._convert_to_indexer(key, axis=0)
-        elif idx_type == 'integer' or _is_index_slice(key):
-            indexer = key
-        else:
-            indexer = self.ix._convert_to_indexer(key, axis=0)
-        return self._slice(indexer, axis=0)
+        return self._slice(key, axis=0)
 
     def _getitem_array(self, key):
         # also raises Exception if object array with NA values
@@ -1982,10 +1978,12 @@ def __setattr__(self, name, value):
                 object.__setattr__(self, name, value)
 
     def __setitem__(self, key, value):
-        if isinstance(key, slice):
-            # slice rows
-            self._setitem_slice(key, value)
-        elif isinstance(key, (np.ndarray, list)):
+        # see if we can slice the rows
+        indexer = _convert_to_index_sliceable(self, key)
+        if indexer is not None:
+            return self._setitem_slice(indexer, value)
+
+        if isinstance(key, (np.ndarray, list)):
             self._setitem_array(key, value)
         elif isinstance(key, DataFrame):
             self._setitem_frame(key, value)
@@ -1994,14 +1992,7 @@ def __setitem__(self, key, value):
             self._set_item(key, value)
 
     def _setitem_slice(self, key, value):
-        idx_type = self.index.inferred_type
-        if idx_type == 'floating':
-            indexer = self.ix._convert_to_indexer(key, axis=0)
-        elif idx_type == 'integer' or _is_index_slice(key):
-            indexer = key
-        else:
-            indexer = self.ix._convert_to_indexer(key, axis=0)
-        self.ix._setitem_with_indexer(indexer, value)
+        self.ix._setitem_with_indexer(key, value)
 
     def _setitem_array(self, key, value):
         # also raises Exception if object array with NA values
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -827,6 +827,30 @@ def _convert_key(self, key):
 _eps = np.finfo('f4').eps
 
 
+def _convert_to_index_sliceable(obj, key):
+    """ if we are index sliceable, then return my slicer, otherwise return None """
+    idx = obj.index
+    if isinstance(key, slice):
+        idx_type = idx.inferred_type
+        if idx_type == 'floating':
+            indexer = obj.ix._convert_to_indexer(key, axis=0)
+        elif idx_type == 'integer' or _is_index_slice(key):
+            indexer = key
+        else:
+            indexer = obj.ix._convert_to_indexer(key, axis=0)
+        return indexer
+
+    elif isinstance(key, basestring):
+
+        # we need a timelike key here
+        if idx.is_all_dates:
+            try:
+                return idx._get_string_slice(key)
+            except:
+                return None
+
+    return None
+
 def _is_index_slice(obj):
     def _is_valid_index(x):
         return (com.is_integer(x) or com.is_float(x)


Test patch:
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
@@ -196,12 +196,34 @@ def test_indexing_unordered(self):
         for t in result.index:
             self.assertTrue(t.year == 2005)
 
+    def test_indexing(self):
+
+        idx = date_range("2001-1-1", periods=20, freq='M')
+        ts = Series(np.random.rand(len(idx)),index=idx)
+
+        # getting
+
+        # GH 3070, make sure semantics work on Series/Frame
+        expected = ts['2001']
+        
+        df = DataFrame(dict(A = ts))
+        result = df['2001']['A']
+        assert_series_equal(expected,result)
+
+        # setting
+        ts['2001'] = 1
+        expected = ts['2001']
+
+        df.loc['2001','A'] = 1
+
+        result = df['2001']['A']
+        assert_series_equal(expected,result)
+
 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