code: def generate_bins(
      X_features: DataFrame, features_to_bin: list, ideal_bins: int = 10
  ):
      X_len = len(X_features)
      starting_cats = 1000
      bin_index_starting = [
          np.floor(X_len * (num + 1) / starting_cats)
          for num in range(starting_cats - 1)
      ]
      bin_epsilon = 0.000000001
      bin_mapping = dict()
      max_iterations = 20
      for column in features_to_bin:
          num_cats_initial = starting_cats
          bins_value_counts = X_features[column].value_counts(
              ascending=False, normalize=True
          )
          max_bins = len(bins_value_counts)
  
          if max_bins <= ideal_bins:
              bins = pd.Series(data=sorted(X_features[column].unique()))
              num_cats_initial = max_bins
              cur_len = max_bins
              bin_index = list(range(num_cats_initial))
              interval_index = get_bins(
                  bins=bins,
                  bin_index=bin_index,
                  bin_epsilon=bin_epsilon,
              )
          else:
              cur_len = X_len
              bins = X_features[column].sort_values(ascending=True)
              interval_index = get_bins(
                  bins=bins,
                  bin_index=bin_index_starting,
                  bin_epsilon=bin_epsilon,
              )
  
          # TODO: max_desired_bins and min_desired_bins are currently equivalent, but in future they will be parameterized to allow for flexibility.
          max_desired_bins = min(ideal_bins, max_bins)
          min_desired_bins = min(ideal_bins, max_bins)
  
          is_satisfied = (len(interval_index) >= min_desired_bins) and (
              len(interval_index) <= max_desired_bins
          )
  
          num_cats_current = num_cats_initial
          cur_iteration = 0
          while not is_satisfied:
              ratio_reduction = max_desired_bins / len(interval_index)
              num_cats_current = int(
                  np.floor(num_cats_current * ratio_reduction)
              )
              bin_index = [
                  np.floor(cur_len * (num + 1) / num_cats_current)
                  for num in range(num_cats_current - 1)
              ]
              interval_index = get_bins(
                  bins=bins,
                  bin_index=bin_index,
                  bin_epsilon=bin_epsilon,
              )
  
              if (len(interval_index) >= min_desired_bins) and (
                  len(interval_index) <= max_desired_bins
              ):
                  is_satisfied = True
                  # print('satisfied', column, len(interval_index))
              cur_iteration += 1
              if cur_iteration >= max_iterations:
                  is_satisfied = True
                  # print('max_iterations met, stopping prior to satisfaction!', column, len(interval_index))
  
          bins_final = interval_index.right.values
          bin_mapping[column] = bins_final
      return bin_mapping
  
imports: import logging
  import numpy
  import pandas
  from pandas import DataFrame, IntervalIndex, Series
label_desc: Use image node_id 2 for calling a global function with example usage: generate_bins(X_features, features_to_bin, ideal_bins) and returns: bin_mapping