Repository: pandas-dev/pandas

Issue:
Indexing Regression in 0.13.0
In pandas 0.12 the order you indexed a DataFrame didn't matter, which I think is the correct behaviour:

``` python
In [6]: df = pd.DataFrame({'A': 5*[np.zeros(3)], 'B':5*[np.ones(3)]})

In [7]: df
Out[7]: 

    A   B
0   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
1   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
2   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
3   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
4   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]

In [8]: df['A'].iloc[2]
Out[8]: array([ 0., 0., 0.])

In [9]: df.iloc[2]['A']
Out[9]: array([ 0., 0., 0.])

In [10]: pd.__version__
Out[10]: '0.12.0'

In [11]: assert type(df.ix[2, 'A']) == type(df['A'].iloc[2]) == type(df.iloc[2]['A'])

In [12]: 
```

In pandas 0.13 if you index in a different order you can get a different type out which can be problematic for code expecting an array, especially because of the difference between array indexing and label indexing.

``` python
In [1]: df = pd.DataFrame({'A': 5*[np.zeros(3)], 'B':5*[np.ones(3)]})

In [2]: df
Out[2]: 

    A   B
0   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
1   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
2   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
3   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
4   [0.0, 0.0, 0.0] [1.0, 1.0, 1.0]
5 rows × 2 columns 

In [3]: df['A'].iloc[2]
Out[3]: array([ 0., 0., 0.])

In [4]: df.iloc[2]['A']
Out[4]: 
A 0
A 0
A 0
Name: 2, dtype: float64

In [5]: pd.__version__
Out[5]: '0.13.1'

In [6]: assert type(df.ix[2, 'A']) == type(df['A'].iloc[2]) == type(df.iloc[2]['A'])
Traceback (most recent call last):

  File "<ipython-input-11-946e15564ee1>", line 1, in <module>
    assert type(df.ix[2, 'A']) == type(df['A'].iloc[2]) == type(df.iloc[2]['A'])

AssertionError
```



Discussion:
 Storing lists of numpy arrays is not efficient nor really supported. 
Chained indexing is to blame, see here: 
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy'

which exposes how numpy creates / does not create a veiw

don't do it

I know it is inefficient, I said as much in my post on the mailing list.

I don't care whether I am returned a view or a copy - I'm not trying to assign to the data.

Returning a different type dependent on the order of chaining is never a desirable outcome and hence is a bug. It's certainly a regression since the example shown above worked perfectly well in pandas 0.12.


Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -1312,7 +1312,11 @@ def xs(self, key, axis=0, level=None, copy=True, drop_level=True):
             new_values, copy = self._data.fast_xs(loc, copy=copy)
 
             # may need to box a datelike-scalar
-            if not is_list_like(new_values):
+            #
+            # if we encounter an array-like and we only have 1 dim
+            # that means that their are list/ndarrays inside the Series!
+            # so just return them (GH 6394)
+            if not is_list_like(new_values) or self.ndim == 1:
                 return _maybe_box_datetimelike(new_values)
 
             result = Series(new_values, index=self.columns,
diff --git a/pandas/core/series.py b/pandas/core/series.py
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -477,8 +477,15 @@ def _slice(self, slobj, axis=0, raise_on_error=False, typ=None):
     def __getitem__(self, key):
         try:
             result = self.index.get_value(self, key)
-            if isinstance(result, np.ndarray):
-                return self._constructor(result,index=[key]*len(result)).__finalize__(self)
+
+            if not np.isscalar(result):
+                if is_list_like(result) and not isinstance(result, Series):
+
+                    # we need to box if we have a non-unique index here
+                    # otherwise have inline ndarray/lists
+                    if not self.index.is_unique:
+                        result = self._constructor(result,index=[key]*len(result)).__finalize__(self)
+
             return result
         except InvalidIndexError:
             pass


Test patch:
diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py
--- a/pandas/tests/test_indexing.py
+++ b/pandas/tests/test_indexing.py
@@ -551,6 +551,26 @@ def test_loc_setitem(self):
         expected = DataFrame({'a' : [0.5,-0.5,-1.5], 'b' : [0,1,2] })
         assert_frame_equal(df,expected)
 
+    def test_chained_getitem_with_lists(self):
+
+        # GH6394
+        # Regression in chained getitem indexing with embedded list-like from 0.12
+        def check(result, expected):
+            self.assert_numpy_array_equal(result,expected)
+            tm.assert_isinstance(result, np.ndarray)
+
+
+        df = DataFrame({'A': 5*[np.zeros(3)], 'B':5*[np.ones(3)]})
+        expected = df['A'].iloc[2]
+        result = df.loc[2,'A']
+        check(result, expected)
+        result2 = df.iloc[2]['A']
+        check(result2, expected)
+        result3 = df['A'].loc[2]
+        check(result3, expected)
+        result4 = df['A'].iloc[2]
+        check(result4, expected)
+
     def test_loc_getitem_int(self):
 
         # int label
diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py
--- a/pandas/tests/test_series.py
+++ b/pandas/tests/test_series.py
@@ -949,6 +949,12 @@ def test_getitem_dups_with_missing(self):
         result = s[['foo', 'bar', 'bah', 'bam']]
         assert_series_equal(result, expected)
 
+    def test_getitem_dups(self):
+        s = Series(range(5),index=['A','A','B','C','C'])
+        expected = Series([3,4],index=['C','C'])
+        result = s['C']
+        assert_series_equal(result, expected)
+
     def test_setitem_ambiguous_keyerror(self):
         s = Series(lrange(10), index=lrange(0, 20, 2))
 
@@ -4813,6 +4819,7 @@ def test_apply_args(self):
 
         result = s.apply(str.split, args=(',',))
         self.assertEqual(result[0], ['foo', 'bar'])
+        tm.assert_isinstance(result[0], list)
 
     def test_align(self):
         def _check_align(a, b, how='left', fill=None):