code: def plot_fairness_check(data, n_models, epsilon, title):
      upper_bound = max(
          [
              max(data.score[np.invert(pd.isna(data.score))]),
              1 / epsilon - 1,
          ]
      )
      upper_bound = upper_bound * 1.05
      lower_bound = min(
          [min(data.score[np.invert(pd.isna(data.score))]), epsilon - 1]
      )
      lower_bound = lower_bound * 1.1
      upper_bound = round(upper_bound, 1)
  
      # drwhy colors
      colors = _theme.get_default_colors(n_models, "line")
  
      # subgroup y-axis value creation
      n_ticks = len(data.subgroup.unique())
      tick_values = np.linspace(0, 1, n_ticks + 2)[1:-1]
  
      subgroup_tick_dict = {}
      for i in range(len(data.subgroup.unique())):
          subgroup = data.subgroup.unique()[i]
          subgroup_tick_dict[subgroup] = tick_values[i]
  
      data["subgroup_numeric"] = [
          subgroup_tick_dict.get(sub) for sub in data.subgroup
      ]
      data = data.reset_index(drop=True)
  
      # for hover
      data["dispx"] = np.round(data.score.astype(float) + 1, 3)
      # make fig
      fig = px.bar(
          data,
          y="subgroup_numeric",
          x="score",
          color="label",
          color_discrete_sequence=colors,
          facet_col="metric",
          facet_col_wrap=1,
          barmode="group",
          orientation="h",
          custom_data=["subgroup", "dispx", "label"],
      )
  
      fig.update_traces(
          hovertemplate="<br>".join(
              [
                  "<b>%{customdata[2]}</b><br>"
                  "Subgroup: %{customdata[0]}",
                  "Score: %{customdata[1]}",
                  "<extra></extra>",
              ]
          )
      )
  
      # change axes range and labs
      # including upper bound
      # if upper_bound < 1:
      #     # tick are calculated by backend data
      #     if upper_bound < 0.4 and lower_bound > -0.4:
      #         ticks = np.arange(lower_bound, upper_bound + 0.001, step=0.1).round(1)
      #     else:
      #         ticks = np.arange(lower_bound, upper_bound + 0.001, step=0.2).round(1)
      #
      #     ticks = np.append(ticks, 0)
      #     fig.update_xaxes(tickvals=ticks,
      #                      ticktext=(ticks + 1).round(1),
      #                      range=[lower_bound, upper_bound])
      #
      # else:
      # ticks are calculated by appearance
      min_value, max_value, spacing = utils.get_nice_ticks(
          lower_bound + 1, upper_bound + 1
      )
  
      ticks = np.arange(min_value, max_value, step=spacing).round(1)
      ticks = np.append(ticks, 1)
      fig.update_xaxes(
          tickvals=ticks - 1,
          ticktext=ticks,
          range=[lower_bound, upper_bound],
      )
  
      fig.update_yaxes(
          tickvals=list(subgroup_tick_dict.values()),
          ticktext=list(subgroup_tick_dict.keys()),
          range=[0, 1],
      )
  
      # refs are dependent on fixed numbers of metrics
  
      n_metrics = len(np.unique(data.metric))
      refs = ["y" + str(i) for i in range(2, n_metrics + 1)]
      refs.insert(0, "y")
  
      left_red = [
          {
              "type": "rect",
              "x0": lower_bound,
              "y0": 0,
              "x1": epsilon - 1,
              "y1": 1,
              "yref": yref,
              "line": {"width": 0},
              "fillcolor": "#f05a71",
              "layer": "below",
              "opacity": 0.1,
          }
          for yref in refs
      ]
  
      middle_green = [
          {
              "type": "rect",
              "x0": epsilon - 1,
              "y0": 0,
              "x1": 1 / epsilon - 1,
              "y1": 1,
              "yref": yref,
              "line": {"width": 0},
              "fillcolor": "#c7f5bf",
              "layer": "below",
              "opacity": 0.1,
          }
          for yref in refs
      ]
  
      right_red = [
          {
              "type": "rect",
              "x0": 1 / epsilon - 1,
              "y0": 0,
              "x1": upper_bound,
              "y1": 1,
              "yref": yref,
              "line": {"width": 0},
              "fillcolor": "#f05a71",
              "layer": "below",
              "opacity": 0.1,
          }
          for yref in refs
      ]
  
      black_line = [
          {
              "type": "line",
              "x0": 0,
              "x1": 0,
              "y0": 0,
              "y1": 1,
              "yref": yref,
              "xref": "x",
              "line": {"color": "#371ea3", "width": 1.5},
          }
          for yref in refs
      ]
  
      for i in range(len(refs)):
          fig.add_shape(right_red[i])
          fig.add_shape(left_red[i])
          fig.add_shape(middle_green[i])
          fig.add_shape(black_line[i])
  
          fig.update_shapes(dict(xref="x"))
  
      if title is None:
          title = "Fairness Check"
  
      fig.update_layout(utils._fairness_theme(title))
  
      # delete 'metric=' from facet names
      fig.for_each_annotation(
          lambda a: a.update(
              text=a.text.replace("metric=", ""),
              xanchor="left",
              x=0.05,
              font={"size": 15},
          )
      )
  
      # delete y axis names [fixed] number of refs
      for i in [ref[1:] for ref in refs]:
          fig.update_layout({"yaxis" + i + "_title_text": ""})
  
      middle_ref_num = refs[len(refs) // 2][1:]
      fig.update_layout(
          {"yaxis" + str(middle_ref_num) + "_title_text": "subgroup"}
      )
      fig.update_yaxes(showgrid=False, zeroline=False)
  
      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 1 for calling a global function with example usage: plot_fairness_check(data, n_models, epsilon, title) and returns: fig