code: def ndcg_at_k(
      rating_true,
      rating_pred,
      col_user=DEFAULT_USER_COL,
      col_item=DEFAULT_ITEM_COL,
      col_rating=DEFAULT_RATING_COL,
      col_prediction=DEFAULT_PREDICTION_COL,
      relevancy_method="top_k",
      k=DEFAULT_K,
      threshold=DEFAULT_THRESHOLD,
      score_type="binary",
      discfun_type="loge",
      **_,
  ):
      """Normalized Discounted Cumulative Gain (nDCG).
  
      Info: https://en.wikipedia.org/wiki/Discounted_cumulative_gain
  
      Args:
          rating_true (pandas.DataFrame): True DataFrame
          rating_pred (pandas.DataFrame): Predicted DataFrame
          col_user (str): column name for user
          col_item (str): column name for item
          col_rating (str): column name for rating
          col_prediction (str): column name for prediction
          relevancy_method (str): method for determining relevancy ['top_k', 'by_threshold', None]. None means that the
              top k items are directly provided, so there is no need to compute the relevancy operation.
          k (int): number of top k items per user
          threshold (float): threshold of top items per user (optional)
          score_type (str): type of relevance scores ['binary', 'raw', 'exp']. With the default option 'binary', the
              relevance score is reduced to either 1 (hit) or 0 (miss). Option 'raw' uses the raw relevance score.
              Option 'exp' uses (2 ** RAW_RELEVANCE - 1) as the relevance score
          discfun_type (str): type of discount function ['loge', 'log2'] used to calculate DCG.
  
      Returns:
          float: nDCG at k (min=0, max=1).
      """
      df_hit, _, _ = merge_ranking_true_pred(
          rating_true=rating_true,
          rating_pred=rating_pred,
          col_user=col_user,
          col_item=col_item,
          col_prediction=col_prediction,
          relevancy_method=relevancy_method,
          k=k,
          threshold=threshold,
      )
  
      if df_hit.shape[0] == 0:
          return 0.0
  
      df_dcg = df_hit.merge(rating_pred, on=[col_user, col_item]).merge(
          rating_true,
          on=[col_user, col_item],
          how="outer",
          suffixes=("_left", None),
      )
  
      if score_type == "binary":
          df_dcg["rel"] = 1
      elif score_type == "raw":
          df_dcg["rel"] = df_dcg[col_rating]
      elif score_type == "exp":
          df_dcg["rel"] = 2 ** df_dcg[col_rating] - 1
      else:
          raise ValueError(
              "score_type must be one of 'binary', 'raw', 'exp'"
          )
  
      if discfun_type == "loge":
          discfun = np.log
      elif discfun_type == "log2":
          discfun = np.log2
      else:
          raise ValueError("discfun_type must be one of 'loge', 'log2'")
  
      # Calculate the actual discounted gain for each record
      df_dcg["dcg"] = df_dcg["rel"] / discfun(1 + df_dcg["rank"])
  
      # Calculate the ideal discounted gain for each record
      df_idcg = df_dcg.sort_values(
          [col_user, col_rating], ascending=False
      )
      df_idcg["irank"] = df_idcg.groupby(
          col_user, as_index=False, sort=False
      )[col_rating].rank("first", ascending=False)
      df_idcg["idcg"] = df_idcg["rel"] / discfun(1 + df_idcg["irank"])
  
      # Calculate the actual DCG for each user
      df_user = df_dcg.groupby(
          col_user, as_index=False, sort=False
      ).agg({"dcg": "sum"})
  
      # Calculate the ideal DCG for each user
      df_user = df_user.merge(
          df_idcg.groupby(col_user, as_index=False, sort=False)
          .head(k)
          .groupby(col_user, as_index=False, sort=False)
          .agg({"idcg": "sum"}),
          on=col_user,
      )
  
      # DCG over IDCG is the normalized DCG
      df_user["ndcg"] = df_user["dcg"] / df_user["idcg"]
      return df_user["ndcg"].mean()
  
imports: import numpy
  import pandas
  from functools import wraps
  from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, explained_variance_score, roc_auc_score, log_loss
  from recommenders.utils.constants import DEFAULT_USER_COL, DEFAULT_ITEM_COL, DEFAULT_RATING_COL, DEFAULT_PREDICTION_COL, DEFAULT_RELEVANCE_COL, DEFAULT_SIMILARITY_COL, DEFAULT_ITEM_FEATURES_COL, DEFAULT_ITEM_SIM_MEASURE, DEFAULT_K, DEFAULT_THRESHOLD
  from recommenders.datasets.pandas_df_utils import has_columns, has_same_base_dtype, lru_cache_df
label_desc: Use image node_id 12 for calling a global function with example usage: ndcg_at_k(rating_true, rating_pred, col_user, col_item, col_rating, col_prediction, relevancy_method, k, threshold, score_type, discfun_type) and returns: df_user, int