# Buggy code
def calc_shape(tensor_shape, index):
    shape = []
    in_axis = 0
    out_axis = 0
    fancy_index = None
    fancy_index_shapes = []
    for ind in index:
        if (
            isinstance(ind, TENSOR_TYPE + TENSOR_CHUNK_TYPE + (np.ndarray,))
            and ind.dtype == np.bool_
        ):
            # bool
            shape.append(np.nan if not isinstance(ind, np.ndarray) else ind.sum())
            for i, t_size, size in zip(
                itertools.count(0),
                ind.shape,
                tensor_shape[in_axis : ind.ndim + in_axis],
            ):
                if not np.isnan(t_size) and not np.isnan(size) and t_size != size:
                    raise IndexError(
                        "boolean index did not match indexed array along dimension {0}; "
                        "dimension is {1} but corresponding boolean dimension is {2}".format(
                            in_axis + i, size, t_size
                        )
                    )
            in_axis += ind.ndim
            out_axis += 1
        elif isinstance(ind, TENSOR_TYPE + TENSOR_CHUNK_TYPE + (np.ndarray,)):
            first_fancy_index = False
            if fancy_index is None:
                first_fancy_index = True
                fancy_index = out_axis
            if isinstance(ind, np.ndarray) and np.any(ind >= tensor_shape[in_axis]):
                out_of_range_index = next(
                    i for i in ind.flat if i >= tensor_shape[in_axis]
                )
                raise IndexError(
                    "IndexError: index {0} is out of bounds with size {1}".format(
                        out_of_range_index, tensor_shape[in_axis]
                    )
                )
            fancy_index_shapes.append(ind.shape)
            in_axis += 1
            if first_fancy_index:
                out_axis += ind.ndim
        elif isinstance(ind, slice):
            if np.isnan(tensor_shape[in_axis]):
                shape.append(np.nan)
            else:
                shape.append(calc_sliced_size(tensor_shape[in_axis], ind))
            in_axis += 1
            out_axis += 1
        elif isinstance(ind, Integral):
            size = tensor_shape[in_axis]
            if not np.isnan(size) and ind >= size:
                raise IndexError(
                    "index {0} is out of bounds for axis {1} with size {2}".format(
                        ind, in_axis, size
                    )
                )
            in_axis += 1
        else:
            assert ind is None
            shape.append(1)

    if fancy_index is not None:
        try:
            if any(np.isnan(np.prod(s)) for s in fancy_index_shapes):
                fancy_index_shape = (np.nan,) * len(fancy_index_shapes[0])
            else:
                fancy_index_shape = broadcast_shape(*fancy_index_shapes)
            shape = shape[:fancy_index] + list(fancy_index_shape) + shape[fancy_index:]
        except ValueError:
            raise IndexError(
                "shape mismatch: indexing arrays could not be broadcast together "
                "with shapes {0}".format(" ".join(str(s) for s in fancy_index_shapes))
            )

    return shape


# Traceback (AttributeError)
In [3]: import pandas as pd

In [4]: data = pd.DataFrame(np.arange(20).reshape((4, 5)) + 1, columns=['a', 'b', 'c', 'd', 'e'])

In [6]: df = md.DataFrame(data)

In [7]: df.groupby(['a','b']).size().execute()
Unexpected exception occurred in enter_build_mode.<locals>.inner.
Traceback (most recent call last):
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 353, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 493, in inner
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/scheduler/graph.py", line 633, in prepare_graph
self._target_tileable_datas + fetch_tileables, tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 399, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 493, in inner
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 350, in build
tileables, tileable_graph=tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 399, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 493, in inner
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 263, in build
self._on_tile_failure(tileable_data.op, exc_info)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 302, in inner
raise exc_info[1].with_traceback(exc_info[2]) from None
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 243, in build
tiled = self._tile(tileable_data, tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 338, in _tile
return super()._tile(tileable_data, tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 203, in _tile
tds = on_tile(tileable_data.op.outputs, tds)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/scheduler/graph.py", line 615, in on_tile
return self.context.wraps(handler.dispatch)(first.op)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/context.py", line 69, in h
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 399, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 119, in dispatch
tiled = op_cls.tile(op)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/groupby/aggregation.py", line 481, in tile
return cls._tile_with_tree(op)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/groupby/aggregation.py", line 412, in _tile_with_tree
index = out_df.index_value.to_pandas()
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/core.py", line 283, in to_pandas
return self._index_value.to_pandas()
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/core.py", line 197, in to_pandas
sortorder=self._sortorder, names=self._names)
AttributeError: _sortorder

# Fixed code
def calc_shape(tensor_shape, index):
    shape = []
    in_axis = 0
    out_axis = 0
    fancy_index = None
    fancy_index_shapes = []
    for ind in index:
        if (
            isinstance(ind, TENSOR_TYPE + TENSOR_CHUNK_TYPE + (np.ndarray,))
            and ind.dtype == np.bool_
        ):
            # bool
            shape.append(np.nan if not isinstance(ind, np.ndarray) else int(ind.sum()))
            for i, t_size, size in zip(
                itertools.count(0),
                ind.shape,
                tensor_shape[in_axis : ind.ndim + in_axis],
            ):
                if not np.isnan(t_size) and not np.isnan(size) and t_size != size:
                    raise IndexError(
                        "boolean index did not match indexed array along dimension {0}; "
                        "dimension is {1} but corresponding boolean dimension is {2}".format(
                            in_axis + i, size, t_size
                        )
                    )
            in_axis += ind.ndim
            out_axis += 1
        elif isinstance(ind, TENSOR_TYPE + TENSOR_CHUNK_TYPE + (np.ndarray,)):
            first_fancy_index = False
            if fancy_index is None:
                first_fancy_index = True
                fancy_index = out_axis
            if isinstance(ind, np.ndarray) and np.any(ind >= tensor_shape[in_axis]):
                out_of_range_index = next(
                    i for i in ind.flat if i >= tensor_shape[in_axis]
                )
                raise IndexError(
                    "IndexError: index {0} is out of bounds with size {1}".format(
                        out_of_range_index, tensor_shape[in_axis]
                    )
                )
            fancy_index_shapes.append(ind.shape)
            in_axis += 1
            if first_fancy_index:
                out_axis += ind.ndim
        elif isinstance(ind, slice):
            if np.isnan(tensor_shape[in_axis]):
                shape.append(np.nan)
            else:
                shape.append(calc_sliced_size(tensor_shape[in_axis], ind))
            in_axis += 1
            out_axis += 1
        elif isinstance(ind, Integral):
            size = tensor_shape[in_axis]
            if not np.isnan(size) and ind >= size:
                raise IndexError(
                    "index {0} is out of bounds for axis {1} with size {2}".format(
                        ind, in_axis, size
                    )
                )
            in_axis += 1
        else:
            assert ind is None
            shape.append(1)

    if fancy_index is not None:
        try:
            if any(np.isnan(np.prod(s)) for s in fancy_index_shapes):
                fancy_index_shape = (np.nan,) * len(fancy_index_shapes[0])
            else:
                fancy_index_shape = broadcast_shape(*fancy_index_shapes)
            shape = shape[:fancy_index] + list(fancy_index_shape) + shape[fancy_index:]
        except ValueError:
            raise IndexError(
                "shape mismatch: indexing arrays could not be broadcast together "
                "with shapes {0}".format(" ".join(str(s) for s in fancy_index_shapes))
            )

    return shape