docs: def quantize_factor(factor_data,
                      quantiles=5,
                      bins=None,
                      by_group=False,
                      no_raise=False,
                      zero_aware=False):
      """
      Computes period wise factor quantiles.
  
      Parameters
      ----------
      factor_data : pd.DataFrame - MultiIndex
          A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
          containing the values for a single alpha factor, forward returns for
          each period, the factor quantile/bin that factor value belongs to, and
          (optionally) the group the asset belongs to.
  
          - See full explanation in utils.get_clean_factor_and_forward_returns
  
      quantiles : int or sequence[float]
          Number of equal-sized quantile buckets to use in factor bucketing.
          Alternately sequence of quantiles, allowing non-equal-sized buckets
          e.g. [0, .10, .5, .90, 1.] or [.05, .5, .95]
          Only one of 'quantiles' or 'bins' can be not-None
      bins : int or sequence[float]
          Number of equal-width (valuewise) bins to use in factor bucketing.
          Alternately sequence of bin edges allowing for non-uniform bin width
          e.g. [-4, -2, -0.5, 0, 10]
          Only one of 'quantiles' or 'bins' can be not-None
      by_group : bool, optional
          If True, compute quantile buckets separately for each group.
      no_raise: bool, optional
          If True, no exceptions are thrown and the values for which the
          exception would have been thrown are set to np.NaN
      zero_aware : bool, optional
          If True, compute quantile buckets separately for positive and negative
          signal values. This is useful if your signal is centered and zero is
          the separation between long and short signals, respectively.
  
      Returns
      -------
      factor_quantile : pd.Series
          Factor quantiles indexed by date and asset.
      """
      if not ((quantiles is not None and bins is None) or
              (quantiles is None and bins is not None)):
          raise ValueError('Either quantiles or bins should be provided')
  
      if zero_aware and not (isinstance(quantiles, int)
                             or isinstance(bins, int)):
          msg = ("zero_aware should only be True when quantiles or bins is an"
                 " integer")
          raise ValueError(msg)
  
      def quantile_calc(x, _quantiles, _bins, _zero_aware, _no_raise):
          try:
              if _quantiles is not None and _bins is None and not _zero_aware:
                  return pd.qcut(x, _quantiles, labels=False) + 1
              elif _quantiles is not None and _bins is None and _zero_aware:
                  pos_quantiles = pd.qcut(x[x >= 0], _quantiles // 2,
                                          labels=False) + _quantiles // 2 + 1
                  neg_quantiles = pd.qcut(x[x < 0], _quantiles // 2,
                                          labels=False) + 1
                  return pd.concat([pos_quantiles, neg_quantiles]).sort_index()
              elif _bins is not None and _quantiles is None and not _zero_aware:
                  return pd.cut(x, _bins, labels=False) + 1
              elif _bins is not None and _quantiles is None and _zero_aware:
                  pos_bins = pd.cut(x[x >= 0], _bins // 2,
                                    labels=False) + _bins // 2 + 1
                  neg_bins = pd.cut(x[x < 0], _bins // 2,
                                    labels=False) + 1
                  return pd.concat([pos_bins, neg_bins]).sort_index()
          except Exception as e:
              if _no_raise:
                  return pd.Series(index=x.index)
              raise e
  
      grouper = [factor_data.index.get_level_values('date')]
      if by_group:
          grouper.append('group')
  
      factor_quantile = factor_data.groupby(grouper)['factor'] \
          .apply(quantile_calc, quantiles, bins, zero_aware, no_raise)
      factor_quantile.name = 'factor_quantile'
  
      return factor_quantile.dropna()