class_docstr: Equalized odds postprocessing is a post-processing technique that solves
  a linear program to find probabilities with which to change output labels to
  optimize equalized odds [8]_ [9]_.
  
  References:
      .. [8] M. Hardt, E. Price, and N. Srebro, "Equality of Opportunity in
         Supervised Learning," Conference on Neural Information Processing
         Systems, 2016.
      .. [9] G. Pleiss, M. Raghavan, F. Wu, J. Kleinberg, and
         K. Q. Weinberger, "On Fairness and Calibration," Conference on Neural
         Information Processing Systems, 2017.
docstr: Perturb the predicted labels to obtain new labels that satisfy
  equalized odds constraints.
  
  Args:
      dataset (BinaryLabelDataset): Dataset containing labels that needs
          to be transformed.
      dataset (BinaryLabelDataset): Transformed dataset.
code: def predict(self, dataset):
      """Perturb the predicted labels to obtain new labels that satisfy
      equalized odds constraints.
  
      Args:
          dataset (BinaryLabelDataset): Dataset containing labels that needs
              to be transformed.
          dataset (BinaryLabelDataset): Transformed dataset.
      """
      if self.seed is not None:
          np.random.seed(self.seed)
  
      # Get the model parameters output from fit
      sp2p, sn2p, op2p, on2p = self.model_params.x
  
      # Create boolean conditioning vectors for protected groups
      cond_vec_priv = utils.compute_boolean_conditioning_vector(
          dataset.protected_attributes,
          dataset.protected_attribute_names,
          self.privileged_groups,
      )
      cond_vec_unpriv = utils.compute_boolean_conditioning_vector(
          dataset.protected_attributes,
          dataset.protected_attribute_names,
          self.unprivileged_groups,
      )
  
      # Randomly flip labels according to the probabilities in model_params
      self_fair_pred = dataset.labels[cond_vec_priv].copy()
      self_pp_indices, _ = np.nonzero(
          dataset.labels[cond_vec_priv] == dataset.favorable_label
      )
      self_pn_indices, _ = np.nonzero(
          dataset.labels[cond_vec_priv] == dataset.unfavorable_label
      )
      np.random.shuffle(self_pp_indices)
      np.random.shuffle(self_pn_indices)
  
      n2p_indices = self_pn_indices[: int(len(self_pn_indices) * sn2p)]
      self_fair_pred[n2p_indices] = dataset.favorable_label
      p2n_indices = self_pp_indices[
          : int(len(self_pp_indices) * (1 - sp2p))
      ]
      self_fair_pred[p2n_indices] = dataset.unfavorable_label
  
      othr_fair_pred = dataset.labels[cond_vec_unpriv].copy()
      othr_pp_indices, _ = np.nonzero(
          dataset.labels[cond_vec_unpriv] == dataset.favorable_label
      )
      othr_pn_indices, _ = np.nonzero(
          dataset.labels[cond_vec_unpriv] == dataset.unfavorable_label
      )
      np.random.shuffle(othr_pp_indices)
      np.random.shuffle(othr_pn_indices)
  
      n2p_indices = othr_pn_indices[: int(len(othr_pn_indices) * on2p)]
      othr_fair_pred[n2p_indices] = dataset.favorable_label
      p2n_indices = othr_pp_indices[
          : int(len(othr_pp_indices) * (1 - op2p))
      ]
      othr_fair_pred[p2n_indices] = dataset.unfavorable_label
  
      # Mutated, fairer dataset with new labels
      dataset_new = dataset.copy()
  
      new_labels = np.zeros_like(dataset.labels, dtype=np.float64)
      new_labels[cond_vec_priv] = self_fair_pred
      new_labels[cond_vec_unpriv] = othr_fair_pred
  
      dataset_new.labels = new_labels
  
      return dataset_new
  
file_path: AIF360/aif360/algorithms/postprocessing/eq_odds_postprocessing.py
imports: import numpy
  from scipy.optimize import linprog
  from aif360.algorithms import Transformer
  from aif360.metrics import ClassificationMetric, utils
label_desc: Use image node_id 3 for calling the EqOddsPostprocessing obj's underlying member method code with example usage: obj.predict(dataset) and returns: dataset_new