code: def k_prototypes(
      X,
      categorical,
      n_clusters,
      max_iter,
      num_dissim,
      cat_dissim,
      gamma,
      init,
      n_init,
      verbose,
      random_state,
      n_jobs,
      sample_weight=None,
  ):
      """k-prototypes algorithm"""
      random_state = check_random_state(random_state)
      if sparse.issparse(X):
          raise TypeError("k-prototypes does not support sparse data.")
  
      if categorical is None or not categorical:
          raise NotImplementedError(
              "No categorical data selected, effectively doing k-means. "
              "Present a list of categorical columns, or use scikit-learn's "
              "KMeans instead."
          )
      if isinstance(categorical, int):
          categorical = [categorical]
      assert (
          len(categorical) != X.shape[1]
      ), "All columns are categorical, use k-modes instead of k-prototypes."
      assert (
          max(categorical) < X.shape[1]
      ), "Categorical index larger than number of columns."
  
      ncatattrs = len(categorical)
      nnumattrs = X.shape[1] - ncatattrs
      n_points = X.shape[0]
      assert n_clusters <= n_points, (
          f"Cannot have more clusters ({n_clusters}) "
          f"than data points ({n_points})."
      )
  
      Xnum, Xcat = _split_num_cat(X, categorical)
      Xnum, Xcat = check_array(Xnum), check_array(Xcat, dtype=None)
  
      # Convert the categorical values in Xcat to integers for speed.
      # Based on the unique values in Xcat, we can make a mapping to achieve this.
      Xcat, enc_map = encode_features(Xcat)
  
      # Are there more n_clusters than unique rows? Then set the unique
      # rows as initial values and skip iteration.
      unique = get_unique_rows(X)
      n_unique = unique.shape[0]
      if n_unique <= n_clusters:
          max_iter = 0
          n_init = 1
          n_clusters = n_unique
          init = list(_split_num_cat(unique, categorical))
          init[1], _ = encode_features(init[1], enc_map)
  
      # Estimate a good value for gamma, which determines the weighing of
      # categorical values in clusters (see Huang [1997]).
      if gamma is None:
          gamma = 0.5 * np.mean(Xnum.std(axis=0))
  
      results = []
      seeds = random_state.randint(np.iinfo(np.int32).max, size=n_init)
      if n_jobs == 1:
          for init_no in range(n_init):
              results.append(
                  _k_prototypes_single(
                      Xnum,
                      Xcat,
                      nnumattrs,
                      ncatattrs,
                      n_clusters,
                      n_points,
                      max_iter,
                      num_dissim,
                      cat_dissim,
                      gamma,
                      init,
                      init_no,
                      verbose,
                      seeds[init_no],
                      sample_weight,
                  )
              )
      else:
          results = Parallel(n_jobs=n_jobs, verbose=0)(
              delayed(_k_prototypes_single)(
                  Xnum,
                  Xcat,
                  nnumattrs,
                  ncatattrs,
                  n_clusters,
                  n_points,
                  max_iter,
                  num_dissim,
                  cat_dissim,
                  gamma,
                  init,
                  init_no,
                  verbose,
                  seed,
                  sample_weight,
              )
              for init_no, seed in enumerate(seeds)
          )
      (
          all_centroids,
          all_labels,
          all_costs,
          all_n_iters,
          all_epoch_costs,
      ) = zip(*results)
  
      best = np.argmin(all_costs)
      if n_init > 1 and verbose:
          print(f"Best run was number {best + 1}")
  
      # Note: return gamma in case it was automatically determined.
      return (
          all_centroids[best],
          enc_map,
          all_labels[best],
          all_costs[best],
          all_n_iters[best],
          all_epoch_costs[best],
          gamma,
      )
  
imports: from collections import defaultdict
  import numpy
  from joblib import Parallel, delayed
  from scipy import sparse
  from sklearn.utils import check_random_state
  from sklearn.utils.validation import check_array
  from .None import kmodes
  from .util import get_max_value_key, encode_features, get_unique_rows, decode_centroids, pandas_to_numpy
  from .util.dissim import matching_dissim, euclidean_dissim
  from .util.init_methods import init_cao, init_huang
label_desc: Use image node_id 2 for calling a global function with example usage: k_prototypes(X, categorical, n_clusters, max_iter, num_dissim, cat_dissim, gamma, init, n_init, verbose, random_state, n_jobs, sample_weight) and returns: all_centroids,  enc_map,  all_labels,  all_costs,  all_n_iters,  all_epoch_costs,  gamma