class_docstr: TSICEExplainer extends the Individual Conditional Expectation for correlated
  timeseries data (higher dimensions). It uses TSFeatures to derive time series
  structural features, and uses data perturber(TSPerturber) for generating simulated
  data. TSICEExplainer explains the trend in the model forecast change with time series
  derived features.
  
  References:
      .. [#0] `Goldstein et al. 'Peeking Inside the Black Box: Visualizing Statistical
          Learning with Plots of Individual Conditional Expectation'
          <https://arxiv.org/abs/1309.6392>`_
class_docstr_tok: ["TSICEExplainer","extends","the","Individual","Conditional","Expectation","for","correlated","timeseries","data","(","higher","dimensions",")",".","It","uses","TSFeatures","to","derive","time","series","structural","features",",","and","uses","data","perturber","(","TSPerturber",")","for","generating","simulated","data",".","TSICEExplainer","explains","the","trend","in","the","model","forecast","change","with","time","series","derived","features",".","References",":","..","[","#","0","]","`","Goldstein","et","al",".","'Peeking","Inside","the","Black","Box",":","Visualizing","Statistical","Learning","with","Plots","of","Individual","Conditional","Expectation'","<","https",":","\/\/arxiv.org\/abs\/1309.6392",">","`","_"]
code: def _explain_instance(
      self,
      ts: Union["tsFrame", np.ndarray],
      ts_related: Union["tsFrame", np.ndarray],
      **explain_params,
  ):
      x = ts
      x_exog = ts_related
      input_length = explain_params.get(
          "input_length", self._parameters.get("input_length")
      )
      lookahead = explain_params.get(
          "forecast_lookahead",
          self._parameters.get("forecast_lookahead"),
      )
      n_variables = explain_params.get(
          "n_variables", self._parameters.get("n_variables")
      )
      n_exogs = explain_params.get(
          "n_exogs", self._parameters.get("n_exogs")
      )
      n_perturbations = explain_params.get(
          "n_perturbations", self._parameters.get("n_perturbations")
      )
  
      n_obs, n_vars = x.shape
      assert (
          n_obs >= input_length
      ), f"Error: model requires min input length {input_length}!"
      assert (
          n_vars == n_variables
      ), f"Error: expects {n_variables} endogenous variable(s) but found {n_vars}!"
  
      if n_exogs > 0:
          assert (
              x_exog is not None
          ), f"Error: expects {n_exogs} exogenous features to be specific!"
          assert x_exog.shape[1] == n_exogs, (
              f"Error: exogenous dimension does not match "
              f"{n_exogs} != {x_exog.shape[1]}"
          )
          assert (
              x_exog.shape[0] == x.shape[0]
          ), f"Error: exogenous and endogenous variable are not aligned!"
          x_exog = x_exog[-(input_length + lookahead) :]
  
      x = x[-input_length:]
  
      if self.tensor_model:
          xc = to_np_array(x)
          if x_exog is not None:
              xc_exog = to_np_array(x_exog)
              forecast = np.asarray(
                  self.model(
                      [xc.reshape(1, -1), xc_exog.reshape(1, -1)]
                  )[0]
              )
          else:
              forecast = np.asarray(self.model(xc.reshape(1, -1))[0])
      else:
          if x_exog is not None:
              try:
                  forecast = np.asarray(self.model(x, x_exog))
              except:
                  xc = to_np_array(x)
                  xc_exog = to_np_array(x_exog)
                  forecast = np.asarray(
                      self.model(
                          [xc.reshape(1, -1), xc_exog.reshape(1, -1)]
                      )[0]
                  )
                  self.tensor_model = True
          else:
              try:
                  forecast = np.asarray(self.model(x))
              except:
                  xc = to_np_array(x)
                  forecast = np.asarray(
                      self.model(xc.reshape(1, -1))[0]
                  )
                  self.tensor_model = True
      if len(forecast.shape) == 1:
          forecast = np.reshape(forecast, (-1, 1))
      assert forecast.shape[0] == lookahead
      assert forecast.shape[1] == n_variables
  
      x_data, _ = self.perturber.fit_transform(x, None, n_perturbations)
  
      feature_sets = []
      feat_names = []
      current_features_set = []
      for feat_extractor in self.feat_extractors:
          features = np.asarray(feat_extractor.batch_compute(x_data))
          feature_sets.append(features.tolist())
          feat_names.append(feat_extractor.name)
          current_features_set.append(
              np.asarray(feat_extractor.feat_compute(x))
          )
  
      base_f = forecast
  
      signed_impact = []
      unsigned_impact = []
      forecasts_on_perturbations = []
      for i in range(n_perturbations):
          if self.tensor_model:
              xc_data = to_np_array(x_data[i])
              if x_exog is not None:
                  f = np.asarray(
                      self.model(
                          [
                              xc_data.reshape(1, -1),
                              xc_exog.reshape(1, -1),
                          ]
                      )[0]
                  )
              else:
                  f = np.asarray(self.model(xc_data.reshape(1, -1))[0])
          else:
              if x_exog is not None:
                  f = np.asarray(self.model(x_data[i], x_exog))
              else:
                  f = np.asarray(self.model(x_data[i]))
          if len(f.shape) == 1:
              f = np.reshape(f, (-1, 1))
  
          forecasts_on_perturbations.append(f)
          sif = np.mean(np.mean(f - base_f, axis=0))
          uif = np.mean(np.sqrt(np.mean(np.square(f - base_f), axis=0)))
          signed_impact.append(sif)
          unsigned_impact.append(uif)
  
      signed_impact = np.asarray(signed_impact)
      unsigned_impact = np.asarray(unsigned_impact)
  
      if isinstance(ts, pd.DataFrame):
          data_x = ts[-signed_impact.shape[0] :].to_dict()
      else:
          data_x = ts[-signed_impact.shape[0] :].tolist()
  
      explanation = {}
      explanation["data_x"] = data_x
      explanation["current_forecast"] = base_f
      explanation["feature_names"] = feat_names
      explanation["feature_values"] = feature_sets
      explanation["signed_impact"] = signed_impact.tolist()
      explanation["total_impact"] = unsigned_impact.tolist()
      explanation["current_feature_values"] = current_features_set
      explanation["perturbations"] = [pert.to_dict() for pert in x_data]
      explanation[
          "forecasts_on_perturbations"
      ] = forecasts_on_perturbations
  
      return explanation
  
file_path: AIX360/aix360/algorithms/tsice/tsice.py
imports: import warnings
  import numpy
  import pandas
  from typing import List, Union, Callable
  from aix360.algorithms.tslbbe import TSLocalBBExplainer
  from aix360.algorithms.tsutils.tsframe import tsFrame, to_np_array
  from aix360.algorithms.tsutils.tsperturbers.tsperturber import TSPerturber, BlockSelector
  from aix360.algorithms.tsutils.tsperturbers.perturbed_data_generator import PerturbedDataGenerator
  from aix360.algorithms.tsutils.tsfeatures.latest_features import LatestFeature
  from aix360.algorithms.tsutils.tsfeatures.range_features import RangeFeature
label_desc: Use image node_id 4 for calling the TSICEExplainer obj's underlying member method code with example usage: obj._explain_instance(ts, ts_related) and returns: explanation