# Buggy code
def tile_with_columns(cls, op):
    in_df = op.inputs[0]
    out_df = op.outputs[0]
    col_names = op.col_names
    if not isinstance(col_names, list):
        column_index = calc_columns_index(col_names, in_df)
        out_chunks = []
        dtype = in_df.dtypes[col_names]
        for i in range(in_df.chunk_shape[0]):
            c = in_df.cix[(i, column_index)]
            op = DataFrameIndex(col_names=col_names)
            out_chunks.append(
                op.new_chunk(
                    [c],
                    shape=(c.shape[0],),
                    index=(i,),
                    dtype=dtype,
                    index_value=c.index_value,
                    name=col_names,
                )
            )
        new_op = op.copy()
        return new_op.new_seriess(
            op.inputs,
            shape=out_df.shape,
            dtype=out_df.dtype,
            index_value=out_df.index_value,
            name=out_df.name,
            nsplits=(in_df.nsplits[0],),
            chunks=out_chunks,
        )
    else:
        # combine columns into one chunk and keep the columns order at the same time.
        # When chunk columns are ['c1', 'c2', 'c3'], ['c4', 'c5'],
        # selected columns are ['c2', 'c3', 'c4', 'c2'], `column_splits` will be
        # [(['c2', 'c3'], 0), ('c4', 1), ('c2', 0)].
        selected_index = [calc_columns_index(col, in_df) for col in col_names]
        condition = np.where(np.diff(selected_index))[0] + 1
        column_splits = np.split(col_names, condition)
        column_indexes = np.split(selected_index, condition)

        out_chunks = [[] for _ in range(in_df.chunk_shape[0])]
        column_nsplits = []
        for i, (columns, column_idx) in enumerate(zip(column_splits, column_indexes)):
            dtypes = in_df.dtypes[columns]
            column_nsplits.append(len(columns))
            for j in range(in_df.chunk_shape[0]):
                c = in_df.cix[(j, column_idx[0])]
                index_op = DataFrameIndex(
                    col_names=list(columns), output_types=[OutputType.dataframe]
                )
                out_chunk = index_op.new_chunk(
                    [c],
                    shape=(c.shape[0], len(columns)),
                    index=(j, i),
                    dtypes=dtypes,
                    index_value=c.index_value,
                    columns_value=parse_index(pd.Index(columns), store_data=True),
                )
                out_chunks[j].append(out_chunk)
        out_chunks = [item for cl in out_chunks for item in cl]
        new_op = op.copy()
        nsplits = (in_df.nsplits[0], tuple(column_nsplits))
        return new_op.new_dataframes(
            op.inputs,
            shape=out_df.shape,
            dtypes=out_df.dtypes,
            index_value=out_df.index_value,
            columns_value=out_df.columns_value,
            chunks=out_chunks,
            nsplits=nsplits,
        )


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

In [2]: import pandas as pd

In [4]: md.Series(pd.Series([], dtype=object)).execute()
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-4-665db81dbad7> in <module>
----> 1 md.Series(pd.Series([], dtype=object)).execute()

~/Workspace/mars/mars/core.py in execute(self, session, **kw)
641
642         if wait:
--> 643             return run()
644         else:
645             thread_executor = ThreadPoolExecutor(1)

~/Workspace/mars/mars/core.py in run()
637
638         def run():
--> 639             self.data.execute(session, **kw)
640             return self
641

~/Workspace/mars/mars/core.py in execute(self, session, **kw)
377
378         if wait:
--> 379             return run()
380         else:
381             # leverage ThreadPoolExecutor to submit task,

~/Workspace/mars/mars/core.py in run()
372         def run():
373             # no more fetch, thus just fire run
--> 374             session.run(self, **kw)
375             # return Tileable or ExecutableTuple itself
376             return self

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
503         tileables = tuple(mt.tensor(t) if not isinstance(t, (Entity, Base)) else t
504                           for t in tileables)
--> 505         result = self._sess.run(*tileables, **kw)
506
507         for t in tileables:

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
109             # set number of running cores
110             self.context.set_ncores(kw['n_parallel'])
--> 111             res = self._executor.execute_tileables(tileables, **kw)
112             return res
113

~/Workspace/mars/mars/utils.py in _inner(*args, **kwargs)
449         def _inner(*args, **kwargs):
450             with self:
--> 451                 return func(*args, **kwargs)
452
453         return _inner

~/Workspace/mars/mars/executor.py in execute_tileables(self, tileables, fetch, n_parallel, n_thread, print_progress, mock, compose, name)
859                 # build chunk graph, tile will be done during building
860                 chunk_graph = chunk_graph_builder.build(
--> 861                     tileables, tileable_graph=tileable_graph)
862                 tileable_graph = chunk_graph_builder.prev_tileable_graph
863                 temp_result_keys = set(result_keys)

~/Workspace/mars/mars/utils.py in _inner(*args, **kwargs)
449         def _inner(*args, **kwargs):
450             with self:
--> 451                 return func(*args, **kwargs)
452
453         return _inner

~/Workspace/mars/mars/tiles.py in build(self, tileables, tileable_graph)
346
347         chunk_graph = super().build(
--> 348             tileables, tileable_graph=tileable_graph)
349         self._iterative_chunk_graphs.append(chunk_graph)
350         if len(self._interrupted_ops) == 0:

~/Workspace/mars/mars/utils.py in _inner(*args, **kwargs)
449         def _inner(*args, **kwargs):
450             with self:
--> 451                 return func(*args, **kwargs)
452
453         return _inner

~/Workspace/mars/mars/tiles.py in build(self, tileables, tileable_graph)
260                     # for further execution
261                     partial_tiled_chunks = \
--> 262                         self._on_tile_failure(tileable_data.op, exc_info)
263                     if partial_tiled_chunks is not None and \
264                             len(partial_tiled_chunks) > 0:

~/Workspace/mars/mars/tiles.py in inner(op, exc_info)
299                     on_tile_failure(op, exc_info)
300                 else:
--> 301                     raise exc_info[1].with_traceback(exc_info[2]) from None
302         return inner
303

~/Workspace/mars/mars/tiles.py in build(self, tileables, tileable_graph)
240                 continue
241             try:
--> 242                 tiled = self._tile(tileable_data, tileable_graph)
243                 tiled_op.add(tileable_data.op)
244                 for t, td in zip(tileable_data.op.outputs, tiled):

~/Workspace/mars/mars/tiles.py in _tile(self, tileable_data, tileable_graph)
335         if any(inp.op in self._interrupted_ops for inp in tileable_data.inputs):
336             raise TilesError('Tile fail due to failure of inputs')
--> 337         return super()._tile(tileable_data, tileable_graph)
338
339     @enter_mode(build=True, kernel=True)

~/Workspace/mars/mars/tiles.py in _tile(self, tileable_data, tileable_graph)
199                 t._nsplits = o.nsplits
200         elif on_tile is None:
--> 201             tds[0]._inplace_tile()
202         else:
203             tds = on_tile(tileable_data.op.outputs, tds)

~/Workspace/mars/mars/core.py in _inplace_tile(self)
166
167     def _inplace_tile(self):
--> 168         return handler.inplace_tile(self)
169
170     def __getattr__(self, attr):

~/Workspace/mars/mars/tiles.py in inplace_tile(self, to_tile)
134         if not to_tile.is_coarse():
135             return to_tile
--> 136         dispatched = self.dispatch(to_tile.op)
137         self._assign_to([d.data for d in dispatched], to_tile.op.outputs)
138         return to_tile

~/Workspace/mars/mars/utils.py in _inner(*args, **kwargs)
449         def _inner(*args, **kwargs):
450             with self:
--> 451                 return func(*args, **kwargs)
452
453         return _inner

~/Workspace/mars/mars/tiles.py in dispatch(self, op)
117         else:
118             try:
--> 119                 tiled = op_cls.tile(op)
120             except NotImplementedError as ex:
121                 cause = ex

~/Workspace/mars/mars/dataframe/datasource/series.py in tile(cls, op)
60         memory_usage = raw_series.memory_usage(index=False, deep=True)
61         chunk_size = series.extra_params.raw_chunk_size or options.chunk_size
---> 62         chunk_size = decide_series_chunk_size(series.shape, chunk_size, memory_usage)
63         chunk_size_idxes = (range(len(size)) for size in chunk_size)
64

~/Workspace/mars/mars/dataframe/utils.py in decide_series_chunk_size(shape, chunk_size, memory_usage)
193
194     max_chunk_size = options.chunk_store_limit
--> 195     series_chunk_size = max_chunk_size / average_memory_usage
196     return normalize_chunk_sizes(shape, int(series_chunk_size))
197

ZeroDivisionError: division by zero

# Fixed code
def tile_with_columns(cls, op):
    in_df = op.inputs[0]
    out_df = op.outputs[0]
    col_names = op.col_names
    if not isinstance(col_names, list):
        column_index = calc_columns_index(col_names, in_df)
        out_chunks = []
        dtype = in_df.dtypes[col_names]
        for i in range(in_df.chunk_shape[0]):
            c = in_df.cix[(i, column_index)]
            chunk_op = DataFrameIndex(col_names=col_names)
            out_chunks.append(
                chunk_op.new_chunk(
                    [c],
                    shape=(c.shape[0],),
                    index=(i,),
                    dtype=dtype,
                    index_value=c.index_value,
                    name=col_names,
                )
            )
        new_op = op.copy()
        return new_op.new_seriess(
            op.inputs,
            shape=out_df.shape,
            dtype=out_df.dtype,
            index_value=out_df.index_value,
            name=out_df.name,
            nsplits=(in_df.nsplits[0],),
            chunks=out_chunks,
        )
    else:
        # combine columns into one chunk and keep the columns order at the same time.
        # When chunk columns are ['c1', 'c2', 'c3'], ['c4', 'c5'],
        # selected columns are ['c2', 'c3', 'c4', 'c2'], `column_splits` will be
        # [(['c2', 'c3'], 0), ('c4', 1), ('c2', 0)].
        selected_index = [calc_columns_index(col, in_df) for col in col_names]
        condition = np.where(np.diff(selected_index))[0] + 1
        column_splits = np.split(col_names, condition)
        column_indexes = np.split(selected_index, condition)

        out_chunks = [[] for _ in range(in_df.chunk_shape[0])]
        column_nsplits = []
        for i, (columns, column_idx) in enumerate(zip(column_splits, column_indexes)):
            dtypes = in_df.dtypes[columns]
            column_nsplits.append(len(columns))
            for j in range(in_df.chunk_shape[0]):
                c = in_df.cix[(j, column_idx[0])]
                index_op = DataFrameIndex(
                    col_names=list(columns), output_types=[OutputType.dataframe]
                )
                out_chunk = index_op.new_chunk(
                    [c],
                    shape=(c.shape[0], len(columns)),
                    index=(j, i),
                    dtypes=dtypes,
                    index_value=c.index_value,
                    columns_value=parse_index(pd.Index(columns), store_data=True),
                )
                out_chunks[j].append(out_chunk)
        out_chunks = [item for cl in out_chunks for item in cl]
        new_op = op.copy()
        nsplits = (in_df.nsplits[0], tuple(column_nsplits))
        return new_op.new_dataframes(
            op.inputs,
            shape=out_df.shape,
            dtypes=out_df.dtypes,
            index_value=out_df.index_value,
            columns_value=out_df.columns_value,
            chunks=out_chunks,
            nsplits=nsplits,
        )