code: def plot_classification_categorical(
      X,
      *,
      target_col,
      types=None,
      kind="auto",
      hue_order=None,
      **kwargs,
  ):
      """Plots for categorical features in classification.
  
      Creates plots of categorical variable distributions for each target class.
      Relevant features are identified via mutual information.
  
      For high cardinality categorical variables (variables with many categories)
      only the most frequent categories are shown.
  
      Parameters
      ----------
      X : dataframe
          Input data including features and target
      target_col : str or int
          Identifier of the target column in X
      types : dataframe of types, optional.
          Output of detect_types on X. Can be used to avoid recomputing the
          types.
      kind : string, default 'auto'
          Kind of plot to show. Options are 'count', 'proportion',
          'mosaic' and 'auto'.
          Count shows raw class counts within categories
          (can be hard to read with imbalanced classes)
          Proportion shows class proportions within categories
          (can be misleading with imbalanced categories)
          Mosaic shows both aspects, but can be a bit busy.
          Auto uses mosaic plots for binary classification and counts otherwise.
  
      """
      types = _check_X_target_col(
          X, target_col, types, task="classification"
      )
      if kind == "auto":
          if X[target_col].nunique() > 5:
              kind = "count"
          else:
              kind = "mosaic"
  
      features = X.loc[
          :, types.categorical + types.low_card_int_categorical
      ]
      if target_col in features.columns:
          features = features.drop(target_col, axis=1)
  
      if features.shape[1] == 0:
          return
  
      features = features.astype("category")
  
      show_top = _get_n_top(features, "categorical")
  
      # can't use OrdinalEncoder because we might have mix of int and string
      ordinal_encoded = features.apply(lambda x: x.cat.codes)
      target = X[target_col]
      f = mutual_info_classif(
          ordinal_encoded,
          target,
          discrete_features=np.ones(X.shape[1], dtype=bool),
      )
      top_k = np.argsort(f)[-show_top:][::-1]
  
      if kind == "sankey":
          return plot_sankey(
              X, target_col, figure=kwargs.get("figure", None)
          )
      # large number of categories -> taller plot
      row_height = 3 if features.nunique().max() <= 5 else 5
      fig, axes = _make_subplots(
          n_plots=show_top, row_height=row_height
      )
      plt.suptitle("Categorical Features vs Target", y=1.02)
      for i, (col_ind, ax) in enumerate(zip(top_k, axes.ravel())):
          col = features.columns[col_ind]
          # how many categories make up at least 1% of data:
          n_cats = (X[col].value_counts() / len(X) > 0.01).sum()
          n_cats = np.minimum(n_cats, 20)
          X_new = _prune_category_make_X(
              X, col, target_col, max_categories=n_cats
          )
          if kind == "proportion":
              df = (
                  X_new.groupby(col)[target_col]
                  .value_counts(normalize=True)
                  .unstack()
                  .sort_values(by=target[0])
              )  # hacky way to get a class name
              df.plot(kind="barh", stacked="True", ax=ax, legend=i == 0)
              ax.set_ylabel(None)
          elif kind == "mosaic":
              mosaic_plot(X_new, col, target_col, ax=ax, legend=i == 0)
          elif kind == "count":
              X_new = _prune_category_make_X(X, col, target_col)
              # absolute counts
              # FIXME show f value
              # FIXME shorten titles?
              props = {}
              if X[target_col].nunique() > 15:
                  props["font.size"] = 6
              with mpl.rc_context(props):
                  sns.countplot(
                      y=col,
                      data=X_new,
                      ax=ax,
                      hue=target_col,
                      hue_order=hue_order,
                  )
              if i > 0:
                  ax.legend(())
          else:
              raise ValueError("Unknown plot kind {}".format(kind))
          _short_tick_names(ax)
          ax.set_title(col)
  
      for j in range(i + 1, axes.size):
          # turn off axis if we didn't fill last row
          axes.ravel()[j].set_axis_off()
      return axes
  
imports: import warnings
  import numpy
  import matplotlib.pyplot
  import matplotlib
  import seaborn
  import pandas
  from sklearn.feature_selection import mutual_info_regression, mutual_info_classif, f_classif
  from sklearn.impute import SimpleImputer
  from sklearn.preprocessing import scale
  from sklearn.decomposition import PCA
  from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  from sklearn.metrics import recall_score
  from ..preprocessing import detect_types, clean
  from .utils import _check_X_target_col, _get_n_top, _make_subplots, _short_tick_names, _shortname, _prune_category_make_X, find_pretty_grid, _find_scatter_plots_classification, class_hists, discrete_scatter, mosaic_plot, _find_inliers, pairplot, _get_scatter_alpha, _get_scatter_size, _find_categorical_for_regression, _prune_categories, _apply_eng_formatter
  from .sankey import plot_sankey
  from warnings import warn
label_desc: Use image node_id 8 for calling a global function with example usage: plot_classification_categorical(X) and returns: axes, plot_sankey