# Buggy code
def _infer_df_func_returns(self, in_groupby, in_df, dtypes, index):
    index_value, output_type, new_dtypes = None, None, None

    try:
        if in_df.op.output_types[0] == OutputType.dataframe:
            empty_df = build_empty_df(in_df.dtypes, index=pd.RangeIndex(2))
        else:
            empty_df = build_empty_series(
                in_df.dtype, index=pd.RangeIndex(2), name=in_df.name
            )

        selection = getattr(in_groupby.op, "selection", None)
        if selection:
            empty_df = empty_df[selection]

        with np.errstate(all="ignore"):
            infer_df = self.func(empty_df, *self.args, **self.kwds)

        # todo return proper index when sort=True is implemented
        index_value = parse_index(None, in_df.key, self.func)

        if isinstance(infer_df, pd.DataFrame):
            output_type = output_type or OutputType.dataframe
            new_dtypes = new_dtypes or infer_df.dtypes
        elif isinstance(infer_df, pd.Series):
            output_type = output_type or OutputType.series
            new_dtypes = new_dtypes or (infer_df.name, infer_df.dtype)
        else:
            output_type = OutputType.series
            new_dtypes = (None, pd.Series(infer_df).dtype)
    except:  # noqa: E722  # nosec
        pass

    self.output_types = [output_type] if not self.output_types else self.output_types
    dtypes = new_dtypes if dtypes is None else dtypes
    index_value = index_value if index is None else parse_index(index)
    return dtypes, index_value


# Traceback (TypeError)
In [1]: import mars.dataframe as md

In [2]: df = md.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})

In [3]: df['b'] = df['b'].astype(md.ArrowStringDtype())

In [6]: df.groupby('b').count()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in _try_cast(arr, dtype, copy, raise_cast_failure)
545         elif not is_extension_array_dtype(subarr):
--> 546             subarr = construct_1d_ndarray_preserving_na(subarr, dtype, copy=copy)
547     except OutOfBoundsDatetime:

~/miniconda3/lib/python3.7/site-packages/pandas/core/dtypes/cast.py in construct_1d_ndarray_preserving_na(values, dtype, copy)
1506     """
-> 1507     subarr = np.array(values, dtype=dtype, copy=copy)
1508

TypeError: Cannot interpret '<mars.dataframe.arrays.ArrowStringDtype object at 0x7f7f81874150>' as a data type

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
<ipython-input-6-884154032b17> in <module>
----> 1 df.groupby('b').count()

~/Workspace/mars/mars/dataframe/groupby/__init__.py in <lambda>(groupby, **kw)
40         setattr(cls, 'max', lambda groupby, **kw: agg(groupby, 'max', **kw))
41         setattr(cls, 'min', lambda groupby, **kw: agg(groupby, 'min', **kw))
---> 42         setattr(cls, 'count', lambda groupby, **kw: agg(groupby, 'count', **kw))
43         setattr(cls, 'size', lambda groupby, **kw: agg(groupby, 'size', **kw))
44         setattr(cls, 'mean', lambda groupby, **kw: agg(groupby, 'mean', **kw))

~/Workspace/mars/mars/dataframe/groupby/aggregation.py in agg(groupby, func, method, *args, **kwargs)
650     agg_op = DataFrameGroupByAgg(func=func, method=method, raw_func=func,
651                                  groupby_params=groupby.op.groupby_params)
--> 652     return agg_op(groupby)

~/Workspace/mars/mars/dataframe/groupby/aggregation.py in __call__(self, groupby)
201
202         if self.output_types[0] == OutputType.dataframe:
--> 203             return self._call_dataframe(groupby, df)
204         else:
205             return self._call_series(groupby, df)

~/Workspace/mars/mars/dataframe/groupby/aggregation.py in _call_dataframe(self, groupby, input_df)
144
145     def _call_dataframe(self, groupby, input_df):
--> 146         grouped = groupby.op.build_mock_groupby()
147         agg_df = grouped.aggregate(self.func)
148

~/Workspace/mars/mars/dataframe/groupby/core.py in build_mock_groupby(self, **kwargs)
98         in_df = self.inputs[0]
99         if self.is_dataframe_obj:
--> 100             empty_df = build_empty_df(in_df.dtypes, index=pd.RangeIndex(2))
101             obj_dtypes = in_df.dtypes[in_df.dtypes == np.dtype('O')]
102             empty_df[obj_dtypes.index] = 'O'

~/Workspace/mars/mars/dataframe/utils.py in build_empty_df(dtypes, index)
446     df = pd.DataFrame(columns=pd.RangeIndex(len(columns)), index=index)
447     for i, d in enumerate(dtypes):
--> 448         df[i] = pd.Series(dtype=d, index=index)
449     df.columns = columns
450     return df

~/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in __init__(self, data, index, dtype, name, copy, fastpath)
254                 data = data._data
255             elif is_dict_like(data):
--> 256                 data, index = self._init_dict(data, index, dtype)
257                 dtype = None
258                 copy = False

~/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in _init_dict(self, data, index, dtype)
347         # TODO: passing np.float64 to not break anything yet. See GH-17261
348         s = create_series_with_explicit_dtype(
--> 349             values, index=keys, dtype=dtype, dtype_if_empty=np.float64
350         )
351

~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in create_series_with_explicit_dtype(data, index, dtype, name, copy, fastpath, dtype_if_empty)
623         dtype = dtype_if_empty
624     return Series(
--> 625         data=data, index=index, dtype=dtype, name=name, copy=copy, fastpath=fastpath
626     )

~/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in __init__(self, data, index, dtype, name, copy, fastpath)
303                     data = data.copy()
304             else:
--> 305                 data = sanitize_array(data, index, dtype, copy, raise_cast_failure=True)
306
307                 data = SingleBlockManager(data, index, fastpath=True)

~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in sanitize_array(data, index, dtype, copy, raise_cast_failure)
447         subarr = _try_cast(arr, dtype, copy, raise_cast_failure)
448     else:
--> 449         subarr = _try_cast(data, dtype, copy, raise_cast_failure)
450
451     # scalar like, GH

~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in _try_cast(arr, dtype, copy, raise_cast_failure)
560             dtype = cast(ExtensionDtype, dtype)
561             array_type = dtype.construct_array_type()._from_sequence
--> 562             subarr = array_type(arr, dtype=dtype, copy=copy)
563         elif dtype is not None and raise_cast_failure:
564             raise

~/Workspace/mars/mars/dataframe/arrays.py in _from_sequence(cls, scalars, dtype, copy)
253     def _from_sequence(cls, scalars, dtype=None, copy=False):
254         if not hasattr(scalars, 'dtype'):
--> 255             ret = np.empty(len(scalars), dtype=object)
256             for i, s in enumerate(scalars):
257                 ret[i] = s

TypeError: object of type 'pyarrow.lib.NullScalar' has no len()

# Fixed code
def _infer_df_func_returns(self, in_groupby, in_df, dtypes, index):
    index_value, output_type, new_dtypes = None, None, None

    try:
        if in_df.op.output_types[0] == OutputType.dataframe:
            test_df = build_df(in_df, size=2)
        else:
            test_df = build_series(in_df, size=2, name=in_df.name)

        selection = getattr(in_groupby.op, "selection", None)
        if selection:
            test_df = test_df[selection]

        with np.errstate(all="ignore"):
            infer_df = self.func(test_df, *self.args, **self.kwds)

        # todo return proper index when sort=True is implemented
        index_value = parse_index(None, in_df.key, self.func)

        if isinstance(infer_df, pd.DataFrame):
            output_type = output_type or OutputType.dataframe
            new_dtypes = new_dtypes or infer_df.dtypes
        elif isinstance(infer_df, pd.Series):
            output_type = output_type or OutputType.series
            new_dtypes = new_dtypes or (infer_df.name, infer_df.dtype)
        else:
            output_type = OutputType.series
            new_dtypes = (None, pd.Series(infer_df).dtype)
    except:  # noqa: E722  # nosec
        pass

    self.output_types = [output_type] if not self.output_types else self.output_types
    dtypes = new_dtypes if dtypes is None else dtypes
    index_value = index_value if index is None else parse_index(index)
    return dtypes, index_value