class_docstr: Estimate clustering structure from vector array.
  
  OPTICS (Ordering Points To Identify the Clustering Structure), closely
  related to DBSCAN, finds core sample of high density and expands clusters
  from them [1]_. Unlike DBSCAN, keeps cluster hierarchy for a variable
  neighborhood radius. Better suited for usage on large datasets than the
  current sklearn implementation of DBSCAN.
  
  Clusters are then extracted using a DBSCAN-like method
  (cluster_method = 'dbscan') or an automatic
  technique proposed in [1]_ (cluster_method = 'xi').
  
  This implementation deviates from the original OPTICS by first performing
  k-nearest-neighborhood searches on all points to identify core sizes, then
  computing only the distances to unprocessed points when constructing the
  cluster order. Note that we do not employ a heap to manage the expansion
  candidates, so the time complexity will be O(n^2).
  
  Read more in the :ref:`User Guide <optics>`.
  
  Parameters
  ----------
  min_samples : int > 1 or float between 0 and 1, default=5
      The number of samples in a neighborhood for a point to be considered as
      a core point. Also, up and down steep regions can't have more than
      ``min_samples`` consecutive non-steep points. Expressed as an absolute
      number or a fraction of the number of samples (rounded to be at least
      2).
  
  max_eps : float, default=np.inf
      The maximum distance between two samples for one to be considered as
      in the neighborhood of the other. Default value of ``np.inf`` will
      identify clusters across all scales; reducing ``max_eps`` will result
      in shorter run times.
  
  metric : str or callable, default='minkowski'
      Metric to use for distance computation. Any metric from scikit-learn
      or scipy.spatial.distance can be used.
  
      If metric is a callable function, it is called on each
      pair of instances (rows) and the resulting value recorded. The callable
      should take two arrays as input and return one value indicating the
      distance between them. This works for Scipy's metrics, but is less
      efficient than passing the metric name as a string. If metric is
      "precomputed", `X` is assumed to be a distance matrix and must be
      square.
  
      Valid values for metric are:
  
      - from scikit-learn: ['cityblock', 'cosine', 'euclidean', 'l1', 'l2',
        'manhattan']
  
      - from scipy.spatial.distance: ['braycurtis', 'canberra', 'chebyshev',
        'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski',
        'mahalanobis', 'minkowski', 'rogerstanimoto', 'russellrao',
        'seuclidean', 'sokalmichener', 'sokalsneath', 'sqeuclidean',
        'yule']
  
      Sparse matrices are only supported by scikit-learn metrics.
      See the documentation for scipy.spatial.distance for details on these
      metrics.
  
      .. note::
         `'kulsinski'` is deprecated from SciPy 1.9 and will removed in SciPy 1.11.
  
  p : float, default=2
      Parameter for the Minkowski metric from
      :class:`~sklearn.metrics.pairwise_distances`. When p = 1, this is
      equivalent to using manhattan_distance (l1), and euclidean_distance
      (l2) for p = 2. For arbitrary p, minkowski_distance (l_p) is used.
  
  metric_params : dict, default=None
      Additional keyword arguments for the metric function.
  
  cluster_method : str, default='xi'
      The extraction method used to extract clusters using the calculated
      reachability and ordering. Possible values are "xi" and "dbscan".
  
  eps : float, default=None
      The maximum distance between two samples for one to be considered as
      in the neighborhood of the other. By default it assumes the same value
      as ``max_eps``.
      Used only when ``cluster_method='dbscan'``.
  
  xi : float between 0 and 1, default=0.05
      Determines the minimum steepness on the reachability plot that
      constitutes a cluster boundary. For example, an upwards point in the
      reachability plot is defined by the ratio from one point to its
      successor being at most 1-xi.
      Used only when ``cluster_method='xi'``.
  
  predecessor_correction : bool, default=True
      Correct clusters according to the predecessors calculated by OPTICS
      [2]_. This parameter has minimal effect on most datasets.
      Used only when ``cluster_method='xi'``.
  
  min_cluster_size : int > 1 or float between 0 and 1, default=None
      Minimum number of samples in an OPTICS cluster, expressed as an
      absolute number or a fraction of the number of samples (rounded to be
      at least 2). If ``None``, the value of ``min_samples`` is used instead.
      Used only when ``cluster_method='xi'``.
  
  algorithm : {'auto', 'ball_tree', 'kd_tree', 'brute'}, default='auto'
      Algorithm used to compute the nearest neighbors:
  
      - 'ball_tree' will use :class:`~sklearn.neighbors.BallTree`.
      - 'kd_tree' will use :class:`~sklearn.neighbors.KDTree`.
      - 'brute' will use a brute-force search.
      - 'auto' (default) will attempt to decide the most appropriate
        algorithm based on the values passed to :meth:`fit` method.
  
      Note: fitting on sparse input will override the setting of
      this parameter, using brute force.
  
  leaf_size : int, default=30
      Leaf size passed to :class:`~sklearn.neighbors.BallTree` or
      :class:`~sklearn.neighbors.KDTree`. This can affect the speed of the
      construction and query, as well as the memory required to store the
      tree. The optimal value depends on the nature of the problem.
  
  memory : str or object with the joblib.Memory interface, default=None
      Used to cache the output of the computation of the tree.
      By default, no caching is done. If a string is given, it is the
      path to the caching directory.
  
  n_jobs : int, default=None
      The number of parallel jobs to run for neighbors search.
      ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
      ``-1`` means using all processors. See :term:`Glossary <n_jobs>`
      for more details.
  
  Attributes
  ----------
  labels_ : ndarray of shape (n_samples,)
      Cluster labels for each point in the dataset given to fit().
      Noisy samples and points which are not included in a leaf cluster
      of ``cluster_hierarchy_`` are labeled as -1.
  
  reachability_ : ndarray of shape (n_samples,)
      Reachability distances per sample, indexed by object order. Use
      ``clust.reachability_[clust.ordering_]`` to access in cluster order.
  
  ordering_ : ndarray of shape (n_samples,)
      The cluster ordered list of sample indices.
  
  core_distances_ : ndarray of shape (n_samples,)
      Distance at which each sample becomes a core point, indexed by object
      order. Points which will never be core have a distance of inf. Use
      ``clust.core_distances_[clust.ordering_]`` to access in cluster order.
  
  predecessor_ : ndarray of shape (n_samples,)
      Point that a sample was reached from, indexed by object order.
      Seed points have a predecessor of -1.
  
  cluster_hierarchy_ : ndarray of shape (n_clusters, 2)
      The list of clusters in the form of ``[start, end]`` in each row, with
      all indices inclusive. The clusters are ordered according to
      ``(end, -start)`` (ascending) so that larger clusters encompassing
      smaller clusters come after those smaller ones. Since ``labels_`` does
      not reflect the hierarchy, usually
      ``len(cluster_hierarchy_) > np.unique(optics.labels_)``. Please also
      note that these indices are of the ``ordering_``, i.e.
      ``X[ordering_][start:end + 1]`` form a cluster.
      Only available when ``cluster_method='xi'``.
  
  n_features_in_ : int
      Number of features seen during :term:`fit`.
  
      .. versionadded:: 0.24
  
  feature_names_in_ : ndarray of shape (`n_features_in_`,)
      Names of features seen during :term:`fit`. Defined only when `X`
      has feature names that are all strings.
  
      .. versionadded:: 1.0
  
  See Also
  --------
  DBSCAN : A similar clustering for a specified neighborhood radius (eps).
      Our implementation is optimized for runtime.
  
  References
  ----------
  .. [1] Ankerst, Mihael, Markus M. Breunig, Hans-Peter Kriegel,
     and Jörg Sander. "OPTICS: ordering points to identify the clustering
     structure." ACM SIGMOD Record 28, no. 2 (1999): 49-60.
  
  .. [2] Schubert, Erich, Michael Gertz.
     "Improving the Cluster Structure Extracted from OPTICS Plots." Proc. of
     the Conference "Lernen, Wissen, Daten, Analysen" (LWDA) (2018): 318-329.
  
  Examples
  --------
  >>> from sklearn.cluster import OPTICS
  >>> import numpy as np
  >>> X = np.array([[1, 2], [2, 5], [3, 6],
  ...               [8, 7], [8, 8], [7, 3]])
  >>> clustering = OPTICS(min_samples=2).fit(X)
  >>> clustering.labels_
  array([0, 0, 0, 1, 1, 1])
  
  For a more detailed example see
  :ref:`sphx_glr_auto_examples_cluster_plot_optics.py`.
class_docstr_tok: ["Estimate","clustering","structure","from","vector","array",".","OPTICS","(","Ordering","Points","To","Identify","the","Clustering","Structure",")",",","closely","related","to","DBSCAN",",","finds","core","sample","of","high","density","and","expands","clusters","from","them","[","1","]","_",".","Unlike","DBSCAN",",","keeps","cluster","hierarchy","for","a","variable","neighborhood","radius",".","Better","suited","for","usage","on","large","datasets","than","the","current","sklearn","implementation","of","DBSCAN",".","Clusters","are","then","extracted","using","a","DBSCAN-like","method","(","cluster_method","=","'dbscan","'",")","or","an","automatic","technique","proposed","in","[","1","]","_","(","cluster_method","=","'xi","'",")",".","This","implementation","deviates","from","the","original","OPTICS","by","first","performing","k-nearest-neighborhood","searches","on","all","points","to","identify","core","sizes",",","then","computing","only","the","distances","to","unprocessed","points","when","constructing","the","cluster","order",".","Note","that","we","do","not","employ","a","heap","to","manage","the","expansion","candidates",",","so","the","time","complexity","will","be","O","(","n^2",")",".","Read","more","in","the",":","ref",":","`","User","Guide","<","optics",">","`",".","Parameters","--","--","--","--","--","min_samples",":","int",">","1","or","float","between","0","and","1",",","default=5","The","number","of","samples","in","a","neighborhood","for","a","point","to","be","considered","as","a","core","point",".","Also",",","up","and","down","steep","regions","ca","n't","have","more","than","``","min_samples","``","consecutive","non-steep","points",".","Expressed","as","an","absolute","number","or","a","fraction","of","the","number","of","samples","(","rounded","to","be","at","least","2",")",".","max_eps",":","float",",","default=np.inf","The","maximum","distance","between","two","samples","for","one","to","be","considered","as","in","the","neighborhood","of","the","other",".","Default","value","of","``","np.inf","``","will","identify","clusters","across","all","scales",";","reducing","``","max_eps","``","will","result","in","shorter","run","times",".","metric",":","str","or","callable",",","default='minkowski'","Metric","to","use","for","distance","computation",".","Any","metric","from","scikit-learn","or","scipy.spatial.distance","can","be","used",".","If","metric","is","a","callable","function",",","it","is","called","on","each","pair","of","instances","(","rows",")","and","the","resulting","value","recorded",".","The","callable","should","take","two","arrays","as","input","and","return","one","value","indicating","the","distance","between","them",".","This","works","for","Scipy","'s","metrics",",","but","is","less","efficient","than","passing","the","metric","name","as","a","string",".","If","metric","is","``","precomputed","''",",","`","X","`","is","assumed","to","be","a","distance","matrix","and","must","be","square",".","Valid","values","for","metric","are",":","-","from","scikit-learn",":","[","'cityblock","'",",","'cosine","'",",","'euclidean","'",",","'l1","'",",","'l2","'",",","'manhattan","'","]","-","from","scipy.spatial.distance",":","[","'braycurtis","'",",","'canberra","'",",","'chebyshev","'",",","'correlation","'",",","'dice","'",",","'hamming","'",",","'jaccard","'",",","'kulsinski","'",",","'mahalanobis","'",",","'minkowski","'",",","'rogerstanimoto","'",",","'russellrao","'",",","'seuclidean","'",",","'sokalmichener","'",",","'sokalsneath","'",",","'sqeuclidean","'",",","'yule","'","]","Sparse","matrices","are","only","supported","by","scikit-learn","metrics",".","See","the","documentation","for","scipy.spatial.distance","for","details","on","these","metrics",".","..","note",":",":","`","'kulsinski","'","`","is","deprecated","from","SciPy","1.9","and","will","removed","in","SciPy","1.11.","p",":","float",",","default=2","Parameter","for","the","Minkowski","metric","from",":","class",":","`","~sklearn.metrics.pairwise_distances","`",".","When","p","=","1",",","this","is","equivalent","to","using","manhattan_distance","(","l1",")",",","and","euclidean_distance","(","l2",")","for","p","=","2",".","For","arbitrary","p",",","minkowski_distance","(","l_p",")","is","used",".","metric_params",":","dict",",","default=None","Additional","keyword","arguments","for","the","metric","function",".","cluster_method",":","str",",","default='xi'","The","extraction","method","used","to","extract","clusters","using","the","calculated","reachability","and","ordering",".","Possible","values","are","``","xi","''","and","``","dbscan","''",".","eps",":","float",",","default=None","The","maximum","distance","between","two","samples","for","one","to","be","considered","as","in","the","neighborhood","of","the","other",".","By","default","it","assumes","the","same","value","as","``","max_eps","``",".","Used","only","when","``","cluster_method='dbscan","'","``",".","xi",":","float","between","0","and","1",",","default=0.05","Determines","the","minimum","steepness","on","the","reachability","plot","that","constitutes","a","cluster","boundary",".","For","example",",","an","upwards","point","in","the","reachability","plot","is","defined","by","the","ratio","from","one","point","to","its","successor","being","at","most","1-xi",".","Used","only","when","``","cluster_method='xi","'","``",".","predecessor_correction",":","bool",",","default=True","Correct","clusters","according","to","the","predecessors","calculated","by","OPTICS","[","2","]","_",".","This","parameter","has","minimal","effect","on","most","datasets",".","Used","only","when","``","cluster_method='xi","'","``",".","min_cluster_size",":","int",">","1","or","float","between","0","and","1",",","default=None","Minimum","number","of","samples","in","an","OPTICS","cluster",",","expressed","as","an","absolute","number","or","a","fraction","of","the","number","of","samples","(","rounded","to","be","at","least","2",")",".","If","``","None","``",",","the","value","of","``","min_samples","``","is","used","instead",".","Used","only","when","``","cluster_method='xi","'","``",".","algorithm",":","{","'auto","'",",","'ball_tree","'",",","'kd_tree","'",",","'brute","'","}",",","default='auto'","Algorithm","used","to","compute","the","nearest","neighbors",":","-","'ball_tree","'","will","use",":","class",":","`","~sklearn.neighbors.BallTree","`",".","-","'kd_tree","'","will","use",":","class",":","`","~sklearn.neighbors.KDTree","`",".","-","'brute","'","will","use","a","brute-force","search",".","-","'auto","'","(","default",")","will","attempt","to","decide","the","most","appropriate","algorithm","based","on","the","values","passed","to",":","meth",":","`","fit","`","method",".","Note",":","fitting","on","sparse","input","will","override","the","setting","of","this","parameter",",","using","brute","force",".","leaf_size",":","int",",","default=30","Leaf","size","passed","to",":","class",":","`","~sklearn.neighbors.BallTree","`","or",":","class",":","`","~sklearn.neighbors.KDTree","`",".","This","can","affect","the","speed","of","the","construction","and","query",",","as","well","as","the","memory","required","to","store","the","tree",".","The","optimal","value","depends","on","the","nature","of","the","problem",".","memory",":","str","or","object","with","the","joblib.Memory","interface",",","default=None","Used","to","cache","the","output","of","the","computation","of","the","tree",".","By","default",",","no","caching","is","done",".","If","a","string","is","given",",","it","is","the","path","to","the","caching","directory",".","n_jobs",":","int",",","default=None","The","number","of","parallel","jobs","to","run","for","neighbors","search",".","``","None","``","means","1","unless","in","a",":","obj",":","`","joblib.parallel_backend","`","context",".","``","-1","``","means","using","all","processors",".","See",":","term",":","`","Glossary","<","n_jobs",">","`","for","more","details",".","Attributes","--","--","--","--","--","labels_",":","ndarray","of","shape","(","n_samples",",",")","Cluster","labels","for","each","point","in","the","dataset","given","to","fit","(",")",".","Noisy","samples","and","points","which","are","not","included","in","a","leaf","cluster","of","``","cluster_hierarchy_","``","are","labeled","as","-1.","reachability_",":","ndarray","of","shape","(","n_samples",",",")","Reachability","distances","per","sample",",","indexed","by","object","order",".","Use","``","clust.reachability_","[","clust.ordering_","]","``","to","access","in","cluster","order",".","ordering_",":","ndarray","of","shape","(","n_samples",",",")","The","cluster","ordered","list","of","sample","indices",".","core_distances_",":","ndarray","of","shape","(","n_samples",",",")","Distance","at","which","each","sample","becomes","a","core","point",",","indexed","by","object","order",".","Points","which","will","never","be","core","have","a","distance","of","inf",".","Use","``","clust.core_distances_","[","clust.ordering_","]","``","to","access","in","cluster","order",".","predecessor_",":","ndarray","of","shape","(","n_samples",",",")","Point","that","a","sample","was","reached","from",",","indexed","by","object","order",".","Seed","points","have","a","predecessor","of","-1.","cluster_hierarchy_",":","ndarray","of","shape","(","n_clusters",",","2",")","The","list","of","clusters","in","the","form","of","``","[","start",",","end","]","``","in","each","row",",","with","all","indices","inclusive",".","The","clusters","are","ordered","according","to","``","(","end",",","-start",")","``","(","ascending",")","so","that","larger","clusters","encompassing","smaller","clusters","come","after","those","smaller","ones",".","Since","``","labels_","``","does","not","reflect","the","hierarchy",",","usually","``","len","(","cluster_hierarchy_",")",">","np.unique","(","optics.labels_",")","``",".","Please","also","note","that","these","indices","are","of","the","``","ordering_","``",",","i.e",".","``","X","[","ordering_","]","[","start",":","end","+","1","]","``","form","a","cluster",".","Only","available","when","``","cluster_method='xi","'","``",".","n_features_in_",":","int","Number","of","features","seen","during",":","term",":","`","fit","`",".","..","versionadded",":",":","0.24","feature_names_in_",":","ndarray","of","shape","(","`","n_features_in_","`",",",")","Names","of","features","seen","during",":","term",":","`","fit","`",".","Defined","only","when","`","X","`","has","feature","names","that","are","all","strings",".","..","versionadded",":",":","1.0","See","Also","--","--","--","--","DBSCAN",":","A","similar","clustering","for","a","specified","neighborhood","radius","(","eps",")",".","Our","implementation","is","optimized","for","runtime",".","References","--","--","--","--","--","..","[","1","]","Ankerst",",","Mihael",",","Markus","M.","Breunig",",","Hans-Peter","Kriegel",",","and","J\u00f6rg","Sander",".","``","OPTICS",":","ordering","points","to","identify","the","clustering","structure",".","''","ACM","SIGMOD","Record","28",",","no",".","2","(","1999",")",":","49-60",".","..","[","2","]","Schubert",",","Erich",",","Michael","Gertz",".","``","Improving","the","Cluster","Structure","Extracted","from","OPTICS","Plots",".","''","Proc",".","of","the","Conference","``","Lernen",",","Wissen",",","Daten",",","Analysen","''","(","LWDA",")","(","2018",")",":","318-329",".","Examples","--","--","--","--",">",">",">","from","sklearn.cluster","import","OPTICS",">",">",">","import","numpy","as","np",">",">",">","X","=","np.array","(","[","[","1",",","2","]",",","[","2",",","5","]",",","[","3",",","6","]",",","...","[","8",",","7","]",",","[","8",",","8","]",",","[","7",",","3","]","]",")",">",">",">","clustering","=","OPTICS","(","min_samples=2",")",".fit","(","X",")",">",">",">","clustering.labels_","array","(","[","0",",","0",",","0",",","1",",","1",",","1","]",")","For","a","more","detailed","example","see",":","ref",":","`","sphx_glr_auto_examples_cluster_plot_optics.py","`","."]
docstr: Perform OPTICS clustering.
  
  Extracts an ordered list of points and reachability distances, and
  performs initial clustering using ``max_eps`` distance specified at
  OPTICS object instantiation.
  
  Parameters
  ----------
  X : {ndarray, sparse matrix} of shape (n_samples, n_features), or                 (n_samples, n_samples) if metric='precomputed'
      A feature array, or array of distances between samples if
      metric='precomputed'. If a sparse matrix is provided, it will be
      converted into CSR format.
  
  y : Ignored
      Not used, present for API consistency by convention.
  
  Returns
  -------
  self : object
      Returns a fitted instance of self.
docstr_tok: ["Perform","OPTICS","clustering",".","Extracts","an","ordered","list","of","points","and","reachability","distances",",","and","performs","initial","clustering","using","``","max_eps","``","distance","specified","at","OPTICS","object","instantiation",".","Parameters","--","--","--","--","--","X",":","{","ndarray",",","sparse","matrix","}","of","shape","(","n_samples",",","n_features",")",",","or","(","n_samples",",","n_samples",")","if","metric='precomputed'","A","feature","array",",","or","array","of","distances","between","samples","if","metric='precomputed","'",".","If","a","sparse","matrix","is","provided",",","it","will","be","converted","into","CSR","format",".","y",":","Ignored","Not","used",",","present","for","API","consistency","by","convention",".","Returns","--","--","--","-","self",":","object","Returns","a","fitted","instance","of","self","."]
code: def fit(self, X, y=None):
      """Perform OPTICS clustering.
  
          Extracts an ordered list of points and reachability distances, and
          performs initial clustering using ``max_eps`` distance specified at
          OPTICS object instantiation.
  
          Parameters
          ----------
          X : {ndarray, sparse matrix} of shape (n_samples, n_features), or \
                  (n_samples, n_samples) if metric='precomputed'
              A feature array, or array of distances between samples if
              metric='precomputed'. If a sparse matrix is provided, it will be
              converted into CSR format.
  
          y : Ignored
              Not used, present for API consistency by convention.
  
          Returns
          -------
          self : object
              Returns a fitted instance of self.
          """
      dtype = (
          bool if self.metric in PAIRWISE_BOOLEAN_FUNCTIONS else float
      )
      if dtype == bool and X.dtype != bool:
          msg = (
              "Data will be converted to boolean for"
              f" metric {self.metric}, to avoid this warning,"
              " you may convert the data prior to calling fit."
          )
          warnings.warn(msg, DataConversionWarning)
  
      X = self._validate_data(X, dtype=dtype, accept_sparse="csr")
      if self.metric == "precomputed" and issparse(X):
          with warnings.catch_warnings():
              warnings.simplefilter("ignore", SparseEfficiencyWarning)
              # Set each diagonal to an explicit value so each point is its
              # own neighbor
              X.setdiag(X.diagonal())
      memory = check_memory(self.memory)
  
      (
          self.ordering_,
          self.core_distances_,
          self.reachability_,
          self.predecessor_,
      ) = memory.cache(compute_optics_graph)(
          X=X,
          min_samples=self.min_samples,
          algorithm=self.algorithm,
          leaf_size=self.leaf_size,
          metric=self.metric,
          metric_params=self.metric_params,
          p=self.p,
          n_jobs=self.n_jobs,
          max_eps=self.max_eps,
      )
  
      # Extract clusters from the calculated orders and reachability
      if self.cluster_method == "xi":
          labels_, clusters_ = cluster_optics_xi(
              reachability=self.reachability_,
              predecessor=self.predecessor_,
              ordering=self.ordering_,
              min_samples=self.min_samples,
              min_cluster_size=self.min_cluster_size,
              xi=self.xi,
              predecessor_correction=self.predecessor_correction,
          )
          self.cluster_hierarchy_ = clusters_
      elif self.cluster_method == "dbscan":
          if self.eps is None:
              eps = self.max_eps
          else:
              eps = self.eps
  
          if eps > self.max_eps:
              raise ValueError(
                  "Specify an epsilon smaller than %s. Got %s."
                  % (self.max_eps, eps)
              )
  
          labels_ = cluster_optics_dbscan(
              reachability=self.reachability_,
              core_distances=self.core_distances_,
              ordering=self.ordering_,
              eps=eps,
          )
  
      self.labels_ = labels_
      return self
  
imports: import warnings
  from numbers import Integral, Real
  import numpy
  from scipy.sparse import SparseEfficiencyWarning, issparse
  from ..base import BaseEstimator, ClusterMixin, _fit_context
  from ..exceptions import DataConversionWarning
  from ..metrics import pairwise_distances
  from ..metrics.pairwise import _VALID_METRICS, PAIRWISE_BOOLEAN_FUNCTIONS
  from ..neighbors import NearestNeighbors
  from ..utils import gen_batches, get_chunk_n_rows
  from ..utils._param_validation import HasMethods, Interval, RealNotInt, StrOptions, validate_params
  from ..utils.validation import check_memory
label_desc: Use image node_id 2 for calling the OPTICS obj's underlying member method code with example usage: obj.fit(X, y) and returns: self