We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Execute groupby failed
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**

`df.groupby().execute()` may fail.

**To Reproduce**
To help us reproducing this bug, please provide information below:
1. Your Python version
2. The version of Mars you use
3. Versions of crucial packages, such as numpy, scipy and protobuf
4. Full stack of the error.
5. Minimized code to reproduce the error.

```
In [1]: import pandas as pd; import numpy as np                                 

In [2]: df = pd.DataFrame(np.random.rand(4, 3), index=np.arange(5, 1, -1))      

In [4]: import mars.dataframe as md                                             

In [5]: mdf = md.DataFrame(df)                                                  

In [6]: mdf.groupby(0).execute()                                                
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-491b51043e08> in <module>
----> 1 mdf.groupby(0).execute()

~/Workspace/mars/mars/core.py in execute(self, session, **kw)
    426         if session is None:
    427             session = Session.default_or_local()
--> 428         return session.run(self, **kw)
    429 
    430     def fetch(self, session=None, **kw):

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
    181         tileables = tuple(mt.tensor(t) if not isinstance(t, (Entity, Base)) else t
    182                           for t in tileables)
--> 183         result = self._sess.run(*tileables, **kw)
    184 
    185         for t in tileables:

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
     88             # set number of running cores
     89             self.context.set_ncores(kw['n_parallel'])
---> 90             res = self._executor.execute_tileables(tileables, **kw)
     91             return res
     92 

~/Workspace/mars/mars/utils.py in _wrapped(*args, **kwargs)
    380                 _kernel_mode.eager = False
    381             _kernel_mode.eager_count = enter_eager_count + 1
--> 382             return func(*args, **kwargs)
    383         finally:
    384             _kernel_mode.eager_count -= 1

~/Workspace/mars/mars/utils.py in inner(*args, **kwargs)
    468     def inner(*args, **kwargs):
    469         with build_mode():
--> 470             return func(*args, **kwargs)
    471     return inner
    472 

~/Workspace/mars/mars/executor.py in execute_tileables(self, tileables, fetch, n_parallel, n_thread, print_progress, mock, compose)
    828                 # update shape of tileable and its chunks whatever it's successful or not
    829                 self._update_tileable_and_chunk_shape(
--> 830                     tileable_graph, chunk_result, chunk_graph_builder.interrupted_ops)
    831                 if chunk_graph_builder.done:
    832                     if len(intermediate_result_keys) > 0:

~/Workspace/mars/mars/executor.py in _update_tileable_and_chunk_shape(self, tileable_graph, chunk_result, failed_ops)
    726                     continue
    727                 for c in tiled_n.chunks:
--> 728                     c.data._shape = chunk_result[c.key].shape
    729                 new_nsplits = self.get_tileable_nsplits(n, chunk_result=chunk_result)
    730                 for node in (n, tiled_n):

~/Workspace/mars/mars/lib/groupby_wrapper.py in __getattr__(self, item)
     74         if item in getattr(self.obj, 'columns', ()):
     75             return self.__getitem__(item)
---> 76         return getattr(self.groupby_obj, item)
     77 
     78     def __iter__(self):

~/miniconda3/lib/python3.7/site-packages/pandas/core/groupby/groupby.py in __getattr__(self, attr)
    578 
    579         raise AttributeError(
--> 580             f"'{type(self).__name__}' object has no attribute '{attr}'"
    581         )
    582 

AttributeError: 'DataFrameGroupBy' object has no attribute 'shape'
```



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mars/lib/groupby_wrapper.py`
Content:
```
1 # Copyright 1999-2020 Alibaba Group Holding Ltd.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from collections.abc import Iterable
16 
17 import cloudpickle
18 from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy
19 
20 
21 class GroupByWrapper:
22     def __init__(self, obj, groupby_obj=None, keys=None, axis=0, level=None, grouper=None,
23                  exclusions=None, selection=None, as_index=True, sort=True,
24                  group_keys=True, squeeze=False, observed=False, mutated=False,
25                  grouper_cache=None):
26 
27         def fill_value(v, key):
28             return v if v is not None or groupby_obj is None else getattr(groupby_obj, key)
29 
30         self.obj = obj
31         self.keys = fill_value(keys, 'keys')
32         self.axis = fill_value(axis, 'axis')
33         self.level = fill_value(level, 'level')
34         self.exclusions = fill_value(exclusions, 'exclusions')
35         self.selection = selection
36         self.as_index = fill_value(as_index, 'as_index')
37         self.sort = fill_value(sort, 'sort')
38         self.group_keys = fill_value(group_keys, 'group_keys')
39         self.squeeze = fill_value(squeeze, 'squeeze')
40         self.observed = fill_value(observed, 'observed')
41         self.mutated = fill_value(mutated, 'mutated')
42 
43         if groupby_obj is None:
44             if obj.ndim == 2:
45                 self.groupby_obj = DataFrameGroupBy(
46                     obj, keys=keys, axis=axis, level=level, grouper=grouper, exclusions=exclusions,
47                     as_index=as_index, group_keys=group_keys, squeeze=squeeze, observed=observed,
48                     mutated=mutated)
49             else:
50                 self.groupby_obj = SeriesGroupBy(
51                     obj, keys=keys, axis=axis, level=level, grouper=grouper, exclusions=exclusions,
52                     as_index=as_index, group_keys=group_keys, squeeze=squeeze, observed=observed,
53                     mutated=mutated)
54         else:
55             self.groupby_obj = groupby_obj
56 
57         self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)
58 
59         if grouper_cache:
60             self.groupby_obj.grouper._cache = grouper_cache
61         if selection:
62             self.groupby_obj = self.groupby_obj[selection]
63 
64     def __getitem__(self, item):
65         return GroupByWrapper(
66             self.obj, keys=self.keys, axis=self.axis, level=self.level,
67             grouper=self.groupby_obj.grouper, exclusions=self.exclusions, selection=item,
68             as_index=self.as_index, sort=self.sort, group_keys=self.group_keys,
69             squeeze=self.squeeze, observed=self.observed, mutated=self.mutated)
70 
71     def __getattr__(self, item):
72         if item.startswith('_'):  # pragma: no cover
73             return object.__getattribute__(self, item)
74         if item in getattr(self.obj, 'columns', ()):
75             return self.__getitem__(item)
76         return getattr(self.groupby_obj, item)
77 
78     def __iter__(self):
79         return self.groupby_obj.__iter__()
80 
81     @property
82     def empty(self):
83         return self.obj.empty
84 
85     def to_tuple(self, truncate=False, pickle_function=False):
86         if self.selection and truncate:
87             if isinstance(self.selection, Iterable) and not isinstance(self.selection, str):
88                 item_list = list(self.selection)
89             else:
90                 item_list = [self.selection]
91             item_set = set(item_list)
92 
93             if isinstance(self.keys, list):
94                 sel_keys = self.keys
95             elif self.keys in self.obj.columns:
96                 sel_keys = [self.keys]
97             else:
98                 sel_keys = []
99 
100             all_items = item_list + [k for k in sel_keys or () if k not in item_set]
101             if set(all_items) == set(self.obj.columns):
102                 obj = self.obj
103             else:
104                 obj = self.obj[all_items]
105         else:
106             obj = self.obj
107 
108         if pickle_function and callable(self.keys):
109             keys = cloudpickle.dumps(self.keys)
110         else:
111             keys = self.keys
112 
113         return obj, keys, self.axis, self.level, self.exclusions, self.selection, \
114             self.as_index, self.sort, self.group_keys, self.squeeze, self.observed, \
115             self.mutated, getattr(self.groupby_obj.grouper, '_cache', dict())
116 
117     @classmethod
118     def from_tuple(cls, tp):
119         obj, keys, axis, level, exclusions, selection, as_index, sort, group_keys, squeeze, \
120             observed, mutated, grouper_cache = tp
121 
122         if isinstance(keys, (bytes, bytearray)):
123             keys = cloudpickle.loads(keys)
124 
125         return cls(obj, keys=keys, axis=axis, level=level, exclusions=exclusions, selection=selection,
126                    as_index=as_index, sort=sort, group_keys=group_keys, squeeze=squeeze, observed=observed,
127                    mutated=mutated, grouper_cache=grouper_cache)
128 
129 
130 def wrapped_groupby(obj, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True,
131                     squeeze=False, observed=False):
132     groupby_obj = obj.groupby(by=by, axis=axis, level=level, as_index=as_index, sort=sort,
133                               group_keys=group_keys, squeeze=squeeze, observed=observed)
134     return GroupByWrapper(obj, groupby_obj=groupby_obj)
135
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mars/lib/groupby_wrapper.py b/mars/lib/groupby_wrapper.py
--- a/mars/lib/groupby_wrapper.py
+++ b/mars/lib/groupby_wrapper.py
@@ -54,13 +54,13 @@
         else:
             self.groupby_obj = groupby_obj
 
-        self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)
-
         if grouper_cache:
             self.groupby_obj.grouper._cache = grouper_cache
         if selection:
             self.groupby_obj = self.groupby_obj[selection]
 
+        self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)
+
     def __getitem__(self, item):
         return GroupByWrapper(
             self.obj, keys=self.keys, axis=self.axis, level=self.level,
@@ -82,6 +82,13 @@
     def empty(self):
         return self.obj.empty
 
+    @property
+    def shape(self):
+        shape = list(self.groupby_obj.obj.shape)
+        if self.is_frame and self.selection:
+            shape[1] = len(self.selection)
+        return tuple(shape)
+
     def to_tuple(self, truncate=False, pickle_function=False):
         if self.selection and truncate:
             if isinstance(self.selection, Iterable) and not isinstance(self.selection, str):

{"golden_diff": "diff --git a/mars/lib/groupby_wrapper.py b/mars/lib/groupby_wrapper.py\n--- a/mars/lib/groupby_wrapper.py\n+++ b/mars/lib/groupby_wrapper.py\n@@ -54,13 +54,13 @@\n         else:\n             self.groupby_obj = groupby_obj\n \n-        self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)\n-\n         if grouper_cache:\n             self.groupby_obj.grouper._cache = grouper_cache\n         if selection:\n             self.groupby_obj = self.groupby_obj[selection]\n \n+        self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)\n+\n     def __getitem__(self, item):\n         return GroupByWrapper(\n             self.obj, keys=self.keys, axis=self.axis, level=self.level,\n@@ -82,6 +82,13 @@\n     def empty(self):\n         return self.obj.empty\n \n+    @property\n+    def shape(self):\n+        shape = list(self.groupby_obj.obj.shape)\n+        if self.is_frame and self.selection:\n+            shape[1] = len(self.selection)\n+        return tuple(shape)\n+\n     def to_tuple(self, truncate=False, pickle_function=False):\n         if self.selection and truncate:\n             if isinstance(self.selection, Iterable) and not isinstance(self.selection, str):\n", "issue": "[BUG] Execute groupby failed\n<!--\r\nThank you for your contribution!\r\n\r\nPlease review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.\r\n-->\r\n\r\n**Describe the bug**\r\n\r\n`df.groupby().execute()` may fail.\r\n\r\n**To Reproduce**\r\nTo help us reproducing this bug, please provide information below:\r\n1. Your Python version\r\n2. The version of Mars you use\r\n3. Versions of crucial packages, such as numpy, scipy and protobuf\r\n4. Full stack of the error.\r\n5. Minimized code to reproduce the error.\r\n\r\n```\r\nIn [1]: import pandas as pd; import numpy as np                                 \r\n\r\nIn [2]: df = pd.DataFrame(np.random.rand(4, 3), index=np.arange(5, 1, -1))      \r\n\r\nIn [4]: import mars.dataframe as md                                             \r\n\r\nIn [5]: mdf = md.DataFrame(df)                                                  \r\n\r\nIn [6]: mdf.groupby(0).execute()                                                \r\n---------------------------------------------------------------------------\r\nAttributeError                            Traceback (most recent call last)\r\n<ipython-input-6-491b51043e08> in <module>\r\n----> 1 mdf.groupby(0).execute()\r\n\r\n~/Workspace/mars/mars/core.py in execute(self, session, **kw)\r\n    426         if session is None:\r\n    427             session = Session.default_or_local()\r\n--> 428         return session.run(self, **kw)\r\n    429 \r\n    430     def fetch(self, session=None, **kw):\r\n\r\n~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)\r\n    181         tileables = tuple(mt.tensor(t) if not isinstance(t, (Entity, Base)) else t\r\n    182                           for t in tileables)\r\n--> 183         result = self._sess.run(*tileables, **kw)\r\n    184 \r\n    185         for t in tileables:\r\n\r\n~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)\r\n     88             # set number of running cores\r\n     89             self.context.set_ncores(kw['n_parallel'])\r\n---> 90             res = self._executor.execute_tileables(tileables, **kw)\r\n     91             return res\r\n     92 \r\n\r\n~/Workspace/mars/mars/utils.py in _wrapped(*args, **kwargs)\r\n    380                 _kernel_mode.eager = False\r\n    381             _kernel_mode.eager_count = enter_eager_count + 1\r\n--> 382             return func(*args, **kwargs)\r\n    383         finally:\r\n    384             _kernel_mode.eager_count -= 1\r\n\r\n~/Workspace/mars/mars/utils.py in inner(*args, **kwargs)\r\n    468     def inner(*args, **kwargs):\r\n    469         with build_mode():\r\n--> 470             return func(*args, **kwargs)\r\n    471     return inner\r\n    472 \r\n\r\n~/Workspace/mars/mars/executor.py in execute_tileables(self, tileables, fetch, n_parallel, n_thread, print_progress, mock, compose)\r\n    828                 # update shape of tileable and its chunks whatever it's successful or not\r\n    829                 self._update_tileable_and_chunk_shape(\r\n--> 830                     tileable_graph, chunk_result, chunk_graph_builder.interrupted_ops)\r\n    831                 if chunk_graph_builder.done:\r\n    832                     if len(intermediate_result_keys) > 0:\r\n\r\n~/Workspace/mars/mars/executor.py in _update_tileable_and_chunk_shape(self, tileable_graph, chunk_result, failed_ops)\r\n    726                     continue\r\n    727                 for c in tiled_n.chunks:\r\n--> 728                     c.data._shape = chunk_result[c.key].shape\r\n    729                 new_nsplits = self.get_tileable_nsplits(n, chunk_result=chunk_result)\r\n    730                 for node in (n, tiled_n):\r\n\r\n~/Workspace/mars/mars/lib/groupby_wrapper.py in __getattr__(self, item)\r\n     74         if item in getattr(self.obj, 'columns', ()):\r\n     75             return self.__getitem__(item)\r\n---> 76         return getattr(self.groupby_obj, item)\r\n     77 \r\n     78     def __iter__(self):\r\n\r\n~/miniconda3/lib/python3.7/site-packages/pandas/core/groupby/groupby.py in __getattr__(self, attr)\r\n    578 \r\n    579         raise AttributeError(\r\n--> 580             f\"'{type(self).__name__}' object has no attribute '{attr}'\"\r\n    581         )\r\n    582 \r\n\r\nAttributeError: 'DataFrameGroupBy' object has no attribute 'shape'\r\n```\r\n\r\n\n", "before_files": [{"content": "# Copyright 1999-2020 Alibaba Group Holding Ltd.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom collections.abc import Iterable\n\nimport cloudpickle\nfrom pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy\n\n\nclass GroupByWrapper:\n    def __init__(self, obj, groupby_obj=None, keys=None, axis=0, level=None, grouper=None,\n                 exclusions=None, selection=None, as_index=True, sort=True,\n                 group_keys=True, squeeze=False, observed=False, mutated=False,\n                 grouper_cache=None):\n\n        def fill_value(v, key):\n            return v if v is not None or groupby_obj is None else getattr(groupby_obj, key)\n\n        self.obj = obj\n        self.keys = fill_value(keys, 'keys')\n        self.axis = fill_value(axis, 'axis')\n        self.level = fill_value(level, 'level')\n        self.exclusions = fill_value(exclusions, 'exclusions')\n        self.selection = selection\n        self.as_index = fill_value(as_index, 'as_index')\n        self.sort = fill_value(sort, 'sort')\n        self.group_keys = fill_value(group_keys, 'group_keys')\n        self.squeeze = fill_value(squeeze, 'squeeze')\n        self.observed = fill_value(observed, 'observed')\n        self.mutated = fill_value(mutated, 'mutated')\n\n        if groupby_obj is None:\n            if obj.ndim == 2:\n                self.groupby_obj = DataFrameGroupBy(\n                    obj, keys=keys, axis=axis, level=level, grouper=grouper, exclusions=exclusions,\n                    as_index=as_index, group_keys=group_keys, squeeze=squeeze, observed=observed,\n                    mutated=mutated)\n            else:\n                self.groupby_obj = SeriesGroupBy(\n                    obj, keys=keys, axis=axis, level=level, grouper=grouper, exclusions=exclusions,\n                    as_index=as_index, group_keys=group_keys, squeeze=squeeze, observed=observed,\n                    mutated=mutated)\n        else:\n            self.groupby_obj = groupby_obj\n\n        self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)\n\n        if grouper_cache:\n            self.groupby_obj.grouper._cache = grouper_cache\n        if selection:\n            self.groupby_obj = self.groupby_obj[selection]\n\n    def __getitem__(self, item):\n        return GroupByWrapper(\n            self.obj, keys=self.keys, axis=self.axis, level=self.level,\n            grouper=self.groupby_obj.grouper, exclusions=self.exclusions, selection=item,\n            as_index=self.as_index, sort=self.sort, group_keys=self.group_keys,\n            squeeze=self.squeeze, observed=self.observed, mutated=self.mutated)\n\n    def __getattr__(self, item):\n        if item.startswith('_'):  # pragma: no cover\n            return object.__getattribute__(self, item)\n        if item in getattr(self.obj, 'columns', ()):\n            return self.__getitem__(item)\n        return getattr(self.groupby_obj, item)\n\n    def __iter__(self):\n        return self.groupby_obj.__iter__()\n\n    @property\n    def empty(self):\n        return self.obj.empty\n\n    def to_tuple(self, truncate=False, pickle_function=False):\n        if self.selection and truncate:\n            if isinstance(self.selection, Iterable) and not isinstance(self.selection, str):\n                item_list = list(self.selection)\n            else:\n                item_list = [self.selection]\n            item_set = set(item_list)\n\n            if isinstance(self.keys, list):\n                sel_keys = self.keys\n            elif self.keys in self.obj.columns:\n                sel_keys = [self.keys]\n            else:\n                sel_keys = []\n\n            all_items = item_list + [k for k in sel_keys or () if k not in item_set]\n            if set(all_items) == set(self.obj.columns):\n                obj = self.obj\n            else:\n                obj = self.obj[all_items]\n        else:\n            obj = self.obj\n\n        if pickle_function and callable(self.keys):\n            keys = cloudpickle.dumps(self.keys)\n        else:\n            keys = self.keys\n\n        return obj, keys, self.axis, self.level, self.exclusions, self.selection, \\\n            self.as_index, self.sort, self.group_keys, self.squeeze, self.observed, \\\n            self.mutated, getattr(self.groupby_obj.grouper, '_cache', dict())\n\n    @classmethod\n    def from_tuple(cls, tp):\n        obj, keys, axis, level, exclusions, selection, as_index, sort, group_keys, squeeze, \\\n            observed, mutated, grouper_cache = tp\n\n        if isinstance(keys, (bytes, bytearray)):\n            keys = cloudpickle.loads(keys)\n\n        return cls(obj, keys=keys, axis=axis, level=level, exclusions=exclusions, selection=selection,\n                   as_index=as_index, sort=sort, group_keys=group_keys, squeeze=squeeze, observed=observed,\n                   mutated=mutated, grouper_cache=grouper_cache)\n\n\ndef wrapped_groupby(obj, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True,\n                    squeeze=False, observed=False):\n    groupby_obj = obj.groupby(by=by, axis=axis, level=level, as_index=as_index, sort=sort,\n                              group_keys=group_keys, squeeze=squeeze, observed=observed)\n    return GroupByWrapper(obj, groupby_obj=groupby_obj)\n", "path": "mars/lib/groupby_wrapper.py"}], "after_files": [{"content": "# Copyright 1999-2020 Alibaba Group Holding Ltd.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom collections.abc import Iterable\n\nimport cloudpickle\nfrom pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy\n\n\nclass GroupByWrapper:\n    def __init__(self, obj, groupby_obj=None, keys=None, axis=0, level=None, grouper=None,\n                 exclusions=None, selection=None, as_index=True, sort=True,\n                 group_keys=True, squeeze=False, observed=False, mutated=False,\n                 grouper_cache=None):\n\n        def fill_value(v, key):\n            return v if v is not None or groupby_obj is None else getattr(groupby_obj, key)\n\n        self.obj = obj\n        self.keys = fill_value(keys, 'keys')\n        self.axis = fill_value(axis, 'axis')\n        self.level = fill_value(level, 'level')\n        self.exclusions = fill_value(exclusions, 'exclusions')\n        self.selection = selection\n        self.as_index = fill_value(as_index, 'as_index')\n        self.sort = fill_value(sort, 'sort')\n        self.group_keys = fill_value(group_keys, 'group_keys')\n        self.squeeze = fill_value(squeeze, 'squeeze')\n        self.observed = fill_value(observed, 'observed')\n        self.mutated = fill_value(mutated, 'mutated')\n\n        if groupby_obj is None:\n            if obj.ndim == 2:\n                self.groupby_obj = DataFrameGroupBy(\n                    obj, keys=keys, axis=axis, level=level, grouper=grouper, exclusions=exclusions,\n                    as_index=as_index, group_keys=group_keys, squeeze=squeeze, observed=observed,\n                    mutated=mutated)\n            else:\n                self.groupby_obj = SeriesGroupBy(\n                    obj, keys=keys, axis=axis, level=level, grouper=grouper, exclusions=exclusions,\n                    as_index=as_index, group_keys=group_keys, squeeze=squeeze, observed=observed,\n                    mutated=mutated)\n        else:\n            self.groupby_obj = groupby_obj\n\n        if grouper_cache:\n            self.groupby_obj.grouper._cache = grouper_cache\n        if selection:\n            self.groupby_obj = self.groupby_obj[selection]\n\n        self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)\n\n    def __getitem__(self, item):\n        return GroupByWrapper(\n            self.obj, keys=self.keys, axis=self.axis, level=self.level,\n            grouper=self.groupby_obj.grouper, exclusions=self.exclusions, selection=item,\n            as_index=self.as_index, sort=self.sort, group_keys=self.group_keys,\n            squeeze=self.squeeze, observed=self.observed, mutated=self.mutated)\n\n    def __getattr__(self, item):\n        if item.startswith('_'):  # pragma: no cover\n            return object.__getattribute__(self, item)\n        if item in getattr(self.obj, 'columns', ()):\n            return self.__getitem__(item)\n        return getattr(self.groupby_obj, item)\n\n    def __iter__(self):\n        return self.groupby_obj.__iter__()\n\n    @property\n    def empty(self):\n        return self.obj.empty\n\n    @property\n    def shape(self):\n        shape = list(self.groupby_obj.obj.shape)\n        if self.is_frame and self.selection:\n            shape[1] = len(self.selection)\n        return tuple(shape)\n\n    def to_tuple(self, truncate=False, pickle_function=False):\n        if self.selection and truncate:\n            if isinstance(self.selection, Iterable) and not isinstance(self.selection, str):\n                item_list = list(self.selection)\n            else:\n                item_list = [self.selection]\n            item_set = set(item_list)\n\n            if isinstance(self.keys, list):\n                sel_keys = self.keys\n            elif self.keys in self.obj.columns:\n                sel_keys = [self.keys]\n            else:\n                sel_keys = []\n\n            all_items = item_list + [k for k in sel_keys or () if k not in item_set]\n            if set(all_items) == set(self.obj.columns):\n                obj = self.obj\n            else:\n                obj = self.obj[all_items]\n        else:\n            obj = self.obj\n\n        if pickle_function and callable(self.keys):\n            keys = cloudpickle.dumps(self.keys)\n        else:\n            keys = self.keys\n\n        return obj, keys, self.axis, self.level, self.exclusions, self.selection, \\\n            self.as_index, self.sort, self.group_keys, self.squeeze, self.observed, \\\n            self.mutated, getattr(self.groupby_obj.grouper, '_cache', dict())\n\n    @classmethod\n    def from_tuple(cls, tp):\n        obj, keys, axis, level, exclusions, selection, as_index, sort, group_keys, squeeze, \\\n            observed, mutated, grouper_cache = tp\n\n        if isinstance(keys, (bytes, bytearray)):\n            keys = cloudpickle.loads(keys)\n\n        return cls(obj, keys=keys, axis=axis, level=level, exclusions=exclusions, selection=selection,\n                   as_index=as_index, sort=sort, group_keys=group_keys, squeeze=squeeze, observed=observed,\n                   mutated=mutated, grouper_cache=grouper_cache)\n\n\ndef wrapped_groupby(obj, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True,\n                    squeeze=False, observed=False):\n    groupby_obj = obj.groupby(by=by, axis=axis, level=level, as_index=as_index, sort=sort,\n                              group_keys=group_keys, squeeze=squeeze, observed=observed)\n    return GroupByWrapper(obj, groupby_obj=groupby_obj)\n", "path": "mars/lib/groupby_wrapper.py"}]}