class_docstr: An Estimator which implements the exponentiated gradient reduction.
  
  The exponentiated gradient algorithm is described in detail by
  :footcite:t:`agarwal2018reductions`.
  
  Read more in the :ref:`User Guide <exponentiated_gradient>`.
  
  .. versionchanged:: 0.3.0
      Was a function before, not a class
  
  .. versionchanged:: 0.4.6
      Requires 0-1 labels for classification problems
  
  Parameters
  ----------
  estimator : estimator
      An estimator implementing methods :code:`fit(X, y, sample_weight)` and
      :code:`predict(X)`, where `X` is the matrix of features, `y` is the
      vector of labels (binary classification) or continuous values
      (regression), and `sample_weight` is a vector of weights.
      In binary classification labels `y` and predictions returned by
      :code:`predict(X)` are either 0 or 1.
      In regression values `y` and predictions are continuous.
  constraints : fairlearn.reductions.Moment
      The fairness constraints expressed as a :class:`~Moment`.
  objective : fairlearn.reductions.Moment
      The objective expressed as a :class:`~Moment`. The default is
      :code:`ErrorRate()` for binary classification and
      :code:`MeanLoss(...)` for regression.
  eps : float
      Allowed fairness constraint violation; the solution is guaranteed to
      have the error within :code:`2*best_gap` of the best error under
      constraint `eps`; the constraint violation is at most
      :code:`2*(eps+best_gap)`.
  
      .. versionchanged:: 0.5.0
          :code:`eps` is now only responsible for setting the L1 norm bound
          in the optimization
  max_iter : int
      Maximum number of iterations
  
      .. versionadded:: 0.5.0
          Used to be :code:`T`
  
  nu : float
      Convergence threshold for the duality gap, corresponding to a
      conservative automatic setting based on the statistical uncertainty
      in measuring classification error
  
  eta0 : float
      Initial setting of the learning rate
  
      .. versionadded:: 0.5.0
          Used to be :code:`eta_mul`
  
  run_linprog_step : bool
      if True each step of exponentiated gradient is followed by the saddle
      point optimization over the convex hull of classifiers returned so
      far; default True
  
      .. versionadded:: 0.5.0
  
  sample_weight_name : str
      Name of the argument to `estimator.fit()` which supplies the sample weights
      (defaults to `sample_weight`)
  
      .. versionadded:: 0.5.0
docstr: Return a fair classifier under specified fairness constraints.
  
  Parameters
  ----------
  X : numpy.ndarray or pandas.DataFrame
      Feature data
  y : numpy.ndarray, pandas.DataFrame, pandas.Series, or list
      Label vector
docstr_tok: ["Return","a","fair","classifier","under","specified","fairness","constraints",".","Parameters","--","--","--","--","--","X",":","numpy.ndarray","or","pandas.DataFrame","Feature","data","y",":","numpy.ndarray",",","pandas.DataFrame",",","pandas.Series",",","or","list","Label","vector"]
code: def fit(self, X, y, **kwargs):
      """Return a fair classifier under specified fairness constraints.
  
      Parameters
      ----------
      X : numpy.ndarray or pandas.DataFrame
          Feature data
      y : numpy.ndarray, pandas.DataFrame, pandas.Series, or list
          Label vector
      """
      self.lambda_vecs_EG_ = pd.DataFrame()
      self.lambda_vecs_LP_ = pd.DataFrame()
      self.lambda_vecs_ = pd.DataFrame()
  
      logger.debug("...Exponentiated Gradient STARTING")
  
      B = 1 / self.eps
      lagrangian = _Lagrangian(
          X=X,
          y=y,
          estimator=self.estimator,
          constraints=self.constraints,
          B=B,
          objective=self.objective,
          sample_weight_name=self.sample_weight_name,
          **kwargs,
      )
  
      theta = pd.Series(0, lagrangian.constraints.index)
      Qsum = pd.Series(dtype="float64")
      gaps_EG = []
      gaps = []
      Qs = []
  
      last_regret_checked = _REGRET_CHECK_START_T
      last_gap = np.PINF
      for t in range(0, self.max_iter):
          logger.debug("...iter=%03d", t)
  
          # set lambdas for every constraint
          lambda_vec = B * np.exp(theta) / (1 + np.exp(theta).sum())
          self.lambda_vecs_EG_[t] = lambda_vec
          lambda_EG = self.lambda_vecs_EG_.mean(axis=1)
  
          # select classifier according to best_h method
          h, h_idx = lagrangian.best_h(lambda_vec)
  
          if t == 0:
              if self.nu is None:
                  self.nu = (
                      _ACCURACY_MUL
                      * (h(X) - self.constraints._y_as_series)
                      .abs()
                      .std()
                      / np.sqrt(self.constraints.total_samples)
                  )
              eta = self.eta0 / B
              logger.debug(
                  "...eps=%.3f, B=%.1f, nu=%.6f, max_iter=%d",
                  self.eps,
                  B,
                  self.nu,
                  self.max_iter,
              )
  
          if h_idx not in Qsum.index:
              Qsum.at[h_idx] = 0.0
          Qsum[h_idx] += 1.0
          gamma = lagrangian.gammas[h_idx]
          Q_EG = Qsum / Qsum.sum()
          result_EG = lagrangian.eval_gap(Q_EG, lambda_EG, self.nu)
          gap_EG = result_EG.gap()
          gaps_EG.append(gap_EG)
  
          if t == 0 or not self.run_linprog_step:
              gap_LP = np.PINF
          else:
              # saddle point optimization over the convex hull of
              # classifiers returned so far
              (
                  Q_LP,
                  self.lambda_vecs_LP_[t],
                  result_LP,
              ) = lagrangian.solve_linprog(self.nu)
              gap_LP = result_LP.gap()
  
          # keep values from exponentiated gradient or linear programming
          if gap_EG < gap_LP:
              Qs.append(Q_EG)
              gaps.append(gap_EG)
          else:
              Qs.append(Q_LP)
              gaps.append(gap_LP)
  
          logger.debug(
              "%seta=%.6f, L_low=%.3f, L=%.3f, L_high=%.3f, gap=%.6f, disp=%.3f, "
              "err=%.3f, gap_LP=%.6f",
              _INDENTATION,
              eta,
              result_EG.L_low,
              result_EG.L,
              result_EG.L_high,
              gap_EG,
              result_EG.gamma.max(),
              result_EG.error,
              gap_LP,
          )
  
          if (gaps[t] < self.nu) and (t >= _MIN_ITER):
              # solution found
              break
  
          # update regret
          if t >= last_regret_checked * _REGRET_CHECK_INCREASE_T:
              best_gap = min(gaps_EG)
  
              if best_gap > last_gap * _SHRINK_REGRET:
                  eta *= _SHRINK_ETA
              last_regret_checked = t
              last_gap = best_gap
  
          # update theta based on learning rate
          theta += eta * (gamma - self.constraints.bound())
  
      # retain relevant result data
      gaps_series = pd.Series(gaps)
      gaps_best = gaps_series[
          gaps_series <= gaps_series.min() + _PRECISION
      ]
      self.best_iter_ = gaps_best.index[-1]
      self.best_gap_ = gaps[self.best_iter_]
      self.weights_ = Qs[self.best_iter_]
      self._hs = lagrangian.hs
      for h_idx in self._hs.index:
          if h_idx not in self.weights_.index:
              self.weights_.at[h_idx] = 0.0
  
      self.last_iter_ = len(Qs) - 1
      self.predictors_ = lagrangian.predictors
      self.n_oracle_calls_ = lagrangian.n_oracle_calls
      self.n_oracle_calls_dummy_returned_ = (
          lagrangian.n_oracle_calls_dummy_returned
      )
      self.oracle_execution_times_ = lagrangian.oracle_execution_times
      self.lambda_vecs_ = lagrangian.lambdas
  
      logger.debug(
          "...eps=%.3f, B=%.1f, nu=%.6f, max_iter=%d",
          self.eps,
          B,
          self.nu,
          self.max_iter,
      )
      logger.debug(
          "...last_iter=%d, best_iter=%d, best_gap=%.6f, n_oracle_calls=%d, n_hs=%d",
          self.last_iter_,
          self.best_iter_,
          self.best_gap_,
          lagrangian.n_oracle_calls,
          len(lagrangian.predictors),
      )
      return self
  
file_path: fairlearn/fairlearn/reductions/_exponentiated_gradient/exponentiated_gradient.py
imports: import logging
  import numpy
  import pandas
  from sklearn.base import BaseEstimator, MetaEstimatorMixin
  from sklearn.utils import check_random_state
  from sklearn.utils.validation import check_is_fitted
  from fairlearn.reductions._moments import ClassificationMoment
  from ._constants import _ACCURACY_MUL, _INDENTATION, _MIN_ITER, _PRECISION, _REGRET_CHECK_INCREASE_T, _REGRET_CHECK_START_T, _SHRINK_ETA, _SHRINK_REGRET
  from ._lagrangian import _Lagrangian
label_desc: Use image node_id 2 for calling the ExponentiatedGradient obj's underlying member method code with example usage: obj.fit(X, y) and returns: self