code: def plot_decision_regions(
      X,
      y,
      clf,
      feature_index=None,
      filler_feature_values=None,
      filler_feature_ranges=None,
      ax=None,
      X_highlight=None,
      zoom_factor=1.0,
      legend=1,
      hide_spines=True,
      markers="s^oxv<>",
      colors=(
          "#1f77b4,#ff7f0e,#3ca02c,#d62728,"
          "#9467bd,#8c564b,#e377c2,"
          "#7f7f7f,#bcbd22,#17becf"
      ),
      scatter_kwargs=None,
      contourf_kwargs=None,
      contour_kwargs=None,
      scatter_highlight_kwargs=None,
      n_jobs=None,
  ):
      """Plot decision regions of a classifier.
  
      Please note that this functions assumes that class labels are
      labeled consecutively, e.g,. 0, 1, 2, 3, 4, and 5. If you have class
      labels with integer labels > 4, you may want to provide additional colors
      and/or markers as `colors` and `markers` arguments.
      See https://matplotlib.org/examples/color/named_colors.html for more
      information.
  
      Parameters
      ----------
      X : array-like, shape = [n_samples, n_features]
          Feature Matrix.
  
      y : array-like, shape = [n_samples]
          True class labels.
  
      clf : Classifier object.
          Must have a .predict method.
  
      feature_index : array-like (default: (0,) for 1D, (0, 1) otherwise)
          Feature indices to use for plotting. The first index in
          `feature_index` will be on the x-axis, the second index will be
          on the y-axis.
  
      filler_feature_values : dict (default: None)
          Only needed for number features > 2. Dictionary of feature
          index-value pairs for the features not being plotted.
  
      filler_feature_ranges : dict (default: None)
          Only needed for number features > 2. Dictionary of feature
          index-value pairs for the features not being plotted. Will use the
          ranges provided to select training samples for plotting.
  
      ax : matplotlib.axes.Axes (default: None)
          An existing matplotlib Axes. Creates
          one if ax=None.
  
      X_highlight : array-like, shape = [n_samples, n_features] (default: None)
          An array with data points that are used to highlight samples in `X`.
  
      zoom_factor : float (default: 1.0)
          Controls the scale of the x- and y-axis of the decision plot.
  
      hide_spines : bool (default: True)
          Hide axis spines if True.
  
      legend : int (default: 1)
          Integer to specify the legend location.
          No legend if legend is 0.
  
      markers : str (default: 's^oxv<>')
          Scatterplot markers.
  
      colors : str (default: 'red,blue,limegreen,gray,cyan')
          Comma separated list of colors.
  
      scatter_kwargs : dict (default: None)
          Keyword arguments for underlying matplotlib scatter function.
  
      contourf_kwargs : dict (default: None)
          Keyword arguments for underlying matplotlib contourf function.
  
      contour_kwargs : dict (default: None)
          Keyword arguments for underlying matplotlib contour function
          (which draws the lines between decision regions).
  
      scatter_highlight_kwargs : dict (default: None)
          Keyword arguments for underlying matplotlib scatter function.
  
      n_jobs : int or None, optional (default=None)
          The number of CPUs to use to do the computation using Python's
          multiprocessing library.
          `None` means 1.
          `-1` means using all processors. New in v0.22.0.
  
      Returns
      ---------
      ax : matplotlib.axes.Axes object
  
      Examples
      -----------
      For usage examples, please see
      https://rasbt.github.io/mlxtend/user_guide/plotting/plot_decision_regions/
  
      """
  
      check_Xy(X, y, y_int=True)  # Validate X and y arrays
      dim = X.shape[1]
  
      if n_jobs is None:
          n_jobs = 1
  
      if ax is None:
          ax = plt.gca()
  
      plot_testdata = True
      if not isinstance(X_highlight, np.ndarray):
          if X_highlight is not None:
              raise ValueError(
                  "X_highlight must be a NumPy array or None"
              )
          else:
              plot_testdata = False
      elif len(X_highlight.shape) < 2:
          raise ValueError("X_highlight must be a 2D array")
  
      if feature_index is not None:
          # Unpack and validate the feature_index values
          if dim == 1:
              raise ValueError(
                  "feature_index requires more than one training feature"
              )
          try:
              x_index, y_index = feature_index
          except ValueError:
              raise ValueError(
                  "Unable to unpack feature_index. Make sure feature_index "
                  "only has two dimensions."
              )
          try:
              X[:, x_index], X[:, y_index]
          except IndexError:
              raise IndexError(
                  "feature_index values out of range. X.shape is {}, but "
                  "feature_index is {}".format(X.shape, feature_index)
              )
      else:
          feature_index = (0, 1)
          x_index, y_index = feature_index
  
      # Extra input validation for higher number of training features
      if dim > 2:
          if filler_feature_values is None:
              raise ValueError(
                  "Filler values must be provided when "
                  "X has more than 2 training features."
              )
  
          if filler_feature_ranges is not None:
              if not set(filler_feature_values) == set(
                  filler_feature_ranges
              ):
                  raise ValueError(
                      "filler_feature_values and filler_feature_ranges must "
                      "have the same keys"
                  )
  
          # Check that all columns in X are accounted for
          column_check = np.zeros(dim, dtype=bool)
          for idx in filler_feature_values:
              column_check[idx] = True
          for idx in feature_index:
              column_check[idx] = True
          if not all(column_check):
              missing_cols = np.argwhere(~column_check).flatten()
              raise ValueError(
                  "Column(s) {} need to be accounted for in either "
                  "feature_index or filler_feature_values".format(
                      missing_cols
                  )
              )
  
      # Check that the n_jobs isn't higher than the available CPU cores
      if n_jobs > mp.cpu_count():
          raise ValueError(
              "Number of defined CPU cores is more than the available resources {} ".format(
                  mp.cpu_count()
              )
          )
  
      marker_gen = cycle(list(markers))
  
      n_classes = np.unique(y).shape[0]
      colors = colors.split(",")
      colors_gen = cycle(colors)
      colors = [next(colors_gen) for c in range(n_classes)]
  
      # Get minimum and maximum
      x_min, x_max = (
          X[:, x_index].min() - 1.0 / zoom_factor,
          X[:, x_index].max() + 1.0 / zoom_factor,
      )
      if dim == 1:
          y_min, y_max = -1, 1
      else:
          y_min, y_max = (
              X[:, y_index].min() - 1.0 / zoom_factor,
              X[:, y_index].max() + 1.0 / zoom_factor,
          )
  
      xnum, ynum = plt.gcf().dpi * plt.gcf().get_size_inches()
      xnum, ynum = floor(xnum), ceil(ynum)
      xx, yy = np.meshgrid(
          np.linspace(x_min, x_max, num=xnum),
          np.linspace(y_min, y_max, num=ynum),
      )
  
      if dim == 1:
          X_predict = np.array([xx.ravel()]).T
      else:
          X_grid = np.array([xx.ravel(), yy.ravel()]).T
          X_predict = np.zeros((X_grid.shape[0], dim))
          X_predict[:, x_index] = X_grid[:, 0]
          X_predict[:, y_index] = X_grid[:, 1]
          if dim > 2:
              for feature_idx in filler_feature_values:
                  X_predict[:, feature_idx] = filler_feature_values[
                      feature_idx
                  ]
  
      if n_jobs == 1:
          Z = clf.predict(X_predict.astype(X.dtype))
          Z = Z.reshape(xx.shape)
      else:
          if n_jobs == -1:
              cpus = mp.cpu_count()
          else:
              cpus = n_jobs
          pool = mp.Pool(cpus)
          partQuant = len(X_predict) / cpus
          partitions = []
          for n in range(cpus - 1):
              start, end = np.floor(partQuant * n).astype(
                  int
              ), np.floor(partQuant * (n + 1)).astype(int)
              partitions.append(X_predict[start:end])
          partitions.append(X_predict[end:])
          xtype = X.dtype
          Z = pool.starmap(
              parallel, [(x, clf, xtype) for x in partitions]
          )
          pool.close()
          Z = np.concatenate(Z)
          Z = Z.reshape(xx.shape)
  
      # Plot decisoin region
      # Make sure contourf_kwargs has backwards compatible defaults
      contourf_kwargs_default = {"alpha": 0.45, "antialiased": True}
      contourf_kwargs = format_kwarg_dictionaries(
          default_kwargs=contourf_kwargs_default,
          user_kwargs=contourf_kwargs,
          protected_keys=["colors", "levels"],
      )
      cset = ax.contourf(
          xx,
          yy,
          Z,
          colors=colors,
          levels=np.arange(Z.max() + 2) - 0.5,
          **contourf_kwargs,
      )
  
      contour_kwargs_default = {
          "linewidths": 0.5,
          "colors": "k",
          "antialiased": True,
      }
      contour_kwargs = format_kwarg_dictionaries(
          default_kwargs=contour_kwargs_default,
          user_kwargs=contour_kwargs,
          protected_keys=[],
      )
      ax.contour(xx, yy, Z, cset.levels, **contour_kwargs)
  
      ax.axis([xx.min(), xx.max(), yy.min(), yy.max()])
  
      # Scatter training data samples
      # Make sure scatter_kwargs has backwards compatible defaults
      scatter_kwargs_default = {"alpha": 0.8, "edgecolor": "black"}
      scatter_kwargs = format_kwarg_dictionaries(
          default_kwargs=scatter_kwargs_default,
          user_kwargs=scatter_kwargs,
          protected_keys=["c", "marker", "label"],
      )
      for idx, c in enumerate(np.unique(y)):
          if dim == 1:
              y_data = [0 for i in X[y == c]]
              x_data = X[y == c]
          elif dim == 2:
              y_data = X[y == c, y_index]
              x_data = X[y == c, x_index]
          elif dim > 2 and filler_feature_ranges is not None:
              class_mask = y == c
              feature_range_mask = get_feature_range_mask(
                  X,
                  filler_feature_values=filler_feature_values,
                  filler_feature_ranges=filler_feature_ranges,
              )
              y_data = X[class_mask & feature_range_mask, y_index]
              x_data = X[class_mask & feature_range_mask, x_index]
          else:
              continue
  
          ax.scatter(
              x=x_data,
              y=y_data,
              c=colors[idx],
              marker=next(marker_gen),
              label=c,
              **scatter_kwargs,
          )
  
      if hide_spines:
          ax.spines["right"].set_visible(False)
          ax.spines["top"].set_visible(False)
          ax.spines["left"].set_visible(False)
          ax.spines["bottom"].set_visible(False)
      ax.yaxis.set_ticks_position("left")
      ax.xaxis.set_ticks_position("bottom")
      if dim == 1:
          ax.axes.get_yaxis().set_ticks([])
  
      if plot_testdata:
          if dim == 1:
              x_data = X_highlight
              y_data = [0 for i in X_highlight]
          elif dim == 2:
              x_data = X_highlight[:, x_index]
              y_data = X_highlight[:, y_index]
          else:
              feature_range_mask = get_feature_range_mask(
                  X_highlight,
                  filler_feature_values=filler_feature_values,
                  filler_feature_ranges=filler_feature_ranges,
              )
              y_data = X_highlight[feature_range_mask, y_index]
              x_data = X_highlight[feature_range_mask, x_index]
  
          # Make sure scatter_highlight_kwargs backwards compatible defaults
          scatter_highlight_defaults = {
              "c": "none",
              "edgecolor": "black",
              "alpha": 1.0,
              "linewidths": 1,
              "marker": "o",
              "s": 80,
          }
          scatter_highlight_kwargs = format_kwarg_dictionaries(
              default_kwargs=scatter_highlight_defaults,
              user_kwargs=scatter_highlight_kwargs,
          )
          ax.scatter(x_data, y_data, **scatter_highlight_kwargs)
  
      if legend:
          if dim > 2 and filler_feature_ranges is None:
              pass
          else:
              handles, labels = ax.get_legend_handles_labels()
              ax.legend(
                  handles,
                  labels,
                  framealpha=0.3,
                  scatterpoints=1,
                  loc=legend,
              )
  
      return ax
  
file_path: mlxtend/mlxtend/plotting/decision_regions.py
imports: import multiprocessing
  from itertools import cycle
  from math import ceil, floor
  import matplotlib.pyplot
  import numpy
  from mlxtend.utils import check_Xy, format_kwarg_dictionaries
label_desc: Use image node_id 3 for calling a global function with example usage: plot_decision_regions(X, y, clf, feature_index, filler_feature_values, filler_feature_ranges, ax, X_highlight, zoom_factor, legend, hide_spines, markers, colors, scatter_kwargs, contourf_kwargs, contour_kwargs, scatter_highlight_kwargs, n_jobs) and returns: ax