def gen_rule(tbl, pts):
  """
  The function generates binning rules based on the binning outcome table and
  a list of cut points and is an utility function that is not supposed to be 
  called directly by users.

  Parameters:
    tbl : A intermediate table of the binning outcome within each binning 
          function
    pts : A list cut points for the binning

  Returns:
    A list of dictionaries with binning rules 
  """

  for _ in tbl:
    if _["bin"] == 0:
      _["rule"] = "numpy.isnan($X$)"
    elif _["bin"] == len(pts) + 1:
      if _["miss"] == 0:
        _["rule"] = "$X$ > " + str(pts[-1])
      else:
        _["rule"] = "$X$ > " + str(pts[-1]) + " or numpy.isnan($X$)"
    elif _["bin"] == 1:
      if _["miss"] == 0:
        _["rule"] = "$X$ <= " + str(pts[0])
      else:
        _["rule"] = "$X$ <= " + str(pts[0]) + " or numpy.isnan($X$)"
    else:
        _["rule"] = "$X$ > " + str(pts[_["bin"] - 2]) + " and $X$ <= " + str(pts[_["bin"] - 1])

  _sel = ["bin", "freq", "miss", "bads", "rate", "woe", "iv", "ks", "rule"]

  return([{k: _[k] for k in _sel} for _ in tbl])