code: def df_groupby_agg_select_function(
      tileable_data: TileableData, required_cols: Set[Any]
  ) -> Dict[TileableData, Set[Any]]:
      op: DataFrameGroupByAgg = tileable_data.op
      assert isinstance(
          op.inputs[0], (BaseDataFrameData, BaseSeriesData)
      )
      inp: BaseDataFrameData = op.inputs[0]
      by = op.groupby_params["by"]
      selection = op.groupby_params.get("selection", None)
      raw_func = op.raw_func
  
      ret = {}
      # group by a series
      groupby_series = False
      if (
          isinstance(by, list)
          and len(by) == 1
          and isinstance(by[0], BaseSeriesData)
      ):
          groupby_series = True
          ret[by[0]] = by[0].name
  
      if isinstance(inp, BaseSeriesData):
          ret[inp] = {inp.name}
      else:
          selected_cols = set()
          # group by keys should be included
          if not groupby_series:
              selected_cols.update(get_cols_exclude_index(inp, by))
          # add agg columns
          if op.raw_func is not None:
              if op.raw_func == "size":
                  # special for size, its return value is always series
                  pass
              elif isinstance(raw_func, dict):
                  selected_cols.update(set(raw_func.keys()))
              else:
                  # no specified agg columns
                  # required_cols should always be a subset of selection
                  for col in required_cols:
                      # col is a tuple when required col is a MultiIndex
                      if isinstance(col, tuple):
                          for c in col:
                              selected_cols.add(c)
                      selected_cols.add(col)
                  if selection is not None:
                      if isinstance(selection, (list, tuple)):
                          selected_cols.update(set(selection))
                      else:
                          selected_cols.add(selection)
          elif op.raw_func_kw:
              # add renamed columns
              for _, origin in op.raw_func_kw.items():
                  if isinstance(origin, NamedAgg):
                      selected_cols.add(origin.column)
                  else:
                      assert isinstance(origin, tuple)
                      selected_cols.add(origin[0])
  
          ret[inp] = selected_cols.intersection(inp.dtypes.index)
      return ret
  
imports: from collections import defaultdict
  from typing import Callable, Dict, Any, Set
  from .....core import TileableData
  from .....dataframe import NamedAgg
  from .....dataframe.arithmetic.core import DataFrameBinOp, DataFrameUnaryOp
  from .....dataframe.core import BaseDataFrameData, BaseSeriesData
  from .....dataframe.groupby.aggregation import DataFrameGroupByAgg
  from .....dataframe.indexing.getitem import DataFrameIndex
  from .....dataframe.indexing.setitem import DataFrameSetitem
  from .....dataframe.merge import DataFrameMerge
  from .....typing import OperandType
  from .utils import get_cols_exclude_index
label_desc: Use image node_id 3 for calling a global function with example usage: df_groupby_agg_select_function(tileable_data, required_cols) and returns: ret