# Buggy code
def _calc_results(self, session_id, graph_key, graph, context_dict, chunk_targets):
    _, op_name = concat_operand_keys(graph, "_")

    logger.debug("Start calculating operand %s in %s.", graph_key, self.uid)
    start_time = time.time()

    local_context_dict = DistributedDictContext(
        self.get_scheduler(self.default_uid()),
        session_id,
        actor_ctx=self.ctx,
        address=self.address,
        n_cpu=self._get_n_cpu(),
    )
    local_context_dict["_actor_cls"] = type(self)
    local_context_dict["_actor_uid"] = self.uid
    local_context_dict["_op_key"] = graph_key
    local_context_dict.update(context_dict)
    context_dict.clear()

    # start actual execution
    executor = Executor(storage=local_context_dict)
    with EventContext(
        self._events_ref,
        EventCategory.PROCEDURE,
        EventLevel.NORMAL,
        self._calc_event_type,
        self.uid,
    ):
        self._execution_pool.submit(
            executor.execute_graph, graph, chunk_targets, retval=False
        ).result()

    end_time = time.time()

    # collect results
    result_keys = []
    result_values = []
    result_sizes = []
    collected_chunk_keys = set()
    for k, v in local_context_dict.items():
        if isinstance(k, tuple):
            k = tuple(to_str(i) for i in k)
        else:
            k = to_str(k)

        chunk_key = get_chunk_key(k)
        if chunk_key in chunk_targets:
            result_keys.append(k)
            result_values.append(v)
            result_sizes.append(calc_data_size(v))
            collected_chunk_keys.add(chunk_key)

    local_context_dict.clear()

    # check if all targets generated
    if any(k not in collected_chunk_keys for k in chunk_targets):
        raise KeyError([k for k in chunk_targets if k not in collected_chunk_keys])

    # adjust sizes in allocation
    apply_allocs = defaultdict(lambda: 0)
    for k, size in zip(result_keys, result_sizes):
        apply_allocs[get_chunk_key(k)] += size

    apply_alloc_quota_keys, apply_alloc_sizes = [], []
    for k, v in apply_allocs.items():
        apply_alloc_quota_keys.append(
            build_quota_key(session_id, k, owner=self.proc_id)
        )
        apply_alloc_sizes.append(v)
    self._mem_quota_ref.alter_allocations(
        apply_alloc_quota_keys, apply_alloc_sizes, _tell=True, _wait=False
    )
    self._mem_quota_ref.hold_quotas(apply_alloc_quota_keys, _tell=True)

    if self._status_ref:
        self._status_ref.update_mean_stats(
            "calc_speed." + op_name,
            sum(apply_alloc_sizes) * 1.0 / (end_time - start_time),
            _tell=True,
            _wait=False,
        )

    logger.debug("Finish calculating operand %s.", graph_key)

    return self.storage_client.put_objects(
        session_id,
        result_keys,
        result_values,
        [self._calc_intermediate_device],
        sizes=result_sizes,
    ).then(lambda *_: result_keys)


# Traceback (AttributeError)
In [25]: merge_df = parsing_df.append(pFold_4_df)

In [26]: mDf_g = merge_df.groupby(["query","template"])

In [27]: mDf_g.execute()
Out[27]: ---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/anaconda3/envs/py37/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
700                 type_pprinters=self.type_printers,
701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
703             printer.flush()
704             return stream.getvalue()

~/anaconda3/envs/py37/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
392                         if cls is not object \
393                                 and callable(cls.__dict__.get('__repr__')):
--> 394                             return _repr_pprint(obj, self, cycle)
395
396             return _default_pprint(obj, self, cycle)

~/anaconda3/envs/py37/lib/python3.7/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
698     """A pprint that just redirects to the normal repr function."""
699     # Find newlines and replace them with p.break_()
--> 700     output = repr(obj)
701     lines = output.splitlines()
702     with p.group():

~/anaconda3/envs/py37/lib/python3.7/site-packages/mars/dataframe/core.py in __repr__(self)
998
999     def __repr__(self):
-> 1000         return self._to_str(representation=True)
1001
1002     def _repr_html_(self):

~/anaconda3/envs/py37/lib/python3.7/site-packages/mars/dataframe/core.py in _to_str(self, representation)
968         else:
969             corner_data = fetch_corner_data(
--> 970                 self, session=self._executed_sessions[-1])
971
972             buf = StringIO()

~/anaconda3/envs/py37/lib/python3.7/site-packages/mars/dataframe/utils.py in fetch_corner_data(df_or_series, session)
768         return df_or_series.fetch(session=session)
769     else:
--> 770         head = iloc(df_or_series)[:index_size]
771         tail = iloc(df_or_series)[-index_size:]
772         head_data, tail_data = \

~/anaconda3/envs/py37/lib/python3.7/site-packages/mars/dataframe/indexing/iloc.py in __getitem__(self, indexes)
111         else:
112             op = SeriesIlocGetItem(indexes=process_iloc_indexes(self._obj, indexes))
--> 113         return op(self._obj)
114
115     def __setitem__(self, indexes, value):

~/anaconda3/envs/py37/lib/python3.7/site-packages/mars/dataframe/indexing/iloc.py in __call__(self, series)
485             index_value = indexing_index_value(series.index_value, self.indexes[0])
486             inputs = [series] + [index for index in self._indexes if isinstance(index, (Base, Entity))]
--> 487             return self.new_series(inputs, shape=shape, dtype=series.dtype,
488                                    index_value=index_value, name=series.name)
489

AttributeError: 'DataFrameGroupByData' object has no attribute 'dtype'

# Fixed code
def _calc_results(self, session_id, graph_key, graph, context_dict, chunk_targets):
    _, op_name = concat_operand_keys(graph, "_")

    logger.debug("Start calculating operand %s in %s.", graph_key, self.uid)
    start_time = time.time()

    for chunk in graph:
        for inp, prepare_inp in zip(chunk.inputs, chunk.op.prepare_inputs):
            if not prepare_inp:
                context_dict[inp.key] = None

    local_context_dict = DistributedDictContext(
        self.get_scheduler(self.default_uid()),
        session_id,
        actor_ctx=self.ctx,
        address=self.address,
        n_cpu=self._get_n_cpu(),
    )
    local_context_dict["_actor_cls"] = type(self)
    local_context_dict["_actor_uid"] = self.uid
    local_context_dict["_op_key"] = graph_key
    local_context_dict.update(context_dict)
    context_dict.clear()

    # start actual execution
    executor = Executor(storage=local_context_dict)
    with EventContext(
        self._events_ref,
        EventCategory.PROCEDURE,
        EventLevel.NORMAL,
        self._calc_event_type,
        self.uid,
    ):
        self._execution_pool.submit(
            executor.execute_graph, graph, chunk_targets, retval=False
        ).result()

    end_time = time.time()

    # collect results
    result_keys = []
    result_values = []
    result_sizes = []
    collected_chunk_keys = set()
    for k, v in local_context_dict.items():
        if isinstance(k, tuple):
            k = tuple(to_str(i) for i in k)
        else:
            k = to_str(k)

        chunk_key = get_chunk_key(k)
        if chunk_key in chunk_targets:
            result_keys.append(k)
            result_values.append(v)
            result_sizes.append(calc_data_size(v))
            collected_chunk_keys.add(chunk_key)

    local_context_dict.clear()

    # check if all targets generated
    if any(k not in collected_chunk_keys for k in chunk_targets):
        raise KeyError([k for k in chunk_targets if k not in collected_chunk_keys])

    # adjust sizes in allocation
    apply_allocs = defaultdict(lambda: 0)
    for k, size in zip(result_keys, result_sizes):
        apply_allocs[get_chunk_key(k)] += size

    apply_alloc_quota_keys, apply_alloc_sizes = [], []
    for k, v in apply_allocs.items():
        apply_alloc_quota_keys.append(
            build_quota_key(session_id, k, owner=self.proc_id)
        )
        apply_alloc_sizes.append(v)
    self._mem_quota_ref.alter_allocations(
        apply_alloc_quota_keys, apply_alloc_sizes, _tell=True, _wait=False
    )
    self._mem_quota_ref.hold_quotas(apply_alloc_quota_keys, _tell=True)

    if self._status_ref:
        self._status_ref.update_mean_stats(
            "calc_speed." + op_name,
            sum(apply_alloc_sizes) * 1.0 / (end_time - start_time),
            _tell=True,
            _wait=False,
        )

    logger.debug("Finish calculating operand %s.", graph_key)

    return self.storage_client.put_objects(
        session_id,
        result_keys,
        result_values,
        [self._calc_intermediate_device],
        sizes=result_sizes,
    ).then(lambda *_: result_keys)