code: def test_groupby_agg_str_cat(setup):
      agg_fun = lambda x: x.str.cat(sep="_", na_rep="NA")
  
      rs = np.random.RandomState(0)
      raw_df = pd.DataFrame(
          {
              "a": rs.choice(["A", "B", "C"], size=(100,)),
              "b": rs.choice(
                  [None, "alfa", "bravo", "charlie"], size=(100,)
              ),
          }
      )
  
      mdf = md.DataFrame(raw_df, chunk_size=13)
  
      r = mdf.groupby("a").agg(agg_fun, method="tree")
      pd.testing.assert_frame_equal(
          r.execute().fetch(), raw_df.groupby("a").agg(agg_fun)
      )
  
      raw_series = pd.Series(
          rs.choice([None, "alfa", "bravo", "charlie"], size=(100,))
      )
  
      ms = md.Series(raw_series, chunk_size=13)
  
      r = ms.groupby(lambda x: x % 2).agg(agg_fun, method="tree")
      pd.testing.assert_series_equal(
          r.execute().fetch(),
          raw_series.groupby(lambda x: x % 2).agg(agg_fun),
      )
  
imports: from collections import OrderedDict
  import numpy
  import pandas
  import pytest
  from ....None import dataframe
  from ....config import option_context
  from ....core.operand import OperandStage
  from ....tests.core import assert_groupby_equal, require_cudf
  from ....utils import arrow_array_to_objects, pd_release_version
  from ...core import DATAFRAME_OR_SERIES_TYPE
  from ..aggregation import DataFrameGroupByAgg
label_desc: Use image node_id 8 for calling a global function with example usage: test_groupby_agg_str_cat(setup) without return types