Repository: pandas-dev/pandas

Issue:
Missing Series name when using count() on groupby object
Consider this very basic group by operation:

``` python
df = pd.DataFrame({'col1': ['a', 'b', 'a', 'c'], 'col2': [1, 3, 2, 5]})
df_grp = df.groupby('col1')['col2']
```

When aggregating using `mean`, `min`, ... (everything but `count`) the resulting Series has `col2` as its name:

``` python
df_grp.mean()

col1
a       1.5
b       3.0
c       5.0
Name: col2, dtype: float64
```

However, if I use `count` for aggregation, the name is not set:

``` python
df_grp.count()

col1
a       2
b       1
c       1
dtype: int64
```

Not a big problem for a simple case like that, but I stumbled over that while working with a MultiIndex that needed to be reindexed and led to a `KeyError` due to that missing column name.



Discussion:
dup of #6124, closed by #6068

@jreback - Apologies for the delayed response, I have been out of the country for a few weeks.  This does not appear to be a duplicate of #6124 and I verified that #6068 does not fix the problem

The problem described in this issue can be traced to `SeriesGroupBy._wrap_applied_output` where it just returns an (unnamed) series.

ok thanks
do you want to try to incorporate a fix for that issue as well ? eg add a test and fix ( in another commit), but can use same pr


Resolving patch:
diff --git a/pandas/core/groupby.py b/pandas/core/groupby.py
--- a/pandas/core/groupby.py
+++ b/pandas/core/groupby.py
@@ -1783,7 +1783,8 @@ def _wrap_aggregated_output(self, output, names=None):
 
     def _wrap_applied_output(self, keys, values, not_indexed_same=False):
         if len(keys) == 0:
-            return Series([])
+            # GH #6265
+            return Series([], name=self.name)
 
         def _get_index():
             if self.grouper.nkeys > 1:
@@ -1805,7 +1806,8 @@ def _get_index():
             return self._concat_objects(keys, values,
                                         not_indexed_same=not_indexed_same)
         else:
-            return Series(values, index=_get_index())
+            # GH #6265
+            return Series(values, index=_get_index(), name=self.name)
 
     def _aggregate_named(self, func, *args, **kwargs):
         result = {}
@@ -2262,17 +2264,29 @@ def _wrap_applied_output(self, keys, values, not_indexed_same=False):
 
                 try:
                     if self.axis == 0:
+                        # GH6124 if the list of Series have a consistent name,
+                        # then propagate that name to the result.
+                        index = v.index.copy()
+                        if index.name is None:
+                            # Only propagate the series name to the result
+                            # if all series have a consistent name.  If the
+                            # series do not have a consistent name, do
+                            # nothing.
+                            names = set(v.name for v in values)
+                            if len(names) == 1:
+                                index.name = list(names)[0]
 
                         # normally use vstack as its faster than concat
                         # and if we have mi-columns
                         if not _np_version_under1p7 or isinstance(v.index,MultiIndex):
                             stacked_values = np.vstack([np.asarray(x) for x in values])
-                            result = DataFrame(stacked_values,index=key_index,columns=v.index)
+                            result = DataFrame(stacked_values,index=key_index,columns=index)
                         else:
                             # GH5788 instead of stacking; concat gets the dtypes correct
                             from pandas.tools.merge import concat
                             result = concat(values,keys=key_index,names=key_index.names,
                                             axis=self.axis).unstack()
+                            result.columns = index
                     else:
                         stacked_values = np.vstack([np.asarray(x) for x in values])
                         result = DataFrame(stacked_values.T,index=v.index,columns=key_index)


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
@@ -2057,6 +2057,41 @@ def test_groupby_series_with_name(self):
         self.assertIn('A', result2)
         self.assertIn('B', result2)
 
+    def test_seriesgroupby_name_attr(self):
+        # GH 6265
+        result = self.df.groupby('A')['C']
+        self.assertEquals(result.count().name, 'C')
+        self.assertEquals(result.mean().name, 'C')
+
+        testFunc = lambda x: np.sum(x)*2
+        self.assertEquals(result.agg(testFunc).name, 'C')
+
+    def test_groupby_name_propagation(self):
+        # GH 6124
+        def summarize(df, name=None):
+            return Series({
+                'count': 1,
+                'mean': 2,
+                'omissions': 3,
+            }, name=name)
+
+        def summarize_random_name(df):
+            # Provide a different name for each Series.  In this case, groupby
+            # should not attempt to propagate the Series name since they are
+            # inconsistent.
+            return Series({
+                'count': 1,
+                'mean': 2,
+                'omissions': 3,
+            }, name=df.iloc[0]['A'])
+
+        metrics = self.df.groupby('A').apply(summarize)
+        self.assertEqual(metrics.columns.name, None)
+        metrics = self.df.groupby('A').apply(summarize, 'metrics')
+        self.assertEqual(metrics.columns.name, 'metrics')
+        metrics = self.df.groupby('A').apply(summarize_random_name)
+        self.assertEqual(metrics.columns.name, None)
+
     def test_groupby_nonstring_columns(self):
         df = DataFrame([np.arange(10) for x in range(10)])
         grouped = df.groupby(0)