Repository: pandas-dev/pandas

Issue:
bug in groupby.apply?, goes away if I copy the data.
Tested pandas 0.11, and 0.12, both exhibit this problem

```
import pandas as pd

data = pd.DataFrame({'id_field' : [100, 100, 200, 300], 'category' : ['a','b','c','c'], 'value' : [1,2,3,4]})

def filt1(x):
    if x.shape[0] == 1:
        return x.copy()
    else:
        return x[x.category == 'c']


def filt2(x):
    if x.shape[0] == 1:
        return x
    else:
        return x[x.category == 'c']

#works fine                                                                                                                                                                                                                                    
print data.groupby('id_field').apply(filt1)

#throws error                                                                                                                                                                                                                                  
print data.groupby('id_field').apply(filt2)
```



Discussion:

Resolving patch:
diff --git a/pandas/core/groupby.py b/pandas/core/groupby.py
--- a/pandas/core/groupby.py
+++ b/pandas/core/groupby.py
@@ -543,13 +543,13 @@ def head(self, n=5):
 
         >>> df = DataFrame([[1, 2], [1, 4], [5, 6]],
                             columns=['A', 'B'])
-        >>> df.groupby('A', as_index=False).head(1) 
+        >>> df.groupby('A', as_index=False).head(1)
            A  B
         0  1  2
         2  5  6
         >>> df.groupby('A').head(1)
              A  B
-        A        
+        A
         1 0  1  2
         5 2  5  6
 
@@ -572,16 +572,16 @@ def tail(self, n=5):
 
         >>> df = DataFrame([[1, 2], [1, 4], [5, 6]],
                             columns=['A', 'B'])
-        >>> df.groupby('A', as_index=False).tail(1) 
+        >>> df.groupby('A', as_index=False).tail(1)
            A  B
         0  1  2
         2  5  6
         >>> df.groupby('A').head(1)
              A  B
-        A        
+        A
         1 0  1  2
         5 2  5  6
-        
+
         """
         rng = np.arange(0, -self.grouper._max_groupsize, -1, dtype='int64')
         in_tail = self._cumcount_array(rng, ascending=False) > -n
@@ -2149,6 +2149,12 @@ def _wrap_applied_output(self, keys, values, not_indexed_same=False):
                                 keys, values, not_indexed_same=not_indexed_same
                             )
 
+                        # still a series
+                        # path added as of GH 5545
+                        elif all_indexed_same:
+                            from pandas.tools.merge import concat
+                            return concat(values)
+
                     if not all_indexed_same:
                         return self._concat_objects(
                             keys, values, not_indexed_same=not_indexed_same


Test patch:
diff --git a/pandas/tests/test_groupby.py b/pandas/tests/test_groupby.py
--- a/pandas/tests/test_groupby.py
+++ b/pandas/tests/test_groupby.py
@@ -1214,7 +1214,7 @@ def test_groupby_as_index_apply(self):
         res_not_as_apply = g_not_as.apply(lambda x: x.head(2)).index
 
         # apply doesn't maintain the original ordering
-        exp_not_as_apply = Index([0, 2, 1, 4])        
+        exp_not_as_apply = Index([0, 2, 1, 4])
         exp_as_apply = MultiIndex.from_tuples([(1, 0), (1, 2), (2, 1), (3, 4)])
 
         assert_index_equal(res_as_apply, exp_as_apply)
@@ -1845,6 +1845,28 @@ def test_apply_corner(self):
         expected = self.tsframe * 2
         assert_frame_equal(result, expected)
 
+    def test_apply_without_copy(self):
+        # GH 5545
+        # returning a non-copy in an applied function fails
+
+        data = DataFrame({'id_field' : [100, 100, 200, 300], 'category' : ['a','b','c','c'], 'value' : [1,2,3,4]})
+
+        def filt1(x):
+            if x.shape[0] == 1:
+                return x.copy()
+            else:
+                return x[x.category == 'c']
+
+        def filt2(x):
+            if x.shape[0] == 1:
+                return x
+            else:
+                return x[x.category == 'c']
+
+        expected = data.groupby('id_field').apply(filt1)
+        result = data.groupby('id_field').apply(filt2)
+        assert_frame_equal(result,expected)
+
     def test_apply_use_categorical_name(self):
         from pandas import qcut
         cats = qcut(self.df.C, 4)
@@ -2638,7 +2660,7 @@ def test_cumcount_mi(self):
         expected = Series([0, 1, 2, 0, 3], index=mi)
 
         assert_series_equal(expected, g.cumcount())
-        assert_series_equal(expected, sg.cumcount())        
+        assert_series_equal(expected, sg.cumcount())
 
     def test_cumcount_groupby_not_col(self):
         df = DataFrame([['a'], ['a'], ['a'], ['b'], ['a']], columns=['A'], index=[0] * 5)
@@ -2895,7 +2917,7 @@ def test_filter_maintains_ordering(self):
     def test_filter_and_transform_with_non_unique_int_index(self):
         # GH4620
         index = [1, 1, 1, 2, 1, 1, 0, 1]
-        df = DataFrame({'pid' : [1,1,1,2,2,3,3,3], 
+        df = DataFrame({'pid' : [1,1,1,2,2,3,3,3],
                        'tag' : [23,45,62,24,45,34,25,62]}, index=index)
         grouped_df = df.groupby('tag')
         ser = df['pid']
@@ -2923,7 +2945,7 @@ def test_filter_and_transform_with_non_unique_int_index(self):
         # ^ made manually because this can get confusing!
         assert_series_equal(actual, expected)
 
-        # Transform Series 
+        # Transform Series
         actual = grouped_ser.transform(len)
         expected = Series([1, 2, 2, 1, 2, 1, 1, 2], index)
         assert_series_equal(actual, expected)