class_docstr: MPCA implementation compatible with scikit-learn
  
  Args:
      var_ratio (float, optional): Percentage of variance explained (between 0 and 1). Defaults to 0.97.
      max_iter (int, optional): Maximum number of iteration. Defaults to 1.
      vectorize (bool): Whether return the transformed/projected tensor in vector. Defaults to False.
      n_components (int): Number of components to keep. Applies only when vectorize=True. Defaults to None.
  
  Attributes:
      proj_mats (list of arrays): A list of transposed projection matrices, shapes (P_1, I_1), ...,
          (P_N, I_N), where P_1, ..., P_N are output tensor shape for each sample.
      idx_order (array-like): The ordering index of projected (and vectorized) features in decreasing variance.
      mean_ (array-like): Per-feature empirical mean, estimated from the training set, shape (I_1, I_2, ..., I_N).
      shape_in (tuple): Input tensor shapes, i.e. (I_1, I_2, ..., I_N).
      shape_out (tuple): Output tensor shapes, i.e. (P_1, P_2, ..., P_N).
  
  Reference:
      Haiping Lu, K.N. Plataniotis, and A.N. Venetsanopoulos, "MPCA: Multilinear Principal Component Analysis of
      Tensor Objects", IEEE Transactions on Neural Networks, Vol. 19, No. 1, Page: 18-39, January 2008. For initial
      Matlab implementation, please go to https://uk.mathworks.com/matlabcentral/fileexchange/26168.
  
  Examples:
      >>> import numpy as np
      >>> from kale.embed.factorization import MPCA
      >>> x = np.random.random((40, 20, 25, 20))
      >>> x.shape
      (40, 20, 25, 20)
      >>> mpca = MPCA()
      >>> x_projected = mpca.fit_transform(x)
      >>> x_projected.shape
      (40, 18, 23, 18)
      >>> x_projected = mpca.transform(x)
      >>> x_projected.shape
      (40, 7452)
      >>> x_projected = mpca.transform(x)
      >>> x_projected.shape
      (40, 50)
      >>> x_rec = mpca.inverse_transform(x_projected)
      >>> x_rec.shape
      (40, 20, 25, 20)
docstr: Perform dimension reduction on x
  
  Args:
      x (array-like tensor): Data to perform dimension reduction, shape (n_samples, I_1, I_2, ..., I_N).
  
  Returns:
      array-like tensor:
          Projected data in lower dimension, shape (n_samples, P_1, P_2, ..., P_N) if self.vectorize==False.
          If self.vectorize==True, features will be sorted based on their explained variance ratio, shape
          (n_samples, P_1 * P_2 * ... * P_N) if self.n_components is None, and shape (n_samples, n_components)
          if self.n_component is a valid integer.
docstr_tok: ["Perform","dimension","reduction","on","x","Args",":","x","(","array-like","tensor",")",":","Data","to","perform","dimension","reduction",",","shape","(","n_samples",",","I_1",",","I_2",",","...",",","I_N",")",".","Returns",":","array-like","tensor",":","Projected","data","in","lower","dimension",",","shape","(","n_samples",",","P_1",",","P_2",",","...",",","P_N",")","if","self.vectorize==False",".","If","self.vectorize==True",",","features","will","be","sorted","based","on","their","explained","variance","ratio",",","shape","(","n_samples",",","P_1","*","P_2","*","...","*","P_N",")","if","self.n_components","is","None",",","and","shape","(","n_samples",",","n_components",")","if","self.n_component","is","a","valid","integer","."]
code: def transform(self, x):
      """Perform dimension reduction on x
  
      Args:
          x (array-like tensor): Data to perform dimension reduction, shape (n_samples, I_1, I_2, ..., I_N).
  
      Returns:
          array-like tensor:
              Projected data in lower dimension, shape (n_samples, P_1, P_2, ..., P_N) if self.vectorize==False.
              If self.vectorize==True, features will be sorted based on their explained variance ratio, shape
              (n_samples, P_1 * P_2 * ... * P_N) if self.n_components is None, and shape (n_samples, n_components)
              if self.n_component is a valid integer.
      """
      # reshape x to shape (1, I_1, I_2, ..., I_N) if x in shape (I_1, I_2, ..., I_N), i.e. n_samples = 1
      if x.ndim == self.n_dims - 1:
          x = x.reshape((1,) + x.shape)
      _check_tensor_dim_shape(x, self.n_dims, self.shape_in)
      x = x - self.mean_
  
      # projected tensor in lower dimensions
      x_projected = multi_mode_dot(
          x, self.proj_mats, modes=[m for m in range(1, self.n_dims)]
      )
  
      if self.vectorize:
          x_projected = unfold(x_projected, mode=0)
          x_projected = x_projected[:, self.idx_order]
          if isinstance(self.n_components, int):
              n_features = int(np.prod(self.shape_out))
              if self.n_components > n_features:
                  self.n_components = n_features
                  warn_msg = "n_components exceeds the maximum number, all features will be returned."
                  logging.warning(warn_msg)
                  warnings.warn(warn_msg)
              x_projected = x_projected[:, : self.n_components]
  
      return x_projected
  
imports: import logging
  import warnings
  import numpy
  from numpy.linalg import multi_dot
  from scipy import linalg
  from sklearn.base import BaseEstimator, TransformerMixin
  from sklearn.metrics.pairwise import pairwise_kernels
  from sklearn.preprocessing import KernelCenterer, LabelBinarizer
  from sklearn.utils.validation import check_is_fitted
  from tensorly.base import fold, unfold
  from tensorly.tenalg import multi_mode_dot
label_desc: Use image node_id 4 for calling the MPCA obj's underlying member method code with example usage: obj.transform(x) and returns: x_projected