code: def train(
      ctx,
      train_data,
      train_output_data,
      output_model,
      role,
      method,
      n_bins,
      split_pt_dict,
      bin_col,
      bin_idx,
      category_col,
      category_idx,
      use_anonymous,
      transform_method,
      skip_metrics,
      local_only,
      relative_error,
      adjustment_factor,
  ):
      logger.info(f"start binning train")
      sub_ctx = ctx.sub_ctx("train")
      train_data = train_data.read()
      columns = train_data.schema.columns.to_list()
      anonymous_columns = None
      if use_anonymous:
          anonymous_columns = (
              train_data.schema.anonymous_columns.to_list()
          )
          split_pt_dict = {
              columns[anonymous_columns.index(col)]: split_pt_dict[col]
              for col in split_pt_dict.keys()
          }
      to_bin_cols, merged_category_col = get_to_bin_cols(
          columns,
          anonymous_columns,
          bin_col,
          bin_idx,
          category_col,
          category_idx,
      )
      if split_pt_dict:
          to_bin_cols = list(
              set(to_bin_cols).intersection(split_pt_dict.keys())
          )
  
      if role.is_guest:
          binning = HeteroBinningModuleGuest(
              method,
              n_bins,
              split_pt_dict,
              to_bin_cols,
              transform_method,
              merged_category_col,
              local_only,
              relative_error,
              adjustment_factor,
          )
      elif role.is_host:
          binning = HeteroBinningModuleHost(
              method,
              n_bins,
              split_pt_dict,
              to_bin_cols,
              transform_method,
              merged_category_col,
              local_only,
              relative_error,
              adjustment_factor,
          )
      else:
          raise ValueError(f"unknown role: {role}")
      binning.fit(sub_ctx, train_data)
      binned_data = None
      if not skip_metrics:
          binned_data = binning._bin_obj.bucketize_data(train_data)
          binning.compute_metrics(sub_ctx, binned_data)
      model = binning.get_model()
      output_model.write(model)
  
      sub_ctx = ctx.sub_ctx("predict")
      output_data = train_data
      if transform_method is not None:
          if binned_data is None:
              binned_data = binning._bin_obj.bucketize_data(train_data)
          output_data = binning.transform(sub_ctx, binned_data)
      train_output_data.write(output_data)
  
file_path: FATE/python/fate/components/components/hetero_feature_binning.py
imports: import logging
  from typing import List
  from fate.arch import Context
  from fate.components.core import GUEST, HOST, Role, cpn, params
  from fate.ml.feature_binning import HeteroBinningModuleHost, HeteroBinningModuleGuest
label_desc: Use image node_id 4 for calling a global function with example usage: train(ctx, train_data, train_output_data, output_model, role, method, n_bins, split_pt_dict, bin_col, bin_idx, category_col, category_idx, use_anonymous, transform_method, skip_metrics, local_only, relative_error, adjustment_factor) without return types