class_docstr: Pipeline of transformers and a clusterer.
  
  The `ClustererPipeline` compositor chains transformers and a single clusterer.
  The pipeline is constructed with a list of sktime transformers, plus a clusterer,
      i.e., estimators following the BaseTransformer resp BaseClusterer interface.
  The transformer list can be unnamed - a simple list of transformers -
      or string named - a list of pairs of string, estimator.
  
  For a list of transformers `trafo1`, `trafo2`, ..., `trafoN` and a clusterer `clst`,
      the pipeline behaves as follows:
  `fit(X, y)` - changes styte by running `trafo1.fit_transform` on `X`,
      them `trafo2.fit_transform` on the output of `trafo1.fit_transform`, etc
      sequentially, with `trafo[i]` receiving the output of `trafo[i-1]`,
      and then running `clst.fit` with `X` being the output of `trafo[N]`,
      and `y` identical with the input to `self.fit`
  `predict(X)` - result is of executing `trafo1.transform`, `trafo2.transform`, etc
      with `trafo[i].transform` input = output of `trafo[i-1].transform`,
      then running `clst.predict` on the output of `trafoN.transform`,
      and returning the output of `clst.predict`
  `predict_proba(X)` - result is of executing `trafo1.transform`, `trafo2.transform`,
      etc, with `trafo[i].transform` input = output of `trafo[i-1].transform`,
      then running `clst.predict_proba` on the output of `trafoN.transform`,
      and returning the output of `clst.predict_proba`
  
  `get_params`, `set_params` uses `sklearn` compatible nesting interface
      if list is unnamed, names are generated as names of classes
      if names are non-unique, `f"_{str(i)}"` is appended to each name string
          where `i` is the total count of occurrence of a non-unique string
          inside the list of names leading up to it (inclusive)
  
  `ClustererPipeline` can also be created by using the magic multiplication
      on any clusterer, i.e., if `my_clst` inherits from `BaseClusterer`,
          and `my_trafo1`, `my_trafo2` inherit from `BaseTransformer`, then,
          for instance, `my_trafo1 * my_trafo2 * my_clst`
          will result in the same object as  obtained from the constructor
          `ClustererPipeline(clusterer=my_clst, transformers=[my_trafo1, my_trafo2])`
      magic multiplication can also be used with (str, transformer) pairs,
          as long as one element in the chain is a transformer
  
  Parameters
  ----------
  clusterer : sktime clusterer, i.e., estimator inheriting from BaseClusterer
      this is a "blueprint" clusterer, state does not change when `fit` is called
  transformers : list of sktime transformers, or
      list of tuples (str, transformer) of sktime transformers
      these are "blueprint" transformers, states do not change when `fit` is called
  
  Attributes
  ----------
  clusterer_ : sktime clusterer, clone of clusterer in `clusterer`
      this clone is fitted in the pipeline when `fit` is called
  transformers_ : list of tuples (str, transformer) of sktime transformers
      clones of transformers in `transformers` which are fitted in the pipeline
      is always in (str, transformer) format, even if transformers is just a list
      strings not passed in transformers are unique generated strings
      i-th transformer in `transformers_` is clone of i-th in `transformers`
  
  Examples
  --------
  >>> from sktime.transformations.panel.pca import PCATransformer
  >>> from sktime.clustering.k_means import TimeSeriesKMeans
  >>> from sktime.datasets import load_unit_test
  >>> from sktime.clustering.compose import ClustererPipeline
  >>> X_train, y_train = load_unit_test(split="train") # doctest: +SKIP
  >>> X_test, y_test = load_unit_test(split="test") # doctest: +SKIP
  >>> pipeline = ClustererPipeline(
  ...     TimeSeriesKMeans(), [PCATransformer()]
  ... ) # doctest: +SKIP
  >>> pipeline.fit(X_train, y_train) # doctest: +SKIP
  ClustererPipeline(...)
  >>> y_pred = pipeline.predict(X_test) # doctest: +SKIP
  
  Alternative construction via dunder method:
  
  >>> pipeline = PCATransformer() * TimeSeriesKMeans() # doctest: +SKIP
code: def _transformers(self):
      return self.transformers_._steps
  
imports: import numpy
  from sktime.base import _HeterogenousMetaEstimator
  from sktime.clustering.base import BaseClusterer
  from sktime.datatypes import convert_to
  from sktime.transformations.base import BaseTransformer
  from sktime.transformations.compose import TransformerPipeline
  from sktime.utils.sklearn import is_sklearn_clusterer
label_desc: Use image node_id 2 for calling the ClustererPipeline obj's underlying member method code with example usage: obj._transformers() and returns: self