code: def plotly_shap_violin_plot(
      X_col,
      shap_values,
      X_color_col=None,
      points=False,
      interaction=False,
      units="",
      highlight_index=None,
      idxs=None,
      round=3,
      cats_order=None,
      max_cat_colors=5,
  ):  # sourcery no-metrics
      """Generates a violin plot for displaying shap value distributions for
      categorical features.
  
      Args:
          X_col (pd.DataFrame): dataframe of input rows
          shap_values (np.ndarray): shap values generated for X
          col_name (str): Column of X to display violin plot for
          color_col (str, optional): Column of X to color plot markers by.
              Defaults to None.
          points (bool, optional): display point cloud next to violin plot.
              Defaults to False.
          interaction (bool, optional): Is this a plot for shap_interaction_values?
              Defaults to False.
          units (str, optional): Units of target variable. Defaults to "".
          highlight_index (int, str, optional): Row index to highligh. Defaults to None.
          idxs (List[str], optional): List of identifiers for each row in X, e.g.
              names or id's. Defaults to None.
          cats_order (list, optional): list of categories to display. If None
              defaults to X_col.unique().tolist() so displays all categories.
          max_cat_colors (int, optional): maximum number of X_color_col categories
              to colorize in scatter plot next to violin plot. Defaults to 5.
  
      Returns:
          Plotly fig
      """
  
      assert not is_numeric_dtype(
          X_col
      ), f"{X_col.name} is not categorical! Can only plot violin plots for categorical features!"
  
      if cats_order is None:
          cats_order = sorted(X_col.unique().tolist())
  
      n_cats = len(cats_order)
  
      if idxs is not None:
          assert len(idxs) == X_col.shape[0] == len(shap_values)
          idxs = pd.Index(idxs).astype(str)
      else:
          idxs = X_col.index.astype(str)
  
      if highlight_index is not None:
          if isinstance(highlight_index, int):
              highlight_idx = highlight_index
              highlight_name = idxs[highlight_idx]
          elif isinstance(highlight_index, str):
              assert (
                  highlight_index in idxs
              ), f"highlight_index should be int or in idxs, {highlight_index} is neither!"
              highlight_idx = idxs.get_loc(highlight_index)
              highlight_name = highlight_index
  
      if points or X_color_col is not None:
          fig = make_subplots(
              rows=1,
              cols=2 * n_cats,
              column_widths=[3, 1] * n_cats,
              shared_yaxes=True,
          )
          showscale = True
      else:
          fig = make_subplots(rows=1, cols=n_cats, shared_yaxes=True)
  
      shap_range = shap_values.max() - shap_values.min()
      fig.update_yaxes(
          range=[
              shap_values.min() - 0.1 * shap_range,
              shap_values.max() + 0.1 * shap_range,
          ]
      )
  
      if X_color_col is not None:
          color_cats = list(
              X_color_col.value_counts().index[:max_cat_colors]
          )
          n_color_cats = len(color_cats)
          colors = [
              "#636EFA",
              "#EF553B",
              "#00CC96",
              "#AB63FA",
              "#FFA15A",
              "#19D3F3",
              "#FF6692",
              "#B6E880",
              "#FF97FF",
              "#FECB52",
          ]
          colors = colors * (1 + int(n_color_cats / len(colors)))
          colors = colors[:n_color_cats]
          show_legend = set(color_cats + ["Category_Other"])
  
      for i, cat in enumerate(cats_order):
          col = (
              1 + i * 2 if points or X_color_col is not None else 1 + i
          )
          if cat.startswith(X_col.name + "_"):
              cat_name = cat[len(X_col.name) + 1 :]
          else:
              cat_name = cat
          fig.add_trace(
              go.Violin(
                  x=np.repeat(cat_name, len(X_col[X_col == cat])),
                  y=shap_values[X_col == cat],
                  name=cat_name,
                  box_visible=True,
                  meanline_visible=True,
                  showlegend=False,
              ),
              row=1,
              col=col,
          )
          if X_color_col is not None:
              if is_numeric_dtype(X_color_col):
                  fig.add_trace(
                      go.Scattergl(
                          x=np.random.randn((X_col == cat).sum()),
                          y=shap_values[X_col == cat],
                          name=X_color_col.name,
                          mode="markers",
                          showlegend=False,
                          hoverinfo="text",
                          text=[
                              f"{idxs.name}: {index}<br>shap: {shap:.{round}f}<br>{X_color_col.name}: {col}"
                              for index, shap, col in zip(
                                  idxs[X_col == cat],
                                  shap_values[X_col == cat],
                                  X_color_col[X_col == cat],
                              )
                          ],
                          marker=dict(
                              size=7,
                              opacity=0.3,
                              cmin=X_color_col.min(),
                              cmax=X_color_col.max(),
                              color=X_color_col[X_col == cat],
                              colorscale="Bluered",
                              showscale=showscale,
                              colorbar=dict(title=X_color_col.name),
                          ),
                      ),
                      row=1,
                      col=col + 1,
                  )
              else:
                  for color_cat, color in zip(color_cats, colors):
                      if color_cat.startswith(X_color_col.name + "_"):
                          color_cat_name = color_cat[
                              len(X_color_col.name) + 1 :
                          ]
                      else:
                          color_cat_name = color_cat
  
                      fig.add_trace(
                          go.Scattergl(
                              x=np.random.randn(
                                  (
                                      (X_col == cat)
                                      & (X_color_col == color_cat)
                                  ).sum()
                              ),
                              y=shap_values[
                                  (X_col == cat)
                                  & (X_color_col == color_cat)
                              ],
                              name=color_cat_name,
                              mode="markers",
                              showlegend=color_cat in show_legend,
                              hoverinfo="text",
                              text=[
                                  f"{idxs.name}: {index}<br>shap: {shap:.{round}f}<br>{X_color_col.name}: {color_cat_name}"
                                  for index, shap in zip(
                                      idxs[
                                          (X_col == cat)
                                          & (X_color_col == color_cat)
                                      ],
                                      shap_values[
                                          (X_col == cat)
                                          & (X_color_col == color_cat)
                                      ],
                                  )
                              ],
                              marker=dict(
                                  size=7, opacity=0.3, color=color
                              ),
                          ),
                          row=1,
                          col=col + 1,
                      )
                      if (
                          color_cat
                          in X_color_col[X_col == cat].unique()
                      ):
                          show_legend = show_legend - {color_cat}
                  if X_color_col.nunique() > max_cat_colors:
                      fig.add_trace(
                          go.Scattergl(
                              x=np.random.randn(
                                  (
                                      (X_col == cat)
                                      & (~X_color_col.isin(color_cats))
                                  ).sum()
                              ),
                              y=shap_values[
                                  (X_col == cat)
                                  & (~X_color_col.isin(color_cats))
                              ],
                              name="Other",
                              mode="markers",
                              showlegend="Category_Other"
                              in show_legend,
                              hoverinfo="text",
                              text=[
                                  f"{idxs.name}: {index}<br>shap: {shap:.{round}f}<br>{X_color_col.name}: {col}"
                                  for index, shap, col in zip(
                                      idxs[
                                          (X_col == cat)
                                          & (
                                              ~X_color_col.isin(
                                                  color_cats
                                              )
                                          )
                                      ],
                                      shap_values[
                                          (X_col == cat)
                                          & (
                                              ~X_color_col.isin(
                                                  color_cats
                                              )
                                          )
                                      ],
                                      X_color_col[
                                          (X_col == cat)
                                          & (
                                              ~X_color_col.isin(
                                                  color_cats
                                              )
                                          )
                                      ],
                                  )
                              ],
                              marker=dict(
                                  size=7, opacity=0.3, color="grey"
                              ),
                          ),
                          row=1,
                          col=col + 1,
                      )
                      show_legend = show_legend - {"Category_Other"}
  
              showscale = False
          elif points:
              fig.add_trace(
                  go.Scattergl(
                      x=np.random.randn((X_col == cat).sum()),
                      y=shap_values[X_col == cat],
                      mode="markers",
                      showlegend=False,
                      hoverinfo="text",
                      text=[
                          f"{idxs.name}: {index}<br>shap: {shap}"
                          for index, shap in zip(
                              idxs[(X_col == cat)],
                              shap_values[X_col == cat],
                          )
                      ],
                      marker=dict(size=7, opacity=0.6, color="blue"),
                  ),
                  row=1,
                  col=col + 1,
              )
          if (
              highlight_index is not None
              and (points or X_color_col is not None)
              and X_col[highlight_idx] == cat
          ):
              fig.add_trace(
                  go.Scattergl(
                      x=[0],
                      y=[shap_values[highlight_idx]],
                      mode="markers",
                      marker=dict(
                          color="LightSkyBlue",
                          size=25,
                          opacity=0.5,
                          line=dict(color="MediumPurple", width=4),
                      ),
                      name=f"{idxs.name} {highlight_name}",
                      text=f"{idxs.name} {highlight_name}",
                      hoverinfo="text",
                      showlegend=False,
                  ),
                  row=1,
                  col=col + 1,
              )
  
      if points or X_color_col is not None:
          for i in range(n_cats):
              fig.update_xaxes(
                  showgrid=False,
                  zeroline=False,
                  visible=False,
                  row=1,
                  col=2 + i * 2,
              )
              fig.update_yaxes(
                  showgrid=False, zeroline=False, row=1, col=2 + i * 2
              )
  
      fig.update_layout(
          yaxis=dict(
              title=f"SHAP value ({units})"
              if units != ""
              else "SHAP value"
          ),
          hovermode="closest",
      )
  
      if X_color_col is not None and interaction:
          fig.update_layout(
              title=f"Interaction plot for {X_col.name} and {X_color_col.name}"
          )
      elif X_color_col is not None:
          fig.update_layout(
              title=f"Shap values for {X_col.name}<br>(colored by {X_color_col.name})"
          )
      else:
          fig.update_layout(title=f"Shap values for {X_col.name}")
      fig.update_layout(margin=dict(t=40, b=40, l=40, r=40))
      return fig
  
imports: import warnings
  import numpy
  import pandas
  from pandas.api.types import is_numeric_dtype
  import plotly.graph_objs
  from plotly.subplots import make_subplots
  from sklearn.metrics import classification_report, confusion_matrix, precision_recall_curve, roc_curve, roc_auc_score, average_precision_score
  from .explainer_methods import matching_cols, safe_isinstance
label_desc: Use image node_id 8 for calling a global function with example usage: plotly_shap_violin_plot(X_col, shap_values, X_color_col, points, interaction, units, highlight_index, idxs, round, cats_order, max_cat_colors) and returns: fig