code: def plot_ceteris_paribus_cutoff(
      fobject,
      other_objects=None,
      title=None,
      verbose=True,
      grid_points=101,
      subgroup=None,
      metrics=["TPR", "ACC", "PPV", "FPR", "STP"],
      **kwargs,
  ):
      if subgroup is None:
          raise ParameterCheckError("parameter 'subgroup' is needed")
  
      protected = fobject.protected
      privileged = fobject.privileged
      if subgroup not in protected:
          raise ParameterCheckError(
              "parameter subgroup must be in protected parameter"
          )
  
      objects = [fobject]
      if other_objects is not None:
          basic_checks.check_other_fairness_objects(
              fobject, other_objects
          )
          for obj in other_objects:
              objects.append(obj)
  
      colors = _theme.get_default_colors(len(metrics), "line")
  
      labels = []
      for obj in objects:
          labels.append(obj.label)
  
      fig = make_subplots(
          rows=len(objects), cols=1, subplot_titles=labels
      )
  
      for k, object in enumerate(objects):
          cutoff = object.cutoff
          y = object.y
          y_hat = object.y_hat
          data = pd.DataFrame()
  
          # generate data for hypothetical cutoffs
          for i in range(1, grid_points):
              cutoff[subgroup] = i / grid_points
              sub_confusion_matrix = utils.SubgroupConfusionMatrix(
                  y_true=y,
                  y_pred=y_hat,
                  protected=protected,
                  cutoff=cutoff,
              )
  
              sub_confusion_matrix_metrics = (
                  utils.SubgroupConfusionMatrixMetrics(
                      sub_confusion_matrix
                  )
              )
              parity_loss = utils.calculate_parity_loss(
                  sub_confusion_matrix_metrics, privileged
              )
              parity_loss = parity_loss.loc[
                  parity_loss.index.isin(metrics)
              ]
              newdata = pd.DataFrame(
                  {
                      "score": parity_loss,
                      "metric": parity_loss.index,
                      "cutoff": i / (grid_points - 1),
                  }
              )
              newdata = newdata.reset_index(drop=True)
              data = pd.concat([data, newdata])
  
          data = data.reset_index(drop=True)
  
          # Find minimum where NA is not present
          pivoted_data = data.pivot(
              index="cutoff", columns="metric", values="score"
          )
          summed_metrics = pivoted_data.to_numpy().sum(axis=1)
          min_index = np.where(summed_metrics == min(summed_metrics))[
              0
          ][
              0
          ]  # get first minimal index
          min_cutoff = data.cutoff.unique()[min_index]
  
          # make figure from individual parts
          for j, metric in enumerate(metrics):
              metric_data = data.loc[data.metric == metric, :]
              fig.add_trace(
                  go.Scatter(
                      x=metric_data.cutoff,
                      y=metric_data.score,
                      mode="lines",
                      line=dict(color=colors[j]),
                      name=metric,
                      showlegend=k == 0,
                  ),  # show only legend of first subplot
                  row=k + 1,
                  col=1,
              )
  
          fig.add_shape(
              type="line",
              x0=min_cutoff,
              x1=min_cutoff,
              y0=0,
              y1=max(data.score),
              layer="below",
              line=dict(color="grey", dash="dot"),
              row=k + 1,
              col=1,
          )
  
          fig.add_annotation(
              x=min_cutoff,
              y=max(data.score) + 0.05,
              text=f"minimum: {min_cutoff}",
              row=k + 1,
              col=1,
          )
  
      fig.update_xaxes(
          title=f"cutoff for {subgroup}, other cutoffs constant"
      )
      fig.update_yaxes(title="parity loss")
  
      if title is None:
          title = "Ceteris Paribus Cutoff"
      fig.update_layout(utils._fairness_theme(title))
      fig.update_layout(hovermode="x")
      fig.update_traces(
          hovertemplate="<br>".join(["parity loss: %{y:.3f}"])
      )
  
      return fig
  
imports: import numpy
  import pandas
  import plotly.express
  import plotly.graph_objects
  from plotly.subplots import make_subplots
  from .None import checks
  from .None import utils
  from .._basics import checks
  from .._basics.exceptions import ParameterCheckError
  from ...None import _theme
  from ..._explainer import helper
label_desc: Use image node_id 9 for calling a global function with example usage: plot_ceteris_paribus_cutoff(fobject, other_objects, title, verbose, grid_points, subgroup, metrics) and returns: fig