Repository: pandas-dev/pandas

Issue:
Series losts its name after set_categories
#### Code Works fine
``` python
(
    Series([1,2,3], name='A')
    .astype('category')
    .name
)
```
I get 'A'

#### Code in trouble
``` python
(
    Series([1,2,3], name='A')
    .astype('category')
    .cat.set_categories([1,2,3])
    .name
)
```
I get NoneType

I think its better to keep original name after set_categories()



Discussion:
@jreback : This seems to ring a bell for some reason.  Maybe it was regarding `Series` in general?

In any case, it seems reasonable that the name should be kept.  Feel free to investigate and see where things can be changed to create the desired behavior.
I wonder if https://github.com/pandas-dev/pandas/blob/f11bbf2f505d81900cc83ce387a6a1b1d2a2f866/pandas/core/categorical.py#L2071 should pass through `name=self.name`.
This would affect all of the delegated method:

-  'add_categories',
-  'as_ordered',
-  'as_unordered',
-  'remove_categories',
-  'remove_unused_categories',
-  'rename_categories',
-  'reorder_categories',
- 'set_categories'

@junjiecai mind submitting a pull request with [that change](https://github.com/pandas-dev/pandas/issues/17509#issuecomment-329238872) and a simple test for each of those to verify that the name is passed through?

Resolving patch:
diff --git a/pandas/core/categorical.py b/pandas/core/categorical.py
--- a/pandas/core/categorical.py
+++ b/pandas/core/categorical.py
@@ -2054,9 +2054,10 @@ class CategoricalAccessor(PandasDelegate, NoNewAttributesMixin):
 
     """
 
-    def __init__(self, values, index):
+    def __init__(self, values, index, name):
         self.categorical = values
         self.index = index
+        self.name = name
         self._freeze()
 
     def _delegate_property_get(self, name):
@@ -2075,14 +2076,15 @@ def _delegate_method(self, name, *args, **kwargs):
         method = getattr(self.categorical, name)
         res = method(*args, **kwargs)
         if res is not None:
-            return Series(res, index=self.index)
+            return Series(res, index=self.index, name=self.name)
 
     @classmethod
     def _make_accessor(cls, data):
         if not is_categorical_dtype(data.dtype):
             raise AttributeError("Can only use .cat accessor with a "
                                  "'category' dtype")
-        return CategoricalAccessor(data.values, data.index)
+        return CategoricalAccessor(data.values, data.index,
+                                   getattr(data, 'name', None),)
 
 
 CategoricalAccessor._add_delegate_accessors(delegate=Categorical,


Test patch:
diff --git a/pandas/tests/test_categorical.py b/pandas/tests/test_categorical.py
--- a/pandas/tests/test_categorical.py
+++ b/pandas/tests/test_categorical.py
@@ -57,6 +57,25 @@ def test_getitem_listlike(self):
         expected = c[np.array([100000]).astype(np.int64)].codes
         tm.assert_numpy_array_equal(result, expected)
 
+    @pytest.mark.parametrize(
+        "method",
+        [
+            lambda x: x.cat.set_categories([1, 2, 3]),
+            lambda x: x.cat.reorder_categories([2, 3, 1], ordered=True),
+            lambda x: x.cat.rename_categories([1, 2, 3]),
+            lambda x: x.cat.remove_unused_categories(),
+            lambda x: x.cat.remove_categories([2]),
+            lambda x: x.cat.add_categories([4]),
+            lambda x: x.cat.as_ordered(),
+            lambda x: x.cat.as_unordered(),
+        ])
+    def test_getname_categorical_accessor(self, method):
+        # GH 17509
+        s = pd.Series([1, 2, 3], name='A').astype('category')
+        expected = 'A'
+        result = method(s).name
+        assert result == expected
+
     def test_getitem_category_type(self):
         # GH 14580
         # test iloc() on Series with Categorical data