code: def decision_boundaries_bivar(
      model,
      X: np.ndarray,
      y: np.ndarray,
      ntiles=50,
      tile_fraction=0.9,
      binary_threshold=0.5,
      show=[
          "instances",
          "boundaries",
          "probabilities",
          "misclassified",
          "legend",
      ],
      feature_names=None,
      target_name=None,
      class_names=None,
      markers=None,
      boundary_marker="o",
      boundary_markersize=0.8,
      fontsize=9,
      fontname="Arial",
      dot_w=25,
      colors: dict = None,
      ranges=None,
      figsize=None,
      ax=None,
  ) -> None:
      """
      See comment and parameter descriptions for decision_boundaries() above.
      """
      if isinstance(X, pd.DataFrame):
          X = X.values
      if isinstance(y, pd.Series):
          y = y.values
  
      if (
          len(X.shape) == 1
          or (len(X.shape) == 2 and X.shape[1] != 2)
          or len(X.shape) > 2
      ):
          raise ValueError(f"Expecting 2D data not {X.shape}")
  
      if ax is None:
          if figsize:
              fig, ax = plt.subplots(figsize=figsize)
          else:
              fig, ax = plt.subplots()
  
      # Created grid over the range of x1 and x2 variables, get probabilities, predictions
      (
          grid_points,
          grid_proba,
          grid_pred_as_matrix,
          w,
          x_,
          class_X,
          class_values,
      ) = _compute_tiling(
          model,
          X,
          y,
          binary_threshold,
          ntiles,
          tile_fraction,
          ranges=ranges,
      )
  
      x_proba = _predict_proba(model, X)
      if len(np.unique(y)) == 2:  # is k=2 binary?
          X_pred = np.where(x_proba[:, 1] >= binary_threshold, 1, 0)
      else:
          X_pred = np.argmax(
              x_proba, axis=1
          )  # TODO: assumes classes are 0..k-1
      class_X_pred = [X_pred[y == cl] for cl in class_values]
  
      if markers is None:
          markers = ["o"] * len(class_X)
  
      colors = adjust_colors(colors)
  
      class_values = np.unique(y)  # returns sorted
  
      # Get class to color map for probabilities and predictions
      color_map, grid_pred_colors, grid_proba_colors = _get_grid_colors(
          grid_proba, grid_pred_as_matrix, class_values, colors
      )
  
      # Draw probabilities or class prediction grid
      facecolors = (
          grid_proba_colors
          if "probabilities" in show
          else grid_pred_colors
      )
      _draw_tiles(
          ax, grid_points, facecolors, colors["tile_alpha"], x_, w
      )
  
      # Get grid with class predictions with coordinates (x,y)
      # e.g., y_pred[0,0] is lower left pixel and y_pred[5,5] is top-right pixel
      # for npoints=5
      grid_pred_as_matrix = grid_pred_as_matrix.reshape(ntiles, ntiles)
  
      if "boundaries" in show:
          _draw_boundary_edges(
              ax,
              grid_points,
              grid_pred_as_matrix,
              boundary_marker,
              boundary_markersize,
              colors,
              w,
              x_,
          )
  
      # Draw the X instances circles
      if "instances" in show:
          for i, x_ in enumerate(class_X):
              if "misclassified" in show:
                  # Show correctly classified markers
                  good_x = x_[class_X_pred[i] == class_values[i], :]
                  ax.scatter(
                      good_x[:, 0],
                      good_x[:, 1],
                      s=dot_w,
                      c=color_map[i],
                      marker=markers[i],
                      alpha=colors["scatter_marker_alpha"],
                      edgecolors=colors["scatter_edge"],
                      lw=0.5,
                  )
                  # Show misclassified markers (can't have alpha per marker so do in 2 calls)
                  bad_x = x_[class_X_pred[i] != class_values[i], :]
                  ax.scatter(
                      bad_x[:, 0],
                      bad_x[:, 1],
                      s=dot_w,
                      c=color_map[i],
                      marker=markers[i],
                      alpha=1.0,
                      edgecolors=colors["warning"],
                      lw=0.5,
                  )
              else:
                  ax.scatter(
                      x_[:, 0],
                      x_[:, 1],
                      s=dot_w,
                      c=color_map[i],
                      marker=markers[i],
                      alpha=colors["scatter_marker_alpha"],
                      edgecolors=colors["scatter_edge"],
                      lw=0.5,
                  )
  
      _format_axes(
          ax,
          feature_names[0] if feature_names is not None else None,
          feature_names[1] if feature_names is not None else None,
          colors,
          fontsize,
          fontname,
      )
  
      if "legend" in show:
          class_names = utils._normalize_class_names(
              class_names, nclasses=len(class_values)
          )
          add_classifier_legend(
              ax,
              class_names,
              class_values,
              color_map,
              target_name,
              colors,
              fontsize=fontsize,
              fontname=fontname,
          )
  
file_path: dtreeviz/dtreeviz/classifiers.py
imports: from typing import Tuple
  import matplotlib.pyplot
  import numpy
  import pandas
  from PIL import ImageColor
  from colour import Color
  from matplotlib import patches
  from matplotlib.collections import PatchCollection
  from dtreeviz import utils
  from dtreeviz.colors import adjust_colors
  from dtreeviz.utils import add_classifier_legend, _format_axes
label_desc: Use image node_id 2 for calling a global function with example usage: decision_boundaries_bivar(model, X, y, ntiles, tile_fraction, binary_threshold, show, feature_names, target_name, class_names, markers, boundary_marker, boundary_markersize, fontsize, fontname, dot_w, colors, ranges, figsize, ax) without return types