class_docstr: Wrap ``VotingClassifier`` and ``VotingRegressor`` from scikit-learn.
  
  Note that this class does not inherit from the ``Learner`` class but rather
  uses different ``Learner`` instances underlyingly.
  
  Parameters
  ----------
  learner_names : List[str]
      List of the learner names that will participate in the voting process.
  voting : Optional[str], default="hard"
      One of "hard" or "soft". If "hard", the predicted class labels
      are used for majority rule voting. If "soft", the predicted class
      label is based on the argmax of the sums of the predicted
      probabilities from each of the underlying learnrs. This parameter
      is only relevant for classification.
  custom_learner_path : Optional[:class:`skll.types.PathOrStr`], default=None
      Path to a Python file containing the definitions of any custom
      learners. Any and all custom learners in ``estimator_names`` must
      be defined in this file. If the custom learner does not inherit
      from an already existing scikit-learn estimator, it must explicitly
      define an `_estimator_type` attribute indicating whether it's a
      "classifier" or a "regressor".
  feature_scaling : str, default="none"
      How to scale the features, if at all for each estimator. Options are
      -  "with_std": scale features using the standard deviation
      -  "with_mean": center features using the mean
      -  "both": do both scaling as well as centering
      -  "none": do neither scaling nor centering
  pos_label : Optional[:class:`skll.types.LabelType`], default=None
      A string denoting the label of the class to be
      treated as the positive class in a binary classification
      setting, for each estimator. If ``None``, the class represented
      by the label that appears second when sorted is chosen as the
      positive class. For example, if the two labels in data are "A"
      and "B" and ``pos_label`` is not specified, "B" will
      be chosen as the positive class.
  min_feature_count : int, default=1
      The minimum number of examples a feature must have a nonzero
      value in to be included, for each estimator.
  model_kwargs_list : Optional[List[Dict[str, Any]]], default=None
      A list of dictionaries of keyword arguments to pass to the
      initializer for each of the estimators. There's a one-to-one
      correspondence between the order of this list and the order
      of the ``learner_names`` list.
  sampler_list : Optional[List[str]], default=None
      The samplers to use for kernel approximation, if desired, for each
      estimator. Valid values are:
      -  "AdditiveChi2Sampler"
      -  "Nystroem"
      -  "RBFSampler"
      -  "SkewedChi2Sampler"
      There's a one-to-one correspondence between the order of this list
      and the order of the ``learner_names`` list.
  sampler_kwargs_list : Optional[List[Dict[str, Any]]], default=None
      A list of dictionaries of keyword arguments to pass to the
      initializer for the specified sampler, one per estimator.
      There's a one-to-one correspondence between the order of this
      list and the order of the ``learner_names`` list.
  logger : Optional[logging.Logger], default=None
      A logging object. If ``None`` is passed, get logger from ``__name__``.
docstr: Train the voting meta-estimator.
  
  First, we train each of the underlying estimators (represented by
  a skll ``Learner``), possibly with grid search. Then, we instantiate
  a ``VotingClassifier`` or ``VotingRegressor`` as appropriate with the
  scikit-learn ``Pipeline`` stored in the ``pipeline`` attribute
  of each trained ``Learner`` instance as the estimator. Finally,
  we call ``fit()`` on the ``VotingClassifier`` or ``VotingRegressor``
  instance. We follow this process because it allows us to use grid
  search to find good hyperparameter values for our underlying learners
  before passing them to the meta-estimator AND because it allows us to
  use SKLL featuresets and do all of the same pre-processing when
  doing inference.
  
  The trained meta-estimator is saved in the ``_model`` attribute.
  Nothing is returned.
  
  Parameters
  ----------
  examples : :class:`skll.data.featureset.FeatureSet`
      The ``FeatureSet`` instance to use for training.
  param_grid_list : Optional[List[Dict[str, Any]]], default=None
      The list of parameter grids to search through for grid
      search, one for each underlying learner. The order of
      the dictionaries should correspond to the order in
      which the underlying estimators were specified when the
      ``VotingLearner`` was instantiated. If ``None``, the default
      parameter grids will be used for the underlying estimators.
  grid_search_folds : Union[int, :class:`skll.types.FoldMapping`], default=5
      The number of folds to use when doing the grid search
      for each of the underlying learners, or a mapping from
      example IDs to folds.
  grid_search : bool, default=True
      Should we use grid search when training each underlying learner?
  grid_objective : Optional[str], default=None
      The name of the objective function to use when
      doing the grid search for each underlying learner.
      Must be specified if ``grid_search`` is ``True``.
  grid_jobs : Optional[int], default=None
      The number of jobs to run in parallel when doing the
      grid search for each underlying learner. If ``None`` or 0,
      the number of grid search folds will be used.
  shuffle : bool, default=False
      Shuffle examples (e.g., for grid search CV.)
docstr_tok: ["Train","the","voting","meta-estimator",".","First",",","we","train","each","of","the","underlying","estimators","(","represented","by","a","skll","``","Learner","``",")",",","possibly","with","grid","search",".","Then",",","we","instantiate","a","``","VotingClassifier","``","or","``","VotingRegressor","``","as","appropriate","with","the","scikit-learn","``","Pipeline","``","stored","in","the","``","pipeline","``","attribute","of","each","trained","``","Learner","``","instance","as","the","estimator",".","Finally",",","we","call","``","fit","(",")","``","on","the","``","VotingClassifier","``","or","``","VotingRegressor","``","instance",".","We","follow","this","process","because","it","allows","us","to","use","grid","search","to","find","good","hyperparameter","values","for","our","underlying","learners","before","passing","them","to","the","meta-estimator","AND","because","it","allows","us","to","use","SKLL","featuresets","and","do","all","of","the","same","pre-processing","when","doing","inference",".","The","trained","meta-estimator","is","saved","in","the","``","_model","``","attribute",".","Nothing","is","returned",".","Parameters","--","--","--","--","--","examples",":",":","class",":","`","skll.data.featureset.FeatureSet","`","The","``","FeatureSet","``","instance","to","use","for","training",".","param_grid_list",":","Optional","[","List","[","Dict","[","str",",","Any","]","]","]",",","default=None","The","list","of","parameter","grids","to","search","through","for","grid","search",",","one","for","each","underlying","learner",".","The","order","of","the","dictionaries","should","correspond","to","the","order","in","which","the","underlying","estimators","were","specified","when","the","``","VotingLearner","``","was","instantiated",".","If","``","None","``",",","the","default","parameter","grids","will","be","used","for","the","underlying","estimators",".","grid_search_folds",":","Union","[","int",",",":","class",":","`","skll.types.FoldMapping","`","]",",","default=5","The","number","of","folds","to","use","when","doing","the","grid","search","for","each","of","the","underlying","learners",",","or","a","mapping","from","example","IDs","to","folds",".","grid_search",":","bool",",","default=True","Should","we","use","grid","search","when","training","each","underlying","learner","?","grid_objective",":","Optional","[","str","]",",","default=None","The","name","of","the","objective","function","to","use","when","doing","the","grid","search","for","each","underlying","learner",".","Must","be","specified","if","``","grid_search","``","is","``","True","``",".","grid_jobs",":","Optional","[","int","]",",","default=None","The","number","of","jobs","to","run","in","parallel","when","doing","the","grid","search","for","each","underlying","learner",".","If","``","None","``","or","0",",","the","number","of","grid","search","folds","will","be","used",".","shuffle",":","bool",",","default=False","Shuffle","examples","(","e.g.",",","for","grid","search","CV",".",")"]
code: def train(
      self,
      examples: FeatureSet,
      param_grid_list: Optional[List[Dict[str, Any]]] = None,
      grid_search_folds: Union[int, FoldMapping] = 5,
      grid_search: bool = True,
      grid_objective: Optional[str] = None,
      grid_jobs: Optional[int] = None,
      shuffle: bool = False,
  ) -> None:
      """
      Train the voting meta-estimator.
  
      First, we train each of the underlying estimators (represented by
      a skll ``Learner``), possibly with grid search. Then, we instantiate
      a ``VotingClassifier`` or ``VotingRegressor`` as appropriate with the
      scikit-learn ``Pipeline`` stored in the ``pipeline`` attribute
      of each trained ``Learner`` instance as the estimator. Finally,
      we call ``fit()`` on the ``VotingClassifier`` or ``VotingRegressor``
      instance. We follow this process because it allows us to use grid
      search to find good hyperparameter values for our underlying learners
      before passing them to the meta-estimator AND because it allows us to
      use SKLL featuresets and do all of the same pre-processing when
      doing inference.
  
      The trained meta-estimator is saved in the ``_model`` attribute.
      Nothing is returned.
  
      Parameters
      ----------
      examples : :class:`skll.data.featureset.FeatureSet`
          The ``FeatureSet`` instance to use for training.
      param_grid_list : Optional[List[Dict[str, Any]]], default=None
          The list of parameter grids to search through for grid
          search, one for each underlying learner. The order of
          the dictionaries should correspond to the order in
          which the underlying estimators were specified when the
          ``VotingLearner`` was instantiated. If ``None``, the default
          parameter grids will be used for the underlying estimators.
      grid_search_folds : Union[int, :class:`skll.types.FoldMapping`], default=5
          The number of folds to use when doing the grid search
          for each of the underlying learners, or a mapping from
          example IDs to folds.
      grid_search : bool, default=True
          Should we use grid search when training each underlying learner?
      grid_objective : Optional[str], default=None
          The name of the objective function to use when
          doing the grid search for each underlying learner.
          Must be specified if ``grid_search`` is ``True``.
      grid_jobs : Optional[int], default=None
          The number of jobs to run in parallel when doing the
          grid search for each underlying learner. If ``None`` or 0,
          the number of grid search folds will be used.
      shuffle : bool, default=False
          Shuffle examples (e.g., for grid search CV.)
      """
      if param_grid_list is None:
          self._param_grids = []
      else:
          if not isinstance(param_grid_list, list):
              raise ValueError(
                  f"`param_grid_list` should be a list of dictionaries, "
                  f"you specified: {param_grid_list}"
              )
          else:
              self._param_grids = param_grid_list
  
      # train each of the underlying estimators with grid search, if required;
      # basically, we are just running grid search to find good hyperparameter
      # values that we can then pass to scikit-learn below
      for learner, param_grid in zip_longest(
          self.learners, self._param_grids
      ):
          _ = learner.train(
              examples,
              grid_search=grid_search,
              grid_objective=grid_objective,
              param_grid=param_grid,
              grid_search_folds=grid_search_folds,
              grid_jobs=grid_jobs,
              shuffle=shuffle,
          )
  
      # once we have our instantiated learners, we use their `pipeline`
      # attribute as the input estimators to the specific voting learner type
      estimators = list(
          zip(
              self._learner_names,
              [learner.pipeline for learner in self.learners],
          )
      )
      if self.learner_type == "classifier":
          self._model_type = VotingClassifier
          model_kwargs = {"voting": self.voting}
      else:
          self._model_type = VotingRegressor
          model_kwargs = {}
      meta_learner = self.model_type(estimators, **model_kwargs)
  
      # get the training features in the right dictionary format
      if isinstance(examples.vectorizer, DictVectorizer):
          X_train = examples.vectorizer.inverse_transform(
              examples.features
          )
  
      # since label dictionaries are identical for all underlying
      # learners, save it into a easier to access attribute
      self.label_dict = self.learners[0].label_dict
  
      # get the training labels in the right format too
      # NOTE: technically, we could also use a `LabelEncoder` here but
      # that may not account for passing `pos_label` above when
      # instantiating the learners so we stick with the label dict
      y_train: np.ndarray
      if examples.labels is not None:
          if self.learner_type == "classifier":
              y_train = np.array(
                  [self.label_dict[label] for label in examples.labels]
              )
          else:
              # for regressors, the labels are just the labels
              y_train = examples.labels
  
      # now we need to fit the actual meta learner which will also fit
      # clones of the underlying pipelines;
      # NOTE: this will *not* yield the same results as the case where we take
      # the predictions from the trained SKLL learners above and do the
      # voting ourselves. This is because SKLL learners do a lot of things
      # differently (e.g., shuffling before grid search) that can cause
      # differences in results and/or floating point precision.
      self._model = meta_learner.fit(X_train, y_train)
  
imports: import copy
  import logging
  from importlib import import_module
  from itertools import zip_longest
  from multiprocessing import cpu_count
  from typing import Any, Dict, List, Optional, Tuple, Union
  import joblib
  import numpy
  from sklearn.ensemble import VotingClassifier, VotingRegressor
  from sklearn.utils import shuffle
  from sklearn.utils.multiclass import type_of_target
  from skll.data import FeatureSet
  from skll.data.dict_vectorizer import DictVectorizer
  from skll.learner import Learner
  from skll.types import EvaluateTaskResults, FoldMapping, LabelType, LearningCurveSizes, PathOrStr, VotingCrossValidateTaskResults
  from skll.utils.constants import MAX_CONCURRENT_PROCESSES
  from .utils import _load_learner_from_disk, _save_learner_to_disk, add_unseen_labels, compute_evaluation_metrics, get_acceptable_classification_metrics, get_acceptable_regression_metrics, get_predictions, setup_cv_fold_iterator, setup_cv_split_iterator, train_and_score, write_predictions
label_desc: Use image node_id 9 for calling the VotingLearner obj's underlying member method code with example usage: obj.train(examples, param_grid_list, grid_search_folds, grid_search, grid_objective, grid_jobs, shuffle) without return types