class_docstr: This object provides the primary interface to the functionality of this library. You can think of it as
  an adaptor that adapts the various decision-tree based libraries for use with dtreeviz. In implementation,
  however, this object encapsulates the key functionality and API but delegates tree model adaptation
  from sklearn etc... to dtreeviz.models.ShadowDecTree subclasses.
class_docstr_tok: ["This","object","provides","the","primary","interface","to","the","functionality","of","this","library",".","You","can","think","of","it","as","an","adaptor","that","adapts","the","various","decision-tree","based","libraries","for","use","with","dtreeviz",".","In","implementation",",","however",",","this","object","encapsulates","the","key","functionality","and","API","but","delegates","tree","model","adaptation","from","sklearn","etc","...","to","dtreeviz.models.ShadowDecTree","subclasses","."]
docstr: Based on a decision tree regressor or classifier, create and return a tree visualization using the
  graphviz (DOT) language.
  
  Usage example :
  viz_model = dtreeviz.model(tree_model, X_train=dataset[features], y_train=dataset[target],
                             feature_names=features, target_name=target, class_names=[0, 1])
  viz_model.view()
  
  :param precision: When displaying floating-point numbers, how many digits to display
                    after the decimal point. Default is 2.
  :param orientation:  Is the tree top down, "TD", or left to right, "LR"?
  :param instance_orientation: table orientation (TD, LR) for showing feature prediction's values.
  :param leaftype: leaf plot type ('pie', 'barh')
  :param show_root_edge_labels: Include < and >= on the edges emanating from the root?
  :param show_node_labels: Add "Node id" to top of each node in graph for educational purposes
  :param show_just_path: If True, it shows only the sample(X) prediction path
  :param fancy:
  :param histtype: [For classifiers] Either 'bar' or 'barstacked' to indicate
                   histogram type. We find that 'barstacked' looks great up to about.
                   four classes.
  :param highlight_path: A list of node IDs to highlight, default is [].
                         Useful for emphasizing node(s) in tree for discussion.
                         If X argument given then this is ignored.
  :type highlight_path: List[int]
  :param x: Instance to run down the tree; derived path to highlight from this vector.
            Show feature vector with labels underneath leaf reached. highlight_path
            is ignored if X is not None.
  :type x: np.ndarray
  :param label_fontsize: Size of the label font
  :param ticks_fontsize: Size of the tick font
  :param fontname: Font which is used for labels and text
  :param max_X_features_LR: If len(X) exceeds this limit for LR layout,
                          display only those features
                         used to guide X vector down tree. Helps when len(X) is large.
                         Default is 10.
  :param max_X_features_TD: If len(X) exceeds this limit for TD layout,
                          display only those features
                         used to guide X vector down tree. Helps when len(X) is large.
                         Default is 25.
  :param depth_range_to_display: range of depth levels to be displayed. The range values are inclusive
  :param title: An optional title placed at the top of the tree.
  :param title_fontsize: Size of the text for the title.
  :param colors: dict A custom set of colors for visualisations
  :param scale: Default is 1.0. Scale the width, height of the overall SVG preserving aspect ratio
  :return: A DTreeVizRender object containing string in graphviz DOT language that
          describes the decision tree.
code: def view(
      self,
      precision: int = 2,
      orientation: ("TD", "LR") = "TD",
      instance_orientation: ("TD", "LR") = "LR",
      show_root_edge_labels: bool = True,
      show_node_labels: bool = False,
      show_just_path: bool = False,
      fancy: bool = True,
      histtype: ("bar", "barstacked", "strip") = "barstacked",
      leaftype: ("pie", "barh") = "pie",
      highlight_path: List[int] = [],
      x: np.ndarray = None,
      max_X_features_LR: int = 10,
      max_X_features_TD: int = 20,
      depth_range_to_display: tuple = None,
      label_fontsize: int = 12,
      ticks_fontsize: int = 8,
      fontname: str = "Arial",
      title: str = None,
      title_fontsize: int = 10,
      colors: dict = None,
      scale=1.0,
  ) -> DTreeVizRender:
      """
      Based on a decision tree regressor or classifier, create and return a tree visualization using the
      graphviz (DOT) language.
  
      Usage example :
      viz_model = dtreeviz.model(tree_model, X_train=dataset[features], y_train=dataset[target],
                                 feature_names=features, target_name=target, class_names=[0, 1])
      viz_model.view()
  
      :param precision: When displaying floating-point numbers, how many digits to display
                        after the decimal point. Default is 2.
      :param orientation:  Is the tree top down, "TD", or left to right, "LR"?
      :param instance_orientation: table orientation (TD, LR) for showing feature prediction's values.
      :param leaftype: leaf plot type ('pie', 'barh')
      :param show_root_edge_labels: Include < and >= on the edges emanating from the root?
      :param show_node_labels: Add "Node id" to top of each node in graph for educational purposes
      :param show_just_path: If True, it shows only the sample(X) prediction path
      :param fancy:
      :param histtype: [For classifiers] Either 'bar' or 'barstacked' to indicate
                       histogram type. We find that 'barstacked' looks great up to about.
                       four classes.
      :param highlight_path: A list of node IDs to highlight, default is [].
                             Useful for emphasizing node(s) in tree for discussion.
                             If X argument given then this is ignored.
      :type highlight_path: List[int]
      :param x: Instance to run down the tree; derived path to highlight from this vector.
                Show feature vector with labels underneath leaf reached. highlight_path
                is ignored if X is not None.
      :type x: np.ndarray
      :param label_fontsize: Size of the label font
      :param ticks_fontsize: Size of the tick font
      :param fontname: Font which is used for labels and text
      :param max_X_features_LR: If len(X) exceeds this limit for LR layout,
                              display only those features
                             used to guide X vector down tree. Helps when len(X) is large.
                             Default is 10.
      :param max_X_features_TD: If len(X) exceeds this limit for TD layout,
                              display only those features
                             used to guide X vector down tree. Helps when len(X) is large.
                             Default is 25.
      :param depth_range_to_display: range of depth levels to be displayed. The range values are inclusive
      :param title: An optional title placed at the top of the tree.
      :param title_fontsize: Size of the text for the title.
      :param colors: dict A custom set of colors for visualisations
      :param scale: Default is 1.0. Scale the width, height of the overall SVG preserving aspect ratio
      :return: A DTreeVizRender object containing string in graphviz DOT language that
              describes the decision tree.
      """
  
      def node_name(node: ShadowDecTreeNode) -> str:
          return f"node{node.id}"
  
      def split_node(name, node_name, split):
          if fancy:
              filepath = os.path.join(
                  tmp, f"node{node.id}_{os.getpid()}.svg"
              )
              labelgraph = node_label(node) if show_node_labels else ""
              html = f"""<table border="0">
                      {labelgraph}
                      <tr>
                              <td><img src="{filepath}"/></td>
                      </tr>
                      </table>"""
          else:
              html = f"""<font face="{fontname}" color="{colors["text"]}" point-size="12">{name}@{split}</font>"""
          if node.id in highlight_path:
              gr_node = f'{node_name} [margin="0" shape=box penwidth=".5" color="{colors["highlight"]}" style="dashed" label=<{html}>]'
          else:
              gr_node = (
                  f'{node_name} [margin="0" shape=none label=<{html}>]'
              )
          return gr_node
  
      def regr_leaf_node(node, label_fontsize: int = 12):
          # always generate fancy regr leaves for now but shrink a bit for nonfancy.
          labelgraph = node_label(node) if show_node_labels else ""
          filepath = os.path.join(
              tmp, f"leaf{node.id}_{os.getpid()}.svg"
          )
          html = f"""<table border="0">
                  {labelgraph}
                  <tr>
                          <td><img src="{filepath}"/></td>
                  </tr>
                  </table>"""
          if node.id in highlight_path:
              return f'leaf{node.id} [margin="0" shape=box penwidth=".5" color="{colors["highlight"]}" style="dashed" label=<{html}>]'
          else:
              return f'leaf{node.id} [margin="0" shape=box penwidth="0" color="{colors["text"]}" label=<{html}>]'
  
      def class_leaf_node(node, label_fontsize: int = 12):
          labelgraph = node_label(node) if show_node_labels else ""
          filepath = os.path.join(
              tmp, f"leaf{node.id}_{os.getpid()}.svg"
          )
          html = f"""<table border="0" CELLBORDER="0">
                  {labelgraph}
                  <tr>
                          <td><img src="{filepath}"/></td>
                  </tr>
                  </table>"""
          if node.id in highlight_path:
              return f'leaf{node.id} [margin="0" shape=box penwidth=".5" color="{colors["highlight"]}" style="dashed" label=<{html}>]'
          else:
              return f'leaf{node.id} [margin="0" shape=box penwidth="0" color="{colors["text"]}" label=<{html}>]'
  
      def node_label(node):
          return f'<tr><td CELLPADDING="0" CELLSPACING="0"><font face="{fontname}" color="{colors["node_label"]}" point-size="14"><i>Node {node.id}</i></font></td></tr>'
  
      def class_legend_html():
          filepath = os.path.join(tmp, f"legend_{os.getpid()}.svg")
          return f"""
                  <table border="0" cellspacing="0" cellpadding="0">
                      <tr>
                          <td border="0" cellspacing="0" cellpadding="0"><img src="{filepath}"/></td>
                      </tr>
                  </table>
                  """
  
      def class_legend_gr():
          if not self.shadow_tree.is_classifier():
              return ""
          return f"""
                      subgraph cluster_legend {{
                          style=invis;
                          legend [penwidth="0" margin="0" shape=box margin="0.03" width=.1, height=.1 label=<
                          {class_legend_html()}
                          >]
                      }}
                      """
  
      def instance_html(path, instance_fontsize: int = 11):
          headers = []
          features_used = [
              node.feature() for node in path[:-1]
          ]  # don't include leaf
          display_X = x
          display_feature_names = self.shadow_tree.feature_names
          highlight_feature_indexes = features_used
          if (orientation == "TD" and len(x) > max_X_features_TD) or (
              orientation == "LR" and len(x) > max_X_features_LR
          ):
              # squash all features down to just those used
              display_X = [x[i] for i in features_used] + ["..."]
              display_feature_names = [
                  node.feature_name() for node in path[:-1]
              ] + ["..."]
              highlight_feature_indexes = range(0, len(features_used))
  
          for i, name in enumerate(display_feature_names):
              if i in highlight_feature_indexes:
                  color = colors["highlight"]
              else:
                  color = colors["text"]
              headers.append(
                  f'<td cellpadding="1" align="right" bgcolor="white">'
                  f'<font face="{fontname}" color="{color}" point-size="{instance_fontsize}">'
                  f"{name}"
                  "</font>"
                  "</td>"
              )
  
          values = []
          for i, v in enumerate(display_X):
              if i in highlight_feature_indexes:
                  color = colors["highlight"]
              else:
                  color = colors["text"]
              if isinstance(v, int) or isinstance(v, str):
                  disp_v = v
              else:
                  disp_v = myround(v, precision)
              values.append(
                  f'<td cellpadding="1" align="right" bgcolor="white">'
                  f'<font face="{fontname}" color="{color}" point-size="{instance_fontsize}">{disp_v}</font>'
                  "</td>"
              )
  
          if instance_orientation == "TD":
              html_output = """<table border="0" cellspacing="0" cellpadding="0">"""
              for header, value in zip(headers, values):
                  html_output += f"<tr> {header} {value} </tr>"
              html_output += "</table>"
              return html_output
          else:
              return f"""
                          <table border="0" cellspacing="0" cellpadding="0">
                          <tr>
                              {''.join(headers)}
                          </tr>
                          <tr>
                              {''.join(values)}
                          </tr>
                          </table>
                          """
  
      def instance_gr():
          if x is None:
              return ""
          path = self.shadow_tree.predict_path(x)
          leaf = f"leaf{path[-1].id}"
          if self.shadow_tree.is_classifier():
              edge_label = (
                  f"  Prediction<br/> {path[-1].prediction_name()}"
              )
          else:
              edge_label = f"  Prediction<br/> {myround(path[-1].prediction(), precision)}"
          return f"""
                      subgraph cluster_instance {{
                          style=invis;
                          X_y [penwidth="0.3" margin="0" shape=box margin="0.03" width=.1, height=.1 label=<
                          {instance_html(path)}
                          >]
                      }}
                      {leaf} -> X_y [dir=back; penwidth="1.2" color="{colors['highlight']}" label=<<font face="{fontname}" color="{colors['leaf_label']}" point-size="{11}">{edge_label}</font>>]
                      """
  
      def get_internal_nodes():
          if show_just_path and x is not None:
              _internal = []
              for _node in self.shadow_tree.internal:
                  if _node.id in highlight_path:
                      _internal.append(_node)
              return _internal
          else:
              return self.shadow_tree.internal
  
      def get_leaves():
          if show_just_path and x is not None:
              _leaves = []
              for _node in self.shadow_tree.leaves:
                  if _node.id in highlight_path:
                      _leaves.append(_node)
                      break
              return _leaves
          else:
              return self.shadow_tree.leaves
  
      n_classes = self.shadow_tree.nclasses()
      colors = adjust_colors(colors, n_classes)
  
      if orientation == "TD":
          ranksep = ".2"
          nodesep = "0.1"
      else:
          if fancy:
              ranksep = ".22"
              nodesep = "0.1"
          else:
              ranksep = ".05"
              nodesep = "0.09"
  
      tmp = tempfile.gettempdir()
      if x is not None:
          path = self.shadow_tree.predict_path(x)
          highlight_path = [n.id for n in path]
  
      color_values = colors["classes"][n_classes]
  
      # Fix the mapping from target value to color for entire tree
      if self.shadow_tree.is_classifier():
          class_values = self.shadow_tree.classes()
          if np.max(class_values) >= n_classes:
              raise ValueError(
                  f"Target label values (for now) must be 0..{n_classes-1} for n={n_classes} labels"
              )
          color_map = {
              v: color_values[i] for i, v in enumerate(class_values)
          }
          _draw_legend(
              self.shadow_tree,
              self.shadow_tree.target_name,
              os.path.join(tmp, f"legend_{os.getpid()}.svg"),
              colors=colors,
              fontname=fontname,
          )
  
      X_train = self.shadow_tree.X_train
      y_train = self.shadow_tree.y_train
      if isinstance(X_train, pd.DataFrame):
          X_train = X_train.values
      if isinstance(y_train, pd.Series):
          y_train = y_train.values
      if y_train.dtype == np.dtype(object):
          try:
              y_train = y_train.astype("float")
          except ValueError as e:
              raise ValueError(
                  "y_train needs to consist only of numerical values. {}".format(
                      e
                  )
              )
          if len(y_train.shape) != 1:
              raise ValueError(
                  "y_train must a one-dimensional list or Pandas Series, got: {}".format(
                      y_train.shape
                  )
              )
  
      y_range = (
          min(y_train) * 1.03,
          max(y_train) * 1.03,
      )  # same y axis for all
  
      # Find max height (count) for any bar in any node
      if self.shadow_tree.is_classifier():
          nbins = _get_num_bins(histtype, n_classes)
          node_heights = self.shadow_tree.get_split_node_heights(
              X_train, y_train, nbins=nbins
          )
  
      internal = []
      for node in get_internal_nodes():
          if depth_range_to_display is not None:
              if node.level not in range(
                  depth_range_to_display[0],
                  depth_range_to_display[1] + 1,
              ):
                  continue
          if fancy:
              if self.shadow_tree.is_classifier():
                  _class_split_viz(
                      node,
                      X_train,
                      y_train,
                      filename=os.path.join(
                          tmp, f"node{node.id}_{os.getpid()}.svg"
                      ),
                      precision=precision,
                      colors={**color_map, **colors},
                      histtype=histtype,
                      node_heights=node_heights,
                      X=x,
                      ticks_fontsize=ticks_fontsize,
                      label_fontsize=label_fontsize,
                      fontname=fontname,
                      highlight_node=node.id in highlight_path,
                  )
              else:
                  _regr_split_viz(
                      node,
                      X_train,
                      y_train,
                      filename=os.path.join(
                          tmp, f"node{node.id}_{os.getpid()}.svg"
                      ),
                      target_name=self.shadow_tree.target_name,
                      y_range=y_range,
                      X=x,
                      ticks_fontsize=ticks_fontsize,
                      label_fontsize=label_fontsize,
                      fontname=fontname,
                      highlight_node=node.id in highlight_path,
                      colors=colors,
                  )
  
          nname = node_name(node)
          if not node.is_categorical_split():
              gr_node = split_node(
                  node.feature_name(),
                  nname,
                  split=myround(node.split(), precision),
              )
          else:
              gr_node = split_node(
                  node.feature_name(), nname, split=node.split()[0]
              )
          internal.append(gr_node)
  
      leaves = []
      for node in get_leaves():
          if depth_range_to_display is not None:
              if node.level not in range(
                  depth_range_to_display[0],
                  depth_range_to_display[1] + 1,
              ):
                  continue
          if self.shadow_tree.is_classifier():
              _class_leaf_viz(
                  node,
                  colors=color_values,
                  filename=os.path.join(
                      tmp, f"leaf{node.id}_{os.getpid()}.svg"
                  ),
                  graph_colors=colors,
                  fontname=fontname,
                  leaftype=leaftype,
              )
              leaves.append(class_leaf_node(node))
          else:
              # for now, always gen leaf
              _regr_leaf_viz(
                  node,
                  y_train,
                  target_name=self.shadow_tree.target_name,
                  filename=os.path.join(
                      tmp, f"leaf{node.id}_{os.getpid()}.svg"
                  ),
                  y_range=y_range,
                  precision=precision,
                  ticks_fontsize=ticks_fontsize,
                  label_fontsize=label_fontsize,
                  fontname=fontname,
                  colors=colors,
              )
              leaves.append(regr_leaf_node(node))
  
      if show_just_path:
          show_root_edge_labels = False
  
      # TODO do we need show_edge_labels ?
      show_edge_labels = False
      all_llabel = "  &lt;" if show_edge_labels else ""
      all_rlabel = "  &ge;" if show_edge_labels else ""
      root_llabel = (
          f"  {self.shadow_tree.get_root_edge_labels()[0]}"
          if show_root_edge_labels
          else ""
      )
      root_rlabel = (
          f"  {self.shadow_tree.get_root_edge_labels()[1]}"
          if show_root_edge_labels
          else ""
      )
  
      edges = []
      # non leaf edges with > and <=
      for node in get_internal_nodes():
          if depth_range_to_display is not None:
              if node.level not in range(
                  depth_range_to_display[0], depth_range_to_display[1]
              ):
                  continue
          nname = node_name(node)
          if node.left.isleaf():
              left_node_name = "leaf%d" % node.left.id
          else:
              left_node_name = node_name(node.left)
          if node.right.isleaf():
              right_node_name = "leaf%d" % node.right.id
          else:
              right_node_name = node_name(node.right)
  
          if node == self.shadow_tree.root:
              llabel = root_llabel
              rlabel = root_rlabel
          else:
              llabel = all_llabel
              rlabel = all_rlabel
  
          if (
              node.is_categorical_split()
              and not self.shadow_tree.is_classifier()
          ):
              lcolor, rcolor = (
                  colors["categorical_split_left"],
                  colors["categorical_split_right"],
              )
          else:
              lcolor = colors.get("larrow", colors["arrow"])
              rcolor = colors.get("rarrow", colors["arrow"])
  
          lpw = rpw = "0.3"
          if node.left.id in highlight_path:
              lcolor = colors["highlight"]
              lpw = "1.2"
          if node.right.id in highlight_path:
              rcolor = colors["highlight"]
              rpw = "1.2"
  
          if show_just_path:
              if node.left.id in highlight_path:
                  edges.append(
                      f'{nname} -> {left_node_name} [penwidth={lpw} fontname="{fontname}" color="{lcolor}" label=<{llabel}> fontcolor="{colors["text"]}"]'
                  )
              if node.right.id in highlight_path:
                  edges.append(
                      f'{nname} -> {right_node_name} [penwidth={rpw} fontname="{fontname}" color="{rcolor}" label=<{rlabel}> fontcolor="{colors["text"]}"]'
                  )
          else:
              edges.append(
                  f'{nname} -> {left_node_name} [penwidth={lpw} fontname="{fontname}" color="{lcolor}" label=<{llabel}> fontcolor="{colors["text"]}"]'
              )
              edges.append(
                  f'{nname} -> {right_node_name} [penwidth={rpw} fontname="{fontname}" color="{rcolor}" label=<{rlabel}> fontcolor="{colors["text"]}"]'
              )
              edges.append(
                  f"""
                      {{
                          rank=same;
                          {left_node_name} -> {right_node_name} [style=invis]
                      }}
                      """
              )
  
      newline = "\n\t"
      if title:
          title_element = f'graph [label="{title}", labelloc=t, fontname="{fontname}" fontsize={title_fontsize} fontcolor="{colors["title"]}"];'
      else:
          title_element = ""
      dot = f"""
          digraph G {{
              splines=line;
              nodesep={nodesep};
              ranksep={ranksep};
              rankdir={orientation};
              margin=0.0;
              {title_element}
              node [margin="0.03" penwidth="0.5" width=.1, height=.1];
              edge [arrowsize=.4 penwidth="0.3"]
  
              {newline.join(internal)}
              {newline.join(edges)}
              {newline.join(leaves)}
  
              {class_legend_gr()}
              {instance_gr()}
          }}
              """
  
      return DTreeVizRender(dot, scale)
  
imports: import os
  import tempfile
  from typing import Mapping, List, Callable
  import matplotlib
  import matplotlib.patches
  import matplotlib.pyplot
  import numpy
  import pandas
  from colour import Color, rgb2hex
  from sklearn import tree
  from dtreeviz.colors import adjust_colors
  from dtreeviz.interpretation import explain_prediction_plain_english, explain_prediction_sklearn_default
  from dtreeviz.models.shadow_decision_tree import ShadowDecTree
  from dtreeviz.models.shadow_decision_tree import ShadowDecTreeNode
  from dtreeviz.utils import myround, DTreeVizRender, add_classifier_legend, _format_axes, _draw_wedge, _set_wedge_ticks, tessellate, is_numeric
label_desc: Use image node_id 4 for calling the DTreeVizAPI obj's underlying member method code with example usage: obj.view(precision, orientation, instance_orientation, show_root_edge_labels, show_node_labels, show_just_path, fancy, histtype, leaftype, highlight_path, x, max_X_features_LR, max_X_features_TD, depth_range_to_display, label_fontsize, ticks_fontsize, fontname, title, title_fontsize, colors, scale) and returns: DTreeVizRender, str, node, gr_node, str, fontname, str, colors, str, node, str, str, filepath, str, str, class_legend_html, str, str, instance_html, str, leaf, str, colors, str, fontname, str, colors, str, int, str, edge_label, str, str, node, str, colors, str, html, str, str, node, str, colors, str, html, str, str, node, str, colors, str, html, str, str, node, str, colors, str, html, str, str, html_output, str, str, str, str, str, str, _internal, self, _leaves, self