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: Compare two absolute bias metrics or bias metric disparities across models.
  
  :param disparity_table: disparity table. output of bias.get_disparity, or
      fairness.get_fairness function.
  :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_agg_method: Method to aggregate metric values for X axis. Options: 'mean', 'median', 'max', 'min'. Default is 'mean'. For absolute metrics, 'mean' aggregation is a weighted average by group size.
  :param y_agg_method: Method to aggregate metric values for Y axis. Options: 'mean', 'median', 'max', 'min'. Default is 'mean'. For absolute metrics, 'mean' aggregation is a weighted average by group size.
  :param title: whether to include a title in visualizations. Default is True.
  :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 ax: a matplotlib Axis. If not passed, a new figure will be created.
  :param scatter_kws: keyword arguments for scatterplot
  :param show_figure: whether to show figure (plt.show()). Default is True.
  
  :return: A Matplotlib axis
code: def multimodel_comparison(
      self,
      disparity_table,
      x_metric,
      y_metric="precision",
      x_agg_method="mean",
      y_agg_method="mean",
      title=True,
      x_jitter=None,
      y_jitter=None,
      selected_models=None,
      ax=None,
      scatter_kws={"legend": "full"},
      show_figure=True,
  ):
      """
      Compare two absolute bias metrics or bias metric disparities across models.
  
      :param disparity_table: disparity table. output of bias.get_disparity, or
          fairness.get_fairness function.
      :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_agg_method: Method to aggregate metric values for X axis. Options: 'mean', 'median', 'max', 'min'. Default is 'mean'. For absolute metrics, 'mean' aggregation is a weighted average by group size.
      :param y_agg_method: Method to aggregate metric values for Y axis. Options: 'mean', 'median', 'max', 'min'. Default is 'mean'. For absolute metrics, 'mean' aggregation is a weighted average by group size.
      :param title: whether to include a title in visualizations. Default is True.
      :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 ax: a matplotlib Axis. If not passed, a new figure will be created.
      :param scatter_kws: keyword arguments for scatterplot
      :param show_figure: whether to show figure (plt.show()). Default is True.
  
      :return: A Matplotlib axis
      """
  
      df_models = self._check_multiple_models(
          disparity_table, method_table_name="disparities_table"
      )
  
      if not selected_models:
          selected_models = df_models
  
      plot_table = disparity_table.loc[
          disparity_table["model_id"].isin(selected_models)
      ]
  
      # requirement: at least two model_id values
      if len(selected_models) < 2:
          raise ValueError(
              "This method requires at least two distinct 'model_id' values "
              "in the disparities table. Tip: check that "
              "disparities_table.model_id.unique() returns more than one element."
          )
  
      # must be valid metric
      if x_metric not in plot_table.columns:
          raise ValueError(
              f"Absolute metric '{x_metric}' is not included in disparities_table."
          )
  
      if y_metric not in plot_table.columns:
          raise ValueError(
              f"Disparity metric '{y_metric}' is not included in disparities_table."
          )
  
      # must be valid aggregation method
      if (x_agg_method not in ("mean", "median", "max", "min")) or (
          y_agg_method not in ("mean", "median", "max", "min")
      ):
          raise ValueError(
              "Aggregation methods 'x_agg_method' and 'y_agg_method' must "
              "take one of the following values: 'mean', 'median', 'max', 'min'."
          )
  
      # should never really have NaNs for one model but not another, but handling JIC
      get_indices = lambda x: ~np.isnan(x)
      get_weights = lambda x: plot_table.loc[x.index, "group_size"]
      wtd_mean = lambda x: (
          np.average(
              x[get_indices(x)],
              axis=0,
              weights=get_weights(x)[get_indices(x)],
          )
      )
  
      if x_agg_method == "mean":
          if "_disparity" not in x_metric:
              x_agg_method = wtd_mean
  
      if y_agg_method == "mean":
          if "_disparity" not in y_metric:
              y_agg_method = wtd_mean
  
      collected_df = plot_table.groupby("model_id", as_index=False).agg(
          {x_metric: x_agg_method, y_metric: y_agg_method}
      )
  
      if ax is None:
          fig, ax = plt.subplots(figsize=(8, 5))
  
      aq_palette = sns.diverging_palette(
          225, 35, sep=10, n=40, as_cmap=True, center="dark"
      )
  
      with sns.axes_style("whitegrid"):
          ax = sns.scatterplot(
              x=x_metric,
              y=y_metric,
              data=collected_df,
              hue="model_id",
              x_jitter=x_jitter,
              y_jitter=y_jitter,
              palette=aq_palette,
              alpha=0.75,
              **scatter_kws,
          )
  
      ax.xaxis.grid(color="lightgray", which="major")
      ax.yaxis.grid(color="lightgray", which="major")
      labels = [
          item.get_text().replace("_", " ").upper()
          for item in ax.get_xticklabels()
      ]
      if "" not in labels:
          ax.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()
      ax.set_xlabel(x_clean, fontsize=12)
      ax.set_ylabel(y_clean, fontsize=12)
  
      handles, labels = ax.get_legend_handles_labels()
      ax.legend(
          handles=handles[1:],
          labels=[f"Model {model}" for model in labels[1:]],
          title="Model ID",
      )
  
      # plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
  
      if title:
          plot_title = f"MODEL COMPARISON: {y_clean} BY {x_clean}"
          ax.set_title(plot_title, fontsize=20)
  
      if show_figure:
          plt.show()
  
      else:
          return ax
  
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 26 for calling the Plot obj's underlying member method code with example usage: obj.multimodel_comparison(disparity_table, x_metric, y_metric, x_agg_method, y_agg_method, title, x_jitter, y_jitter, selected_models, ax, scatter_kws, show_figure) and returns: ax