class_docstr: Plotting object allows for visualization of absolute group bias metrics and
  relative disparities calculated by Aequitas Group(), Bias(), and Fairness()
  class instances.
docstr: :param disparity_table: disparity table. output of bias.get_disparity, or
      fairness.get_fairness function.
  :param attribute: attributes: which attribute values (sample groups) to plot x and y metrics for.
  :param x_metric: the metric to plot on the X axis. Must be a column in the disparity_table.
  :param y_metric: the metric to plot on the Y axis. Must be a column in the disparity_table.
  :param x_jitter: jitter for x values. Default is None.
  :param y_jitter: jitter for y values. Default is None.
  :param selected_models: which models to visualize. Default is all models in disparity_table.
  :param ncols: The number of subplots to plot per row visualization
      figure.
      Default is 3.
  :param scatter_kws: keyword arguments for scatterplot
  :param title: whether to include a title in visualizations. Default is True.
  :param sharey: whether comparison subplots should share Y axis. Default is True
  :param show_figure: whether to show figure (plt.show()). Default is True.
  
  :return: A Matplotlib figure
code: def multimodel_attribute_comparison(
      self,
      disparity_table,
      attribute,
      x_metric,
      y_metric="precision",
      x_jitter=None,
      y_jitter=None,
      selected_models=None,
      ncols=3,
      scatter_kws={"legend": "full"},
      title=True,
      sharey=True,
      show_figure=True,
  ):
      """
      :param disparity_table: disparity table. output of bias.get_disparity, or
          fairness.get_fairness function.
      :param attribute: attributes: which attribute values (sample groups) to plot x and y metrics for.
      :param x_metric: the metric to plot on the X axis. Must be a column in the disparity_table.
      :param y_metric: the metric to plot on the Y axis. Must be a column in the disparity_table.
      :param x_jitter: jitter for x values. Default is None.
      :param y_jitter: jitter for y values. Default is None.
      :param selected_models: which models to visualize. Default is all models in disparity_table.
      :param ncols: The number of subplots to plot per row visualization
          figure.
          Default is 3.
      :param scatter_kws: keyword arguments for scatterplot
      :param title: whether to include a title in visualizations. Default is True.
      :param sharey: whether comparison subplots should share Y axis. Default is True
      :param show_figure: whether to show figure (plt.show()). Default is True.
  
      :return: A Matplotlib figure
      """
  
      df_models = self._check_multiple_models(
          disparity_table, method_table_name="disparity_table"
      )
  
      if not selected_models:
          selected_models = df_models
  
      attribute_table = disparity_table.loc[
          (disparity_table["attribute_name"] == attribute)
          & (disparity_table["model_id"].isin(selected_models))
      ]
  
      groups = attribute_table.attribute_value.unique()
  
      num_metrics = len(groups)
  
      total_plot_width = 25
  
      fig, axs, rows, axes_to_remove = self.generate_axes(
          ncols=ncols,
          num_metrics=num_metrics,
          total_plot_width=total_plot_width,
          sharey=sharey,
          hspace=0.5,
          indiv_height=6,
      )
  
      # set a different distribution to be plotted in each subplot
      ax_col = -1
      ax_row = 0
      col_num = 0
  
      viz_title = (
          f"MODEL COMPARISON: {x_metric.replace('_', ' ').upper()} BY {y_metric.replace('_', ' ').upper()} "
          f"ACROSS {attribute.replace('_', ' ').upper()}"
      )
  
      aq_palette = sns.diverging_palette(
          225, 35, sep=10, n=20, as_cmap=True, center="dark"
      )
  
      for group in groups:
          # subset df to get only that attribute, no need to aggregate
          group_table = attribute_table.loc[
              attribute_table["attribute_value"] == group
          ]
  
          if (
              group_table.loc[
                  group_table["attribute_value"] == group, x_metric
              ]
              .isnull()
              .all()
          ):
              logging.warning(
                  f"Cannot plot metric '{x_metric}' for group '{group}', only NaN values."
                  f" Continuing with remaining groups."
              )
              axes_to_remove += 1
              continue
  
          elif (
              group_table.loc[
                  group_table["attribute_value"] == group, y_metric
              ]
              .isnull()
              .all()
          ):
              logging.warning(
                  f"Cannot plot metric '{y_metric}' for group '{group}', only NaN values. "
                  f"Continuing with remaining groups."
              )
              axes_to_remove += 1
              continue
  
          current_subplot, ax_row, ax_col = self.iterate_subplots(
              axs, ncols, rows, ax_col, ax_row
          )
  
          with sns.axes_style("whitegrid"):
              # scatterplot of each model for that atttibute value group
              sns.scatterplot(
                  x=x_metric,
                  y=y_metric,
                  data=group_table,
                  hue="model_id",
                  palette=aq_palette,
                  x_jitter=x_jitter,
                  y_jitter=y_jitter,
                  ax=current_subplot,
                  **scatter_kws,
              )
  
          current_subplot.xaxis.grid(color="lightgray", which="major")
          current_subplot.yaxis.grid(color="lightgray", which="major")
          labels = [
              item.get_text().replace("_", " ").upper()
              for item in current_subplot.get_xticklabels()
          ]
          if "" not in labels:
              current_subplot.set_xticklabels(
                  labels, rotation=30, ha="center"
              )
          else:
              plt.xticks(rotation=30, horizontalalignment="center")
  
          x_clean = x_metric.replace("_", " ").upper()
          y_clean = y_metric.replace("_", " ").upper()
          current_subplot.set_xlabel(x_clean, fontsize=12)
          current_subplot.set_ylabel(y_clean, fontsize=12)
  
          handles, labels = current_subplot.get_legend_handles_labels()
          current_subplot.legend(
              handles=handles[1:],
              labels=[f"Model {model}" for model in labels[1:]],
              title="Model ID",
          )
          plot_title = f"MODEL COMPARISON:\n{y_clean} BY {x_clean} ({attribute.replace('_',' ').upper()}: {group.replace('_',' ').upper()})"
          current_subplot.set_title(plot_title, fontsize=20)
  
          # current_subplot.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
  
          col_num += 1
  
      # disable axes not being used
      if axes_to_remove > 0:
          for i in np.arange(axes_to_remove):
              axs[-1, -(i + 1)].axis("off")
  
      if title:
          plt.suptitle(f"{viz_title}", fontsize=25, fontweight="bold")
  
      if show_figure:
          plt.show()
  
      return fig
  
imports: import logging
  import math
  import numpy
  import collections
  import seaborn
  import matplotlib.pyplot
  import matplotlib.colors
  import matplotlib.cm
  from aequitas import squarify_flipped
label_desc: Use image node_id 25 for calling the Plot obj's underlying member method code with example usage: obj.multimodel_attribute_comparison(disparity_table, attribute, x_metric, y_metric, x_jitter, y_jitter, selected_models, ncols, scatter_kws, title, sharey, show_figure) and returns: fig