code: def _error_scatter_trace(
      arms: Sequence[Union[PlotInSampleArm, PlotOutOfSampleArm]],
      y_axis_var: PlotMetric,
      x_axis_var: Optional[PlotMetric] = None,
      y_axis_label: Optional[str] = None,
      x_axis_label: Optional[str] = None,
      status_quo_arm: Optional[PlotInSampleArm] = None,
      show_CI: bool = True,
      name: str = "In-sample",
      color: Tuple[int] = COLORS.STEELBLUE.value,
      visible: bool = True,
      legendgroup: Optional[str] = None,
      showlegend: bool = True,
      hoverinfo: str = "text",
      show_arm_details_on_hover: bool = True,
      show_context: bool = False,
      arm_noun: str = "arm",
      color_parameter: Optional[str] = None,
      color_metric: Optional[str] = None,
  ) -> Dict[str, Any]:
      """Plot scatterplot with error bars.
  
      Args:
          arms (List[Union[PlotInSampleArm, PlotOutOfSampleArm]]):
              a list of in-sample or out-of-sample arms.
              In-sample arms have observed data, while out-of-sample arms
              just have predicted data. As a result,
              when passing out-of-sample arms, pred must be True.
          y_axis_var: name of metric for y-axis, along with whether
              it is observed or predicted.
          x_axis_var: name of metric for x-axis,
              along with whether it is observed or predicted. If None, arm names
              are automatically used.
          y_axis_label: custom label to use for y axis.
              If None, use metric name from `y_axis_var`.
          x_axis_label: custom label to use for x axis.
              If None, use metric name from `x_axis_var` if that is not None.
          status_quo_arm: the status quo
              arm. Necessary for relative metrics.
          show_CI: if True, plot confidence intervals.
          name: name of trace. Default is "In-sample".
          color: color as rgb tuple. Default is
              (128, 177, 211), which corresponds to COLORS.STEELBLUE.
          visible: if True, trace is visible (default).
          legendgroup: group for legends.
          showlegend: if True, legend if rendered.
          hoverinfo: information to show on hover. Default is
              custom text.
          show_arm_details_on_hover: if True, display
              parameterizations of arms on hover. Default is True.
          show_context: if True and show_arm_details_on_hover,
              context will be included in the hover.
          arm_noun: noun to use instead of "arm" (e.g. group)
          color_parameter: color points according to the specified parameter,
              cannot be used together with color_metric.
          color_metric: color points according to the specified metric,
              cannot be used together with color_parameter.
      """
      if color_metric and color_parameter:
          raise RuntimeError(
              "color_metric and color_parameter cannot be used at the same time!"
          )
  
      if (color_metric or color_parameter) and not all(
          isinstance(arm, PlotInSampleArm) for arm in arms
      ):
          raise RuntimeError(
              "Color coding currently only works with in-sample arms!"
          )
  
      # Opportunistically sort if arm names are in {trial}_{arm} format
      arms = sorted(
          arms, key=lambda a: arm_name_to_sort_key(a.name), reverse=True
      )
  
      x, x_se, y, y_se = _error_scatter_data(
          arms=arms,
          y_axis_var=y_axis_var,
          x_axis_var=x_axis_var,
          status_quo_arm=status_quo_arm,
      )
      labels = []
      colors = []
  
      arm_names = [a.name for a in arms]
  
      # No relativization if no x variable.
      rel_x = x_axis_var.rel if x_axis_var else False
      rel_y = y_axis_var.rel
  
      for i in range(len(arm_names)):
          heading = f"<b>{arm_noun.title()} {arm_names[i]}</b><br>"
          x_lab = (
              "{name}: {estimate}{perc} {ci}<br>".format(
                  name=x_axis_var.metric
                  if x_axis_label is None
                  else x_axis_label,
                  estimate=(
                      round(x[i], DECIMALS)
                      if isinstance(x[i], numbers.Number)
                      else x[i]
                  ),
                  ci=""
                  if x_se is None
                  else _format_CI(x[i], x_se[i], rel_x),
                  perc="%" if rel_x else "",
              )
              if x_axis_var is not None
              else ""
          )
          y_lab = "{name}: {estimate}{perc} {ci}<br>".format(
              name=y_axis_var.metric
              if y_axis_label is None
              else y_axis_label,
              estimate=(
                  round(y[i], DECIMALS)
                  if isinstance(y[i], numbers.Number)
                  else y[i]
              ),
              ci=""
              if y_se is None
              else _format_CI(y[i], y_se[i], rel_y),
              perc="%" if rel_y else "",
          )
  
          parameterization = (
              _format_dict(arms[i].parameters, "Parameterization")
              if show_arm_details_on_hover
              else ""
          )
  
          if color_parameter:
              colors.append(arms[i].parameters[color_parameter])
          elif color_metric:
              # Must be PlotInSampleArm here if no error raised previously
              # pyre-ignore[16]: `PlotOutOfSampleArm` has no attribute `y`
              colors.append(arms[i].y[color_metric])
  
          context = (
              # Expected `Dict[str, Optional[Union[bool, float, str]]]` for 1st anonymous
              # parameter to call `ax.plot.helper._format_dict` but got
              # `Optional[Dict[str, Union[float, str]]]`.
              # pyre-fixme[6]:
              _format_dict(arms[i].context_stratum, "Context")
              if show_arm_details_on_hover
              and show_context  # noqa W503
              and arms[i].context_stratum  # noqa W503
              else ""
          )
  
          labels.append(
              "{arm_name}<br>{xlab}{ylab}{param_blob}{context}".format(
                  arm_name=heading,
                  xlab=x_lab,
                  ylab=y_lab,
                  param_blob=parameterization,
                  context=context,
              )
          )
          i += 1
  
      if color_metric or color_parameter:
          rgba_blue_scale = [rgba(c) for c in BLUE_SCALE]
          marker = {
              "color": colors,
              "colorscale": rgba_blue_scale,
              "colorbar": {"title": color_metric or color_parameter},
              "showscale": True,
          }
      else:
          marker = {"color": rgba(color)}
  
      trace = go.Scatter(
          x=x,
          y=y,
          marker=marker,
          mode="markers",
          name=name,
          text=labels,
          hoverinfo=hoverinfo,
      )
  
      if show_CI:
          if x_se is not None:
              trace.update(
                  error_x={
                      "type": "data",
                      "array": np.multiply(x_se, Z),
                      "color": rgba(color, CI_OPACITY),
                  }
              )
          if y_se is not None:
              trace.update(
                  error_y={
                      "type": "data",
                      "array": np.multiply(y_se, Z),
                      "color": rgba(color, CI_OPACITY),
                  }
              )
      if visible is not None:
          trace.update(visible=visible)
      if legendgroup is not None:
          trace.update(legendgroup=legendgroup)
      if showlegend is not None:
          trace.update(showlegend=showlegend)
      return trace
  
imports: import numbers
  import warnings
  from collections import OrderedDict
  from logging import Logger
  from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union
  import numpy
  import plotly.graph_objs
  from ax.core.data import Data
  from ax.core.experiment import Experiment
  from ax.core.observation import Observation, ObservationFeatures
  from ax.modelbridge.base import ModelBridge
  from ax.modelbridge.registry import Models
  from ax.plot.base import AxPlotConfig, AxPlotTypes, CI_OPACITY, DECIMALS, PlotInSampleArm, PlotMetric, PlotOutOfSampleArm, Z
  from ax.plot.color import BLUE_SCALE, COLORS, DISCRETE_COLOR_SCALE, rgba
  from ax.plot.helper import _format_CI, _format_dict, _wrap_metric, arm_name_to_sort_key, arm_name_to_tuple, get_plot_data, infer_is_relative, resize_subtitles, TNullableGeneratorRunsDict
  from ax.utils.common.logger import get_logger
  from ax.utils.common.typeutils import checked_cast_optional
  from ax.utils.stats.statstools import relativize
  from plotly import subplots
label_desc: Use image node_id 2 for calling a global function with example usage: _error_scatter_trace(arms, y_axis_var, x_axis_var, y_axis_label, x_axis_label, status_quo_arm, show_CI, name, color, visible, legendgroup, showlegend, hoverinfo, show_arm_details_on_hover, show_context, arm_noun, color_parameter, color_metric) and returns: trace