Create a PR curves summary protobuf.

  Arguments:
    name: A name for the generated node. Will also serve as a series name in
        TensorBoard.
    labels: The ground truth values. A bool numpy array.
    predictions: A float32 numpy array whose values are in the range `[0, 1]`.
        Dimensions must match those of `labels`.
    num_thresholds: Optional number of thresholds, evenly distributed in
        `[0, 1]`, to compute PR metrics for. When provided, should be an int of
        value at least 2. Defaults to 201.
    weights: Optional float or float32 numpy array. Individual counts are
        multiplied by this value. This tensor must be either the same shape as
        or broadcastable to the `labels` numpy array.
    display_name: Optional name for this summary in TensorBoard, as a `str`.
        Defaults to `name`.
    description: Optional long-form description for this summary, as a `str`.
        Markdown is supported. Defaults to empty.

def pb(name,
       labels,
       predictions,
       num_thresholds=None,
       weights=None,
       display_name=None,
       description=None):
  """Create a PR curves summary protobuf.

  Arguments:
    name: A name for the generated node. Will also serve as a series name in
        TensorBoard.
    labels: The ground truth values. A bool numpy array.
    predictions: A float32 numpy array whose values are in the range `[0, 1]`.
        Dimensions must match those of `labels`.
    num_thresholds: Optional number of thresholds, evenly distributed in
        `[0, 1]`, to compute PR metrics for. When provided, should be an int of
        value at least 2. Defaults to 201.
    weights: Optional float or float32 numpy array. Individual counts are
        multiplied by this value. This tensor must be either the same shape as
        or broadcastable to the `labels` numpy array.
    display_name: Optional name for this summary in TensorBoard, as a `str`.
        Defaults to `name`.
    description: Optional long-form description for this summary, as a `str`.
        Markdown is supported. Defaults to empty.
  """
  # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
  import tensorflow.compat.v1 as tf

  if num_thresholds is None:
    num_thresholds = _DEFAULT_NUM_THRESHOLDS

  if weights is None:
    weights = 1.0

  # Compute bins of true positives and false positives.
  bucket_indices = np.int32(np.floor(predictions * (num_thresholds - 1)))
  float_labels = labels.astype(np.float)
  histogram_range = (0, num_thresholds - 1)
  tp_buckets, _ = np.histogram(
      bucket_indices,
      bins=num_thresholds,
      range=histogram_range,
      weights=float_labels * weights)
  fp_buckets, _ = np.histogram(
      bucket_indices,
      bins=num_thresholds,
      range=histogram_range,
      weights=(1.0 - float_labels) * weights)

  # Obtain the reverse cumulative sum.
  tp = np.cumsum(tp_buckets[::-1])[::-1]
  fp = np.cumsum(fp_buckets[::-1])[::-1]
  tn = fp[0] - fp
  fn = tp[0] - tp
  precision = tp / np.maximum(_MINIMUM_COUNT, tp + fp)
  recall = tp / np.maximum(_MINIMUM_COUNT, tp + fn)

  return raw_data_pb(name,
                     true_positive_counts=tp,
                     false_positive_counts=fp,
                     true_negative_counts=tn,
                     false_negative_counts=fn,
                     precision=precision,
                     recall=recall,
                     num_thresholds=num_thresholds,
                     display_name=display_name,
                     description=description)