class_docstr: Lower bound of ratio of binomial proportions.
  
  This class implements several methods to compute a statistical lower bound of
  the ratio of binomial proportions, e.g. tpr / fpr.
  Most of the methods are based on https://doi.org/10.1111/2041-210X.12304 and
  their code at https://CRAN.R-project.org/package=asbio.
  Clopper pearson is based on https://arxiv.org/pdf/2006.07709.pdf.
  
  Examples:
    >>> tp, fp = np.array([100, 90]), np.array([10, 5])
    >>> pos_size, neg_size = 110, 80
    >>> rb = elb.RatioBound(tp, fp, pos_size, neg_size, 0.05)
    >>> rb.compute_bound(BoundMethod.BAILEY)
    array([4.61953896, 6.87647915])
    >>> rb.compute_bounds([BoundMethod.BAILEY, BoundMethod.KATZ_LOG])
    {<BoundMethod.BAILEY: 'bailey'>: array([4.61953896, 6.87647915]),
     <BoundMethod.KATZ_LOG: 'katz-log'>: array([4.45958661, 6.39712581])}
  
  Attributes:
    available_methods: a dictionary mapping BoundMethod to the function.
class_docstr_tok: ["Lower","bound","of","ratio","of","binomial","proportions",".","This","class","implements","several","methods","to","compute","a","statistical","lower","bound","of","the","ratio","of","binomial","proportions",",","e.g",".","tpr","\/","fpr",".","Most","of","the","methods","are","based","on","https",":","\/\/doi.org\/10.1111\/2041-210X.12304","and","their","code","at","https",":","\/\/CRAN.R-project.org\/package=asbio",".","Clopper","pearson","is","based","on","https",":","\/\/arxiv.org\/pdf\/2006.07709.pdf",".","Examples",":",">",">",">","tp",",","fp","=","np.array","(","[","100",",","90","]",")",",","np.array","(","[","10",",","5","]",")",">",">",">","pos_size",",","neg_size","=","110",",","80",">",">",">","rb","=","elb.RatioBound","(","tp",",","fp",",","pos_size",",","neg_size",",","0.05",")",">",">",">","rb.compute_bound","(","BoundMethod.BAILEY",")","array","(","[","4.61953896",",","6.87647915","]",")",">",">",">","rb.compute_bounds","(","[","BoundMethod.BAILEY",",","BoundMethod.KATZ_LOG","]",")","{","<","BoundMethod.BAILEY",":","'bailey","'",">",":","array","(","[","4.61953896",",","6.87647915","]",")",",","<","BoundMethod.KATZ_LOG",":","'katz-log","'",">",":","array","(","[","4.45958661",",","6.39712581","]",")","}","Attributes",":","available_methods",":","a","dictionary","mapping","BoundMethod","to","the","function","."]
docstr: Initializes the ratio bound class.
  
  Args:
    tp: true positives.
    fp: false positives. Should be of the same length as tp.
    pos_size: number of real positive samples.
    neg_size: number of real negative samples.
    alpha: the confidence level, must be < 0.5.
docstr_tok: ["Initializes","the","ratio","bound","class",".","Args",":","tp",":","true","positives",".","fp",":","false","positives",".","Should","be","of","the","same","length","as","tp",".","pos_size",":","number","of","real","positive","samples",".","neg_size",":","number","of","real","negative","samples",".","alpha",":","the","confidence","level",",","must","be","<","0.5","."]
code: def __init__(
      self,
      tp: Union[Sequence[int], int],
      fp: Union[Sequence[int], int],
      pos_size: int,
      neg_size: int,
      alpha: float,
  ):
      """Initializes the ratio bound class.
  
      Args:
        tp: true positives.
        fp: false positives. Should be of the same length as tp.
        pos_size: number of real positive samples.
        neg_size: number of real negative samples.
        alpha: the confidence level, must be < 0.5.
      """
      if alpha >= 0.5:
          raise ValueError(
              "alpha should be < 0.5, e.g. alpha=0.05, "
              f"but got {alpha}."
          )
      self._is_scalar = (
          False  # Would return scalar if `tp` is a scalar.
      )
      # Convert tp or fp to list if it is a scalar.
      if isinstance(tp, numbers.Number):
          tp = [tp]
          self._is_scalar = True
      if isinstance(fp, numbers.Number):
          fp = [fp]
      if len(tp) != len(fp):
          raise ValueError(
              "tp and fp should have the same number of elements, "
              f"but get {len(tp)} and {len(fp)} respectively."
          )
      # Some methods need the original values.
      self._tp_orig = np.array(tp, dtype=float)
      self._fp_orig = np.array(fp, dtype=float)
      if np.any(self._tp_orig > pos_size) or np.any(self._tp_orig < 0):
          raise ValueError("tp needs to be in [0, pos_size].")
      if np.any(self._fp_orig > neg_size) or np.any(self._fp_orig < 0):
          raise ValueError("fp needs to be in [0, neg_size].")
  
      self.available_methods = immutabledict.immutabledict(
          {
              BoundMethod.KATZ_LOG: self._bound_katz_log,
              BoundMethod.ADJUSTED_LOG: self._bound_adjusted_log,
              BoundMethod.BAILEY: self._bound_bailey,
              BoundMethod.INV_SINH: self._bound_inv_hyperbolic_sine,
              BoundMethod.CLOPPER_PEARSON: self._bound_clopper_pearson,
          }
      )
      self._alpha = alpha
      self._z = scipy.stats.norm.ppf(alpha)
      self._pos_size, self._neg_size = pos_size, neg_size
  
      # Some methods need to adjust maximum possible values. We record the
      # adjusted arrays.
      idx_max = np.logical_and(
          self._tp_orig == self._pos_size,
          self._fp_orig == self._neg_size,
      )
      self._tp = np.where(idx_max, self._pos_size - 0.5, self._tp_orig)
      self._fp = np.where(idx_max, self._neg_size - 0.5, self._fp_orig)
  
      # Some methods need to handle 0 specifically. We record the indices.
      self._idx_tp_0, self._idx_fp_0 = (self._tp == 0), (self._fp == 0)
  
imports: import enum
  import numbers
  from typing import Dict, Iterable, Optional, Sequence, Union
  import immutabledict
  import numpy
  import numpy.typing
  import scipy.integrate
  import scipy.optimize
  import scipy.stats
  import sklearn.metrics
  from statsmodels.stats import proportion
label_desc: Use image node_id 1 to create a new RatioBound object  with example: obj = RatioBound(tp, fp, pos_size, neg_size, alpha)