code: def _grow_tree(self, X, y):
      stack = []
      num_samples_per_class = np.array(
          [np.sum(y == i) for i in range(self.n_classes_)]
      )
      root = Node(
          impurity=self._calc_impurity(y),
          num_samples=y.size,
          num_samples_per_class=num_samples_per_class,
          predicted_probs=num_samples_per_class / y.size,
      )
      root.impurity_reduction = self._best_split(X, y)[-1]
  
      stack.append((root, X, y))
      self.n_splits = 0
      while stack:
          node, X_node, y_node = stack.pop()
          idx, thr, _ = self._best_split(X_node, y_node)
          if idx is not None:
              self.n_splits += 1
              indices_left = X_node[:, idx] < thr
              X_left, y_left = (
                  X_node[indices_left],
                  y_node[indices_left],
              )
              X_right, y_right = (
                  X_node[~indices_left],
                  y_node[~indices_left],
              )
              node.feature_index = idx
              node.threshold = thr
              num_samples_per_class_left = np.array(
                  [np.sum(y_left == i) for i in range(self.n_classes_)]
              )
              node.left = Node(
                  impurity=self._calc_impurity(y_left),
                  num_samples=y_left.size,
                  num_samples_per_class=num_samples_per_class_left,
                  predicted_probs=num_samples_per_class_left
                  / y_left.size,
              )
              # some redundant calculation going on here, but it's okay....
              node.left.impurity_reduction = self._best_split(
                  X_left, y_left
              )[-1]
  
              num_samples_per_class_right = np.array(
                  [np.sum(y_right == i) for i in range(self.n_classes_)]
              )
              node.right = Node(
                  impurity=self._calc_impurity(y_right),
                  num_samples=y_right.size,
                  num_samples_per_class=num_samples_per_class_right,
                  predicted_probs=num_samples_per_class_right
                  / y_right.size,
              )
              node.right.impurity_reduction = self._best_split(
                  X_right, y_right
              )[-1]
              stack.append((node.right, X_right, y_right))
              stack.append((node.left, X_left, y_left))
  
          # early stop
          if (
              self.max_leaf_nodes
              and self.n_splits >= self.max_leaf_nodes - 1
          ):
              return root
  
          # sort stack by impurity_reduction
          stack = sorted(
              stack, key=lambda x: x[0].impurity_reduction, reverse=True
          )
  
      return root
  
imports: from sklearn.tree._tree import Tree
  from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
  from sklearn.base import ClassifierMixin, RegressorMixin
  from sklearn import __version__
  from collections import namedtuple
  import pandas
  import numpy
  from collections import namedtuple
  from sklearn import __version__
  from sklearn.base import ClassifierMixin, RegressorMixin
  from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
  from sklearn.tree._tree import Tree
  import imodels.util.tree
label_desc: Use image node_id 3 for calling the CustomDecisionTreeClassifier obj's underlying member method code with example usage: obj._grow_tree(X, y) and returns: root, root