class_docstr: A predictor that uses a QRX model for each of the steps in the forecast
  horizon.
  
  (In other words, there's a total of prediction_length many models being
  trained. In particular, this predictor does not learn a multivariate
  distribution.) The list of these models is saved under self.model_list.
docstr: This function only works for self.method == "QuantileRegression",
  and uses lightgbm's feature importance functionality. It takes the
  mean feature importance across quantiles and timestamps in the
  forecast horizon; and then adds these mean values across all of the
  feature coordinates that are associated to "target",
  "feat_static_real", "feat_static_cat", "past_feat_dynamic_real",
  "feat_dynamic_real", "feat_dynamic_cat"
  
  Parameters
  ----------
  importance_type: str
      Either "gain" or "split". Since for the models that predict
      timestamps that are further along in the forecast horizon are
      expected to perform less well, it is desirable to give less
      weight to those models compared to the ones that make
      predictions for timestamps closer to the forecast horizon.
      "split" will give equal weight, and is therefore less desirable;
      whereas "gain" will naturally give less weight to models that
      perform less well.
  
  percentage: bool
      If results should be in percentage format and sum up to 1. Default is True
  
  Returns
  -------
  ExplanationResult
docstr_tok: ["This","function","only","works","for","self.method","==","``","QuantileRegression","''",",","and","uses","lightgbm","'s","feature","importance","functionality",".","It","takes","the","mean","feature","importance","across","quantiles","and","timestamps","in","the","forecast","horizon",";","and","then","adds","these","mean","values","across","all","of","the","feature","coordinates","that","are","associated","to","``","target","''",",","''","feat_static_real","''",",","``","feat_static_cat","''",",","``","past_feat_dynamic_real","''",",","''","feat_dynamic_real","''",",","``","feat_dynamic_cat","''","Parameters","--","--","--","--","--","importance_type",":","str","Either","``","gain","''","or","``","split","''",".","Since","for","the","models","that","predict","timestamps","that","are","further","along","in","the","forecast","horizon","are","expected","to","perform","less","well",",","it","is","desirable","to","give","less","weight","to","those","models","compared","to","the","ones","that","make","predictions","for","timestamps","closer","to","the","forecast","horizon",".","``","split","''","will","give","equal","weight",",","and","is","therefore","less","desirable",";","whereas","``","gain","''","will","naturally","give","less","weight","to","models","that","perform","less","well",".","percentage",":","bool","If","results","should","be","in","percentage","format","and","sum","up","to","1",".","Default","is","True","Returns","--","--","--","-","ExplanationResult"]
code: def explain(
      self, importance_type: str = "gain", percentage: bool = True
  ) -> ExplanationResult:
      """
      This function only works for self.method == "QuantileRegression",
      and uses lightgbm's feature importance functionality. It takes the
      mean feature importance across quantiles and timestamps in the
      forecast horizon; and then adds these mean values across all of the
      feature coordinates that are associated to "target",
      "feat_static_real", "feat_static_cat", "past_feat_dynamic_real",
      "feat_dynamic_real", "feat_dynamic_cat"
  
      Parameters
      ----------
      importance_type: str
          Either "gain" or "split". Since for the models that predict
          timestamps that are further along in the forecast horizon are
          expected to perform less well, it is desirable to give less
          weight to those models compared to the ones that make
          predictions for timestamps closer to the forecast horizon.
          "split" will give equal weight, and is therefore less desirable;
          whereas "gain" will naturally give less weight to models that
          perform less well.
  
      percentage: bool
          If results should be in percentage format and sum up to 1. Default is True
  
      Returns
      -------
      ExplanationResult
      """
      assert self.method == "QuantileRegression", (
          "the explain method is "
          "only currently "
          "supported for "
          "QuantileRegression"
      )
  
      assert self.model_list is not None
  
      importances = np.array(
          [
              [
                  self.model_list[time_stamp]
                  .models[quantile]
                  .booster_.feature_importance(
                      importance_type=importance_type
                  )
                  for time_stamp in range(self.prediction_length)
              ]
              for quantile in self.quantiles
          ]
      ).transpose((2, 1, 0))
      # The shape is: (features, pred_length, quantiles)
      importances = importances.mean(axis=2)  # Average over quantiles
      # The shape of importances is: (features, pred_length)
  
      if percentage:
          importances /= importances.sum(axis=0)
      dynamic_length = self.preprocess_object.dynamic_length
      num_feat_static_real = self.preprocess_object.num_feat_static_real
      num_feat_static_cat = self.preprocess_object.num_feat_static_cat
      num_past_feat_dynamic_real = (
          self.preprocess_object.num_past_feat_dynamic_real
      )
      num_feat_dynamic_real = (
          self.preprocess_object.num_feat_dynamic_real
      )
      num_feat_dynamic_cat = self.preprocess_object.num_feat_dynamic_cat
  
      assert num_feat_static_real is not None
      assert num_feat_static_cat is not None
      assert num_past_feat_dynamic_real is not None
      assert num_feat_dynamic_real is not None
      assert num_feat_dynamic_cat is not None
  
      coordinate_map: Dict[str, Any] = {}
      coordinate_map["target"] = (0, dynamic_length)
      coordinate_map["feat_static_real"] = [
          (dynamic_length + i, dynamic_length + i + 1)
          for i in range(num_feat_static_real)
      ]
      coordinate_map["feat_static_cat"] = []
      static_cat_features_so_far = 0
      cardinality = (
          self.preprocess_object.cardinality
          if (
              self.preprocess_object.cardinality
              and self.preprocess_object.one_hot_encode
          )
          else [1] * num_feat_static_cat
      )
  
      for i in range(num_feat_static_cat):
          coordinate_map["feat_static_cat"].append(
              (
                  dynamic_length
                  + num_feat_static_real
                  + static_cat_features_so_far,
                  dynamic_length
                  + num_feat_static_real
                  + static_cat_features_so_far
                  + cardinality[i],
              )
          )
          static_cat_features_so_far += cardinality[i]
  
      coordinate_map["past_feat_dynamic_real"] = [
          (
              num_feat_static_real
              + static_cat_features_so_far
              + (i + 1) * dynamic_length,
              num_feat_static_real
              + static_cat_features_so_far
              + (i + 2) * dynamic_length,
          )
          for i in range(num_past_feat_dynamic_real)
      ]
      coordinate_map["feat_dynamic_real"] = [
          (
              num_feat_static_real
              + static_cat_features_so_far
              + (num_past_feat_dynamic_real + 1) * dynamic_length
              + i * (dynamic_length + self.prediction_length),
              num_feat_static_real
              + static_cat_features_so_far
              + (num_past_feat_dynamic_real + 1) * dynamic_length
              + (i + 1) * (dynamic_length + self.prediction_length),
          )
          for i in range(num_feat_dynamic_real)
      ]
      coordinate_map["feat_dynamic_cat"] = [
          (
              num_feat_static_real
              + static_cat_features_so_far
              + (num_past_feat_dynamic_real + 1) * dynamic_length
              + num_feat_dynamic_real
              * (dynamic_length + self.prediction_length)
              + i,
              num_feat_static_real
              + static_cat_features_so_far
              + (num_past_feat_dynamic_real + 1) * dynamic_length
              + num_feat_dynamic_real
              * (dynamic_length + self.prediction_length)
              + i
              + 1,
          )
          for i in range(num_feat_dynamic_cat)
      ]
      logger.info(
          f"coordinate_map from the preprocessor is: {coordinate_map}"
      )
      logger.info(f"shape of importance matrix is: {importances.shape}")
      assert (
          sum(
              [
                  sum(
                      [
                          coor[1] - coor[0]
                          for coor in coordinate_map[key]
                      ]
                  )
                  for key in coordinate_map
                  if key != "target"
              ]
          )
          + coordinate_map["target"][1]
          - coordinate_map["target"][0]
      ) == importances.shape[
          0
      ]  # Testing that we covered all of coordinates
  
      quantile_aggregated_importance_result = FeatureImportanceResult(
          target=np.expand_dims(
              importances[
                  coordinate_map["target"][0] : coordinate_map[
                      "target"
                  ][1],
                  :,
              ].sum(axis=0),
              axis=0,
          ).tolist(),
          feat_static_real=[
              importances[start:end, :].sum(axis=0).tolist()
              for start, end in coordinate_map["feat_static_real"]
          ],
          feat_static_cat=[
              importances[start:end, :].sum(axis=0).tolist()
              for start, end in coordinate_map["feat_static_cat"]
          ],
          past_feat_dynamic_real=[
              importances[start:end, :].sum(axis=0).tolist()
              for start, end in coordinate_map["past_feat_dynamic_real"]
          ],
          feat_dynamic_real=[
              importances[start:end, :].sum(axis=0).tolist()
              for start, end in coordinate_map["feat_dynamic_real"]
          ],
          feat_dynamic_cat=[
              importances[start:end, :].sum(axis=0).tolist()
              for start, end in coordinate_map["feat_dynamic_cat"]
          ],
      )
  
      explain_result = ExplanationResult(
          time_quantile_aggregated_result=quantile_aggregated_importance_result.mean(
              axis=1
          ),
          quantile_aggregated_result=quantile_aggregated_importance_result,
      )
      logger.info(
          f"explain result with importance_type: {importance_type} and percentage: {percentage} is {explain_result.dict()}"
      )
      return explain_result
  
imports: import concurrent.futures
  import logging
  import pickle
  from itertools import chain
  from typing import Iterator, List, Optional, Any, Dict
  from toolz import first
  import numpy
  import pandas
  from pathlib import Path
  from itertools import compress
  from gluonts.core.component import validated
  from gluonts.dataset.common import Dataset
  from gluonts.dataset.util import forecast_start
  from gluonts.model.forecast import Forecast
  from gluonts.model.forecast_generator import log_once
  from gluonts.model.predictor import RepresentablePredictor
  from ._model import QRF, QRX, QuantileReg
  from ._preprocess import Cardinality, PreprocessOnlyLagFeatures
  from ._types import FeatureImportanceResult, ExplanationResult
label_desc: Use image node_id 6 for calling the TreePredictor obj's underlying member method code with example usage: obj.explain(importance_type, percentage) and returns: explain_result