code: def score_models(
      key: Tuple,
      key_pattern: List[str],
      pdf: pd.DataFrame,
      labeldf: pd.DataFrame,
      sample_index: Dict[str, List[str]],
      alphas: Dict[str, Float],
      covdf: pd.DataFrame,
      maskdf: pd.DataFrame,
      metric: str,
  ) -> pd.DataFrame:
      """
      Similar to apply_model, this function performs the multiplication X*B for a block X and corresponding coefficient
      matrix B, however it also produces a score for the model, specified by the :metric: parameter.
  
      Args:
          key : unique key identifying the group of rows emitted by a groupBy statement
          key_pattern : pattern of columns used in the groupBy statement that emitted this group of rows
          pdf : starting Pandas DataFrame containing the lists of rows used to assemble block X and coefficients B
              identified by :key:
              schema:
                   |-- header_block: string
                   |-- sample_block: string
                   |-- header: string
                   |-- size: integer
                   |-- indices: array
                   |    |-- element: integer
                   |-- values: array
                   |    |-- element: double
                   |-- sort_key: integer
                   |-- mu: double
                   |-- sig: double
                   |-- alphas: array
                   |    |-- element: string
                   |-- labels: array
                   |    |-- element: string
                   |-- coefficients: array
                   |    |-- element: double
          labeldf : Pandas DataFrame containing label values that were used in fitting coefficient matrix B.
          sample_index : sample_index: dict containing a mapping of sample_block ID to a list of corresponding sample IDs
          alphas : dict of {alphaName : alphaValue} for the alpha values that were used when fitting coefficient matrix B
          covdf: Pandas DataFrame containing covariates that should be included with every block X above (can be empty).
          maskdf : Pandas DataFrame mirroring labeldf containing Boolean values flagging samples with missing labels as
              True and others as False.
          metric: String specifying what metric to apply, can be 'r2' or 'log_loss'
  
      Returns:
          Pandas DataFrame containing the r2 scores for each combination of alpha and label
              schema:
                   |-- sample_block: string
                   |-- label: string
                   |-- alpha: string
                   |-- score: double
      """
      (
          header_block,
          sample_block,
          label,
      ) = parse_header_block_sample_block_label(key, key_pattern)
      sort_in_place(pdf, ["sort_key"])
      sample_list = sample_index[sample_block]
  
      if maskdf.empty:
          row_mask = np.array([])
      else:
          row_mask = slice_label_rows(
              maskdf, label, sample_list, np.array([])
          ).ravel()
  
      if covdf.empty:
          n_rows = pdf["size"][0]
          n_cols = len(pdf)
          X = assemble_block(
              n_rows, n_cols, pdf, np.array([]), row_mask
          )
      else:
          # If there is a covdf, we will have null 'values' entries in pdf arising from the right join of blockdf
          # to modeldf, so we will filter those rows out before assembling the block.
          n_rows = int(pdf[~pdf["values"].isnull()]["size"].array[0])
          n_cols = len(pdf[~pdf["values"].isnull()])
          cov_matrix = slice_label_rows(
              covdf, "all", sample_list, np.array([])
          )
          X = assemble_block(
              n_rows,
              n_cols,
              pdf[~pdf["values"].isnull()],
              cov_matrix,
              row_mask,
          )
  
      B = np.row_stack(pdf["coefficients"].array)
      XB = X @ B
      Y = slice_label_rows(labeldf, label, sample_list, row_mask)
      if metric == "r2":
          scores = r_squared(XB, Y)
      elif metric == "log_loss":
          Z = sigmoid(XB)
          scores = log_loss(Z, Y)
      else:
          raise ValueError(
              f'Metric should be either "r2" or "log_loss", found {metric}'
          )
      alpha_names = sorted(alphas.keys())
  
      data = {
          "sample_block": sample_block,
          "label": label,
          "alpha": alpha_names,
          "score": scores,
      }
  
      return pd.DataFrame(data)
  
file_path: glow/python/glow/wgr/ridge_udfs.py
imports: from glow.wgr.model_functions import *
  from nptyping import Float
  import pandas
  from pyspark.sql.types import ArrayType, IntegerType, StructType, StructField, StringType, DoubleType
  from typeguard import typechecked
  from typing import Dict, List
label_desc: Use image node_id 5 for calling a global function with example usage: score_models(key, key_pattern, pdf, labeldf, sample_index, alphas, covdf, maskdf, metric) and returns: pd