code: def tile(cls, op: "GroupByHead"):
      in_df = op.inputs[0]
      groupby_params = op.groupby_params.copy()
      selection = groupby_params.pop("selection", None)
  
      enable_negative = _pandas_enable_negative and op.enable_negative
  
      if len(in_df.shape) > 1:
          in_df = build_concatenated_rows_frame(in_df)
      out_df = op.outputs[0]
  
      # when row_count is not positive and pandas does not support negative head,
      #  or there is only one chunk, tile with a single chunk
      if (not enable_negative and op.row_count <= 0) or len(
          in_df.chunks
      ) <= 1:
          row_num = (
              0 if not enable_negative and op.row_count <= 0 else np.nan
          )
          new_shape = (row_num,)
          new_nsplits = ((row_num,),)
          if out_df.ndim > 1:
              new_shape += (out_df.shape[1],)
              new_nsplits += ((out_df.shape[1],),)
  
          c = in_df.chunks[0]
          chunk_op = op.copy().reset_key()
          params = out_df.params
          params["shape"] = new_shape
          params["index"] = (0,) * out_df.ndim
          out_chunk = chunk_op.new_chunk([c], **params)
  
          tileable_op = op.copy().reset_key()
          return tileable_op.new_tileables(
              [in_df], nsplits=new_nsplits, chunks=[out_chunk], **params
          )
  
      if in_df.ndim > 1 and selection:
          if (
              isinstance(selection, tuple)
              and selection not in in_df.dtypes
          ):
              selection = list(selection)
  
          if not isinstance(selection, list):
              pre_selection = [selection]
          else:
              pre_selection = list(selection)
  
          if isinstance(groupby_params.get("by"), list):
              pre_selection += [
                  el
                  for el in groupby_params["by"]
                  if el not in pre_selection
              ]
  
          if len(pre_selection) != in_df.shape[1]:
              in_df = yield from recursive_tile(in_df[pre_selection])
  
      # generate pre chunks
      if op.row_count < 0:
          # when we have negative row counts, pre-groupby optimization is not possible
          pre_chunks = in_df.chunks
      else:
          pre_chunks = []
          for c in in_df.chunks:
              pre_op = op.copy().reset_key()
              pre_op._output_types = get_output_types(c)
              pre_op.groupby_params = op.groupby_params.copy()
              pre_op.groupby_params.pop("selection", None)
              params = c.params
              params["shape"] = (np.nan,) + c.shape[1:]
              pre_chunks.append(pre_op.new_chunk([c], **params))
  
      new_op = op.copy().reset_key()
      new_op._output_types = get_output_types(in_df)
      new_nsplits = (
          (np.nan,) * len(in_df.nsplits[0]),
      ) + in_df.nsplits[1:]
      pre_tiled = new_op.new_tileable(
          [in_df],
          chunks=pre_chunks,
          nsplits=new_nsplits,
          **in_df.params,
      )
  
      # generate groupby
      grouped = yield from recursive_tile(
          pre_tiled.groupby(**groupby_params)
      )
      if selection:
          grouped = yield from recursive_tile(grouped[selection])
  
      # generate post chunks
      post_chunks = []
      for c in grouped.chunks:
          post_op = op.copy().reset_key()
          post_op.groupby_params = op.groupby_params.copy()
          post_op.groupby_params.pop("selection", None)
          if op.output_types[0] == OutputType.dataframe:
              index = c.index
          else:
              index = (c.index[0],)
          params = out_df.params
          params["index"] = index
          post_chunks.append(post_op.new_chunk([c], **params))
  
      new_op = op.copy().reset_key()
      new_nsplits = ((np.nan,) * len(in_df.nsplits[0]),)
      if out_df.ndim > 1:
          new_nsplits += ((out_df.shape[1],),)
      return new_op.new_tileables(
          [in_df],
          chunks=post_chunks,
          nsplits=new_nsplits,
          **out_df.params,
      )
  
file_path: mars/mars/dataframe/groupby/head.py
imports: import numpy
  import pandas
  from ...None import opcodes
  from ...core import OutputType, get_output_types, recursive_tile
  from ...serialization.serializables import DictField, Int64Field, BoolField
  from ...utils import pd_release_version
  from ..core import IndexValue
  from ..operands import DataFrameOperandMixin, DataFrameOperand
  from ..utils import build_concatenated_rows_frame, parse_index
label_desc: Use image node_id 2 for calling the GroupByHead obj's underlying member method code with example usage: obj.tile(cls, op) and returns: new_op, tileable_op