text: # -*- coding: utf-8 -*-
  
  """This file is part of the TPOT library.
  
  TPOT was primarily developed at the University of Pennsylvania by:
      - Randal S. Olson (rso@randalolson.com)
      - Weixuan Fu (weixuanf@upenn.edu)
      - Daniel Angell (dpa34@drexel.edu)
      - and many more generous open source contributors
  
  TPOT is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as
  published by the Free Software Foundation, either version 3 of
  the License, or (at your option) any later version.
  
  TPOT is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with TPOT. If not, see <http://www.gnu.org/licenses/>.
  
  """
  
  import numpy as np
  from sklearn.base import BaseEstimator, TransformerMixin, ClassifierMixin
  from sklearn.utils import check_array
  
  
  class StackingEstimator(BaseEstimator, TransformerMixin):
      """Meta-transformer for adding predictions and/or class probabilities as synthetic feature(s).
  
      Parameters
      ----------
      estimator : object
          The base estimator from which the transformer is built.
      """
  
      def __init__(self, estimator):
          """Create a StackingEstimator object.
  
          Parameters
          ----------
          estimator: object with fit, predict, and predict_proba methods.
              The estimator to generate synthetic features from.
          """
          self.estimator = estimator
  
      def fit(self, X, y=None, **fit_params):
          """Fit the StackingEstimator meta-transformer.
  
          Parameters
          ----------
          X: array-like of shape (n_samples, n_features)
              The training input samples.
          y: array-like, shape (n_samples,)
              The target values (integers that correspond to classes in classification, real numbers in regression).
          fit_params:
              Other estimator-specific parameters.
  
          Returns
          -------
          self: object
              Returns a copy of the estimator
          """
          self.estimator.fit(X, y, **fit_params)
          return self
  
      def transform(self, X):
          """Transform data by adding two synthetic feature(s).
  
          Parameters
          ----------
          X: numpy ndarray, {n_samples, n_components}
              New data, where n_samples is the number of samples and n_components is the number of components.
  
          Returns
          -------
          X_transformed: array-like, shape (n_samples, n_features + 1) or (n_samples, n_features + 1 + n_classes) for classifier with predict_proba attribute
              The transformed feature set.
          """
          X = check_array(X)
          X_transformed = np.copy(X)
          # add class probabilities as a synthetic feature
          if issubclass(self.estimator.__class__, ClassifierMixin) and hasattr(self.estimator, 'predict_proba'):
              y_pred_proba = self.estimator.predict_proba(X)
              # check all values that should be not infinity or not NAN
              if np.all(np.isfinite(y_pred_proba)):
                  X_transformed = np.hstack((y_pred_proba, X))
  
          # add class prediction as a synthetic feature
          # TODO: note that if prediction is string, will make entire matrix
          # string, so instead we try to get the label index
          preds = self.estimator.predict(X)
          if not np.issubdtype(preds.dtype, np.number):
              classes_ = self.estimator.classes_.tolist()
              preds = [classes_.index(p) for p in preds]
          X_transformed = np.hstack((np.reshape(preds, (-1, 1)), X_transformed))
          return X_transformed
  