code: def series_apply(
      series,
      func,
      convert_dtype=True,
      output_type=None,
      args=(),
      dtypes=None,
      dtype=None,
      name=None,
      index=None,
      skip_infer=False,
      **kwds,
  ):
      """
      Invoke function on values of Series.
  
      Can be ufunc (a NumPy function that applies to the entire Series)
      or a Python function that only works on single values.
  
      Parameters
      ----------
      func : function
          Python function or NumPy ufunc to apply.
  
      convert_dtype : bool, default True
          Try to find better dtype for elementwise function results. If
          False, leave as dtype=object.
  
      output_type : {'dataframe', 'series'}, default None
          Specify type of returned object. See `Notes` for more details.
  
      dtypes : Series, default None
          Specify dtypes of returned DataFrames. See `Notes` for more details.
  
      dtype : numpy.dtype, default None
          Specify dtype of returned Series. See `Notes` for more details.
  
      name : str, default None
          Specify name of returned Series. See `Notes` for more details.
  
      index : Index, default None
          Specify index of returned object. See `Notes` for more details.
  
      args : tuple
          Positional arguments passed to func after the series value.
  
      skip_infer: bool, default False
          Whether infer dtypes when dtypes or output_type is not specified.
  
      **kwds
          Additional keyword arguments passed to func.
  
      Returns
      -------
      Series or DataFrame
          If func returns a Series object the result will be a DataFrame.
  
      See Also
      --------
      Series.map: For element-wise operations.
      Series.agg: Only perform aggregating type operations.
      Series.transform: Only perform transforming type operations.
  
      Notes
      -----
      When deciding output dtypes and shape of the return value, Mars will
      try applying ``func`` onto a mock Series, and the apply call may fail.
      When this happens, you need to specify the type of apply call
      (DataFrame or Series) in output_type.
  
      * For DataFrame output, you need to specify a list or a pandas Series
        as ``dtypes`` of output DataFrame. ``index`` of output can also be
        specified.
      * For Series output, you need to specify ``dtype`` and ``name`` of
        output Series.
  
      Examples
      --------
      Create a series with typical summer temperatures for each city.
  
      >>> import mars.tensor as mt
      >>> import mars.dataframe as md
      >>> s = md.Series([20, 21, 12],
      ...               index=['London', 'New York', 'Helsinki'])
      >>> s.execute()
      London      20
      New York    21
      Helsinki    12
      dtype: int64
  
      Square the values by defining a function and passing it as an
      argument to ``apply()``.
  
      >>> def square(x):
      ...     return x ** 2
      >>> s.apply(square).execute()
      London      400
      New York    441
      Helsinki    144
      dtype: int64
  
      Square the values by passing an anonymous function as an
      argument to ``apply()``.
  
      >>> s.apply(lambda x: x ** 2).execute()
      London      400
      New York    441
      Helsinki    144
      dtype: int64
  
      Define a custom function that needs additional positional
      arguments and pass these additional arguments using the
      ``args`` keyword.
  
      >>> def subtract_custom_value(x, custom_value):
      ...     return x - custom_value
  
      >>> s.apply(subtract_custom_value, args=(5,)).execute()
      London      15
      New York    16
      Helsinki     7
      dtype: int64
  
      Define a custom function that takes keyword arguments
      and pass these arguments to ``apply``.
  
      >>> def add_custom_values(x, **kwargs):
      ...     for month in kwargs:
      ...         x += kwargs[month]
      ...     return x
  
      >>> s.apply(add_custom_values, june=30, july=20, august=25).execute()
      London      95
      New York    96
      Helsinki    87
      dtype: int64
      """
      if isinstance(func, (list, dict)):
          return series.aggregate(func)
  
      # calling member function
      if isinstance(func, str):
          func_body = getattr(series, func, None)
          if func_body is not None:
              return func_body(*args, **kwds)
          func_str = func
          func = getattr(np, func_str, None)
          if func is None:
              raise AttributeError(
                  f"'{func_str!r}' is not a valid function "
                  f"for '{type(series).__name__}' object"
              )
  
      if skip_infer and output_type is None:
          output_type = OutputType.df_or_series
  
      output_types = kwds.pop("output_types", None)
      object_type = kwds.pop("object_type", None)
      output_types = validate_output_types(
          output_type=output_type,
          output_types=output_types,
          object_type=object_type,
      )
      output_type = (
          output_types[0] if output_types else OutputType.series
      )
  
      op = ApplyOperand(
          func=func,
          convert_dtype=convert_dtype,
          args=args,
          kwds=kwds,
          output_type=output_type,
      )
      return op(
          series, dtypes=dtypes, dtype=dtype, name=name, index=index
      )
  
imports: import inspect
  import numpy
  import pandas
  from ...None import opcodes
  from ...config import options
  from ...core import OutputType, recursive_tile
  from ...core.custom_log import redirect_custom_log
  from ...core.operand import OperatorLogicKeyGeneratorMixin
  from ...serialization.serializables import StringField, AnyField, BoolField, TupleField, DictField, FunctionField
  from ...utils import enter_current_session, quiet_stdio, get_func_token, tokenize
  from ..arrays import ArrowArray
  from ..operands import DataFrameOperandMixin, DataFrameOperand
  from ..utils import build_df, build_series, parse_index, validate_axis, validate_output_types, make_dtypes, make_dtype, build_empty_df, build_empty_series, clean_up_func, restore_func
label_desc: Use image node_id 2 for calling a global function with example usage: series_apply(series, func, convert_dtype, output_type, args, dtypes, dtype, name, index, skip_infer) and returns: op, series, func_body