code: def _predict_tree(model, X, joint_contribution=False):
      """
      For a given DecisionTreeRegressor, DecisionTreeClassifier,
      ExtraTreeRegressor, or ExtraTreeClassifier,
      returns a triple of [prediction, bias and feature_contributions], such
      that prediction ≈ bias + feature_contributions.
      """
      leaves = model.apply(X)
      paths = _get_tree_paths(model.tree_, 0)
  
      for path in paths:
          path.reverse()
  
      leaf_to_path = {}
      # map leaves to paths
      for path in paths:
          leaf_to_path[path[-1]] = path
  
      # remove the single-dimensional inner arrays
      values = model.tree_.value.squeeze(axis=1)
      # reshape if squeezed into a single float
      if len(values.shape) == 0:
          values = np.array([values])
      if isinstance(model, DecisionTreeRegressor):
          # we require the values to be the same shape as the biases
          values = values.squeeze(axis=1)
          biases = np.full(X.shape[0], values[paths[0][0]])
          line_shape = X.shape[1]
      elif isinstance(model, DecisionTreeClassifier):
          # scikit stores category counts, we turn them into probabilities
          normalizer = values.sum(axis=1)[:, np.newaxis]
          normalizer[normalizer == 0.0] = 1.0
          values /= normalizer
  
          biases = np.tile(values[paths[0][0]], (X.shape[0], 1))
          line_shape = (X.shape[1], model.n_classes_)
      direct_prediction = values[leaves]
  
      # make into python list, accessing values will be faster
      values_list = list(values)
      feature_index = list(model.tree_.feature)
  
      contributions = []
      if joint_contribution:
          for row, leaf in enumerate(leaves):
              path = leaf_to_path[leaf]
  
              path_features = set()
              contributions.append({})
              for i in range(len(path) - 1):
                  path_features.add(feature_index[path[i]])
                  contrib = (
                      values_list[path[i + 1]] - values_list[path[i]]
                  )
                  # path_features.sort()
                  contributions[row][tuple(sorted(path_features))] = (
                      contributions[row].get(
                          tuple(sorted(path_features)), 0
                      )
                      + contrib
                  )
          return direct_prediction, biases, contributions
  
      else:
          unique_leaves = np.unique(leaves)
          unique_contributions = {}
  
          for row, leaf in enumerate(unique_leaves):
              for path in paths:
                  if leaf == path[-1]:
                      break
  
              contribs = np.zeros(line_shape)
              for i in range(len(path) - 1):
                  contrib = (
                      values_list[path[i + 1]] - values_list[path[i]]
                  )
                  contribs[feature_index[path[i]]] += contrib
              unique_contributions[leaf] = contribs
  
          for row, leaf in enumerate(leaves):
              contributions.append(unique_contributions[leaf])
  
          return direct_prediction, biases, np.array(contributions)
  
file_path: treeinterpreter/treeinterpreter/treeinterpreter.py
imports: import numpy
  import sklearn
  from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, RandomForestRegressor, ExtraTreesRegressor
  from sklearn.tree import DecisionTreeRegressor, DecisionTreeClassifier, _tree
  from distutils.version import LooseVersion
label_desc: Use image node_id 2 for calling a global function with example usage: _predict_tree(model, X, joint_contribution) and returns: direct_prediction,  biases,  contributions, direct_prediction,  biases,  np