code: def _bulk_rbp(recs, truth, k=None, patience=0.5, normalize=False):
      if k is not None:
          recs = recs[recs["rank"] <= k]
  
      good = recs.join(truth, on=["LKTruthID", "item"], how="inner")
      good["rbp_disc"] = patience ** (good["rank"] - 1)
      scores = good.groupby("LKRecID")["rbp_disc"].sum()
  
      if normalize:
          tns = truth.reset_index().groupby("LKTruthID")["item"].count()
          if k is not None:
              tns[tns > k] = k
          max_nrel = np.max(tns)
          # compute 0...k-1 (the powers of k-1 for 1..k)
          kseq = np.arange(max_nrel)
          # compute the discounts at each k-1
          nd = patience**kseq
          # convert to a series of the sums, up through each k
          max_rbps = pd.Series(np.cumsum(nd), index=kseq + 1)
  
          # get a rec/truth mapping
          map = recs[["LKRecID", "LKTruthID"]].drop_duplicates()
          map.set_index("LKRecID", inplace=True)
          map = map.reindex(scores.index)
          # map to nrel, and then to the max RBPs
          map = map.join(
              tns.to_frame("nrel"), on="LKTruthID", how="left"
          )
          map = map.join(
              max_rbps.to_frame("rbp_max"), on="nrel", how="left"
          )
  
          # divide each score by max RBP
          scores /= map["rbp_max"]
      else:
          scores *= 1 - patience
  
      scores = scores.reindex(recs["LKRecID"].unique(), fill_value=0)
      return scores
  
file_path: lkpy/lenskit/metrics/topn.py
imports: import logging
  import warnings
  import numpy
  import pandas
label_desc: Use image node_id 16 for calling a global function with example usage: _bulk_rbp(recs, truth, k, patience, normalize) and returns: scores