class_docstr: Computes SHAP values for a linear model, optionally accounting for inter-feature correlations.
  
  This computes the SHAP values for a linear model and can account for the correlations among
  the input features. Assuming features are independent leads to interventional SHAP values which
  for a linear model are coef[i] * (x[i] - X.mean(0)[i]) for the ith feature. If instead we account
  for correlations then we prevent any problems arising from collinearity and share credit among
  correlated features. Accounting for correlations can be computationally challenging, but
  LinearExplainer uses sampling to estimate a transform that can then be applied to explain
  any prediction of the model.
  
  Parameters
  ----------
  model : (coef, intercept) or sklearn.linear_model.*
      User supplied linear model either as either a parameter pair or sklearn object.
  
  data : (mean, cov), numpy.array, pandas.DataFrame, iml.DenseData or scipy.csr_matrix
      The background dataset to use for computing conditional expectations. Note that only the
      mean and covariance of the dataset are used. This means passing a raw data matrix is just
      a convenient alternative to passing the mean and covariance directly.
  nsamples : int
      Number of samples to use when estimating the transformation matrix used to account for
      feature correlations.
  feature_perturbation : "interventional" (default) or "correlation_dependent"
      There are two ways we might want to compute SHAP values, either the full conditional SHAP
      values or the interventional SHAP values. For interventional SHAP values we break any
      dependence structure between features in the model and so uncover how the model would behave if we
      intervened and changed some of the inputs. For the full conditional SHAP values we respect
      the correlations among the input features, so if the model depends on one input but that
      input is correlated with another input, then both get some credit for the model's behavior. The
      interventional option stays "true to the model" meaning it will only give credit to features that are
      actually used by the model, while the correlation option stays "true to the data" in the sense that
      it only considers how the model would behave when respecting the correlations in the input data.
      For sparse case only interventional option is supported.
  
  Examples
  --------
  See `Linear explainer examples <https://shap.readthedocs.io/en/latest/api_examples/explainers/LinearExplainer.html>`_
class_docstr_tok: ["Computes","SHAP","values","for","a","linear","model",",","optionally","accounting","for","inter-feature","correlations",".","This","computes","the","SHAP","values","for","a","linear","model","and","can","account","for","the","correlations","among","the","input","features",".","Assuming","features","are","independent","leads","to","interventional","SHAP","values","which","for","a","linear","model","are","coef","[","i","]","*","(","x","[","i","]","-","X.mean","(","0",")","[","i","]",")","for","the","ith","feature",".","If","instead","we","account","for","correlations","then","we","prevent","any","problems","arising","from","collinearity","and","share","credit","among","correlated","features",".","Accounting","for","correlations","can","be","computationally","challenging",",","but","LinearExplainer","uses","sampling","to","estimate","a","transform","that","can","then","be","applied","to","explain","any","prediction","of","the","model",".","Parameters","--","--","--","--","--","model",":","(","coef",",","intercept",")","or","sklearn.linear_model",".","*","User","supplied","linear","model","either","as","either","a","parameter","pair","or","sklearn","object",".","data",":","(","mean",",","cov",")",",","numpy.array",",","pandas.DataFrame",",","iml.DenseData","or","scipy.csr_matrix","The","background","dataset","to","use","for","computing","conditional","expectations",".","Note","that","only","the","mean","and","covariance","of","the","dataset","are","used",".","This","means","passing","a","raw","data","matrix","is","just","a","convenient","alternative","to","passing","the","mean","and","covariance","directly",".","nsamples",":","int","Number","of","samples","to","use","when","estimating","the","transformation","matrix","used","to","account","for","feature","correlations",".","feature_perturbation",":","``","interventional","''","(","default",")","or","``","correlation_dependent","''","There","are","two","ways","we","might","want","to","compute","SHAP","values",",","either","the","full","conditional","SHAP","values","or","the","interventional","SHAP","values",".","For","interventional","SHAP","values","we","break","any","dependence","structure","between","features","in","the","model","and","so","uncover","how","the","model","would","behave","if","we","intervened","and","changed","some","of","the","inputs",".","For","the","full","conditional","SHAP","values","we","respect","the","correlations","among","the","input","features",",","so","if","the","model","depends","on","one","input","but","that","input","is","correlated","with","another","input",",","then","both","get","some","credit","for","the","model","'s","behavior",".","The","interventional","option","stays","``","true","to","the","model","''","meaning","it","will","only","give","credit","to","features","that","are","actually","used","by","the","model",",","while","the","correlation","option","stays","``","true","to","the","data","''","in","the","sense","that","it","only","considers","how","the","model","would","behave","when","respecting","the","correlations","in","the","input","data",".","For","sparse","case","only","interventional","option","is","supported",".","Examples","--","--","--","--","See","`","Linear","explainer","examples","<","https",":","\/\/shap.readthedocs.io\/en\/latest\/api_examples\/explainers\/LinearExplainer.html",">","`","_"]
code: def __init__(
      self,
      model,
      masker,
      link=links.identity,
      nsamples=1000,
      feature_perturbation=None,
      **kwargs,
  ):
      if "feature_dependence" in kwargs:
          warnings.warn(
              "The option feature_dependence has been renamed to feature_perturbation!"
          )
          feature_perturbation = kwargs["feature_dependence"]
      if feature_perturbation == "independent":
          warnings.warn(
              'The option feature_perturbation="independent" is has been renamed to feature_perturbation="interventional"!'
          )
          feature_perturbation = "interventional"
      elif feature_perturbation == "correlation":
          warnings.warn(
              'The option feature_perturbation="correlation" is has been renamed to feature_perturbation="correlation_dependent"!'
          )
          feature_perturbation = "correlation_dependent"
      if feature_perturbation is not None:
          warnings.warn(
              "The feature_perturbation option is now deprecated in favor of using the appropriate masker (maskers.Independent, or maskers.Impute)"
          )
      else:
          feature_perturbation = "interventional"
      self.feature_perturbation = feature_perturbation
  
      # wrap the incoming masker object as a shap.Masker object before calling
      # parent class constructor, which does the same but without respecting
      # the user-provided feature_perturbation choice
      if isinstance(masker, pd.DataFrame) or (
          (isinstance(masker, np.ndarray) or issparse(masker))
          and len(masker.shape) == 2
      ):
          if self.feature_perturbation == "correlation_dependent":
              masker = maskers.Impute(masker)
          else:
              masker = maskers.Independent(masker)
      elif issubclass(type(masker), tuple) and len(masker) == 2:
          if self.feature_perturbation == "correlation_dependent":
              masker = maskers.Impute(
                  {"mean": masker[0], "cov": masker[1]}, method="linear"
              )
          else:
              masker = maskers.Independent(
                  {"mean": masker[0], "cov": masker[1]}
              )
  
      super().__init__(model, masker, link=link, **kwargs)
  
      self.nsamples = nsamples
  
      # extract what we need from the given model object
      self.coef, self.intercept = LinearExplainer._parse_model(model)
  
      # extract the data
      if issubclass(
          type(self.masker), (maskers.Independent, maskers.Partition)
      ):
          self.feature_perturbation = "interventional"
      elif issubclass(type(self.masker), maskers.Impute):
          self.feature_perturbation = "correlation_dependent"
      else:
          raise NotImplementedError(
              "The Linear explainer only supports the Independent, Partition, and Impute maskers right now!"
          )
      data = getattr(self.masker, "data", None)
  
      # convert DataFrame's to numpy arrays
      if isinstance(data, pd.DataFrame):
          data = data.values
  
      # get the mean and covariance of the model
      if getattr(self.masker, "mean", None) is not None:
          self.mean = self.masker.mean
          self.cov = self.masker.cov
      elif isinstance(data, dict) and len(data) == 2:
          self.mean = data["mean"]
          if isinstance(self.mean, pd.Series):
              self.mean = self.mean.values
  
          self.cov = data["cov"]
          if isinstance(self.cov, pd.DataFrame):
              self.cov = self.cov.values
      elif isinstance(data, tuple) and len(data) == 2:
          self.mean = data[0]
          if isinstance(self.mean, pd.Series):
              self.mean = self.mean.values
  
          self.cov = data[1]
          if isinstance(self.cov, pd.DataFrame):
              self.cov = self.cov.values
      elif data is None:
          raise ValueError(
              "A background data distribution must be provided!"
          )
      else:
          if issparse(data):
              self.mean = np.array(np.mean(data, 0))[0]
              if self.feature_perturbation != "interventional":
                  raise NotImplementedError(
                      "Only feature_perturbation = 'interventional' is supported for sparse data"
                  )
          else:
              self.mean = np.array(
                  np.mean(data, 0)
              ).flatten()  # assumes it is an array
              if self.feature_perturbation == "correlation_dependent":
                  self.cov = np.cov(data, rowvar=False)
      # print(self.coef, self.mean.flatten(), self.intercept)
      # Note: mean can be numpy.matrixlib.defmatrix.matrix or numpy.matrix type depending on numpy version
      if issparse(self.mean) or str(type(self.mean)).endswith(
          "matrix'>"
      ):
          # accept both sparse and dense coef
          # if not issparse(self.coef):
          #     self.coef = np.asmatrix(self.coef)
          self.expected_value = (
              np.dot(self.coef, self.mean) + self.intercept
          )
  
          # unwrap the matrix form
          if len(self.expected_value) == 1:
              self.expected_value = self.expected_value[0, 0]
          else:
              self.expected_value = np.array(self.expected_value)[0]
      else:
          self.expected_value = (
              np.dot(self.coef, self.mean) + self.intercept
          )
  
      self.M = len(self.mean)
  
      # if needed, estimate the transform matrices
      if self.feature_perturbation == "correlation_dependent":
          self.valid_inds = np.where(np.diag(self.cov) > 1e-8)[0]
          self.mean = self.mean[self.valid_inds]
          self.cov = self.cov[:, self.valid_inds][self.valid_inds, :]
          self.coef = self.coef[self.valid_inds]
  
          # group perfectly redundant variables together
          self.avg_proj, sum_proj = duplicate_components(self.cov)
          self.cov = np.matmul(
              np.matmul(self.avg_proj, self.cov), self.avg_proj.T
          )
          self.mean = np.matmul(self.avg_proj, self.mean)
          self.coef = np.matmul(sum_proj, self.coef)
  
          # if we still have some multi-collinearity present then we just add regularization...
          e, _ = np.linalg.eig(self.cov)
          if e.min() < 1e-7:
              self.cov = self.cov + np.eye(self.cov.shape[0]) * 1e-6
  
          mean_transform, x_transform = self._estimate_transforms(
              nsamples
          )
          self.mean_transformed = np.matmul(mean_transform, self.mean)
          self.x_transform = x_transform
      elif self.feature_perturbation == "interventional":
          if nsamples != 1000:
              warnings.warn(
                  "Setting nsamples has no effect when feature_perturbation = 'interventional'!"
              )
      else:
          raise InvalidFeaturePerturbationError(
              "Unknown type of feature_perturbation provided: "
              + self.feature_perturbation
          )
  
file_path: shap/shap/explainers/_linear.py
imports: import warnings
  import numpy
  import pandas
  from scipy.sparse import issparse
  from tqdm.auto import tqdm
  from ..None import links, maskers
  from ..utils._exceptions import DimensionError, InvalidFeaturePerturbationError, InvalidModelError
  from ._explainer import Explainer
label_desc: Use image node_id 1 to create a new LinearExplainer object from inherited base classes: Explainer with example: obj = LinearExplainer(model, masker, link, nsamples, feature_perturbation)