code: def map_normal_eqn(
      key: Tuple,
      key_pattern: List[str],
      pdf: pd.DataFrame,
      labeldf: pd.DataFrame,
      sample_index: Dict[str, List[str]],
      covdf: pd.DataFrame,
  ) -> pd.DataFrame:
      """
      This function constructs matrices X and Y, and returns X_transpose * X (XtX) and X_transpose * Y (XtY), where X
      corresponds to a block from a block matrix.
  
      Each block X is uniquely identified by a header_block ID, which maps to a set of contiguous columns in the overall block
      matrix, and a sample_block ID, which maps to a set of rows in the overall block matrix (and likewise a set of rows in the
      label matrix Y).  The key that identifies X is therefore of the form (header_blockID, sample_blockID).  In some contexts, the
      block matrix will be tied to a particular label from Y, in which case the key will be of the form
      (header_block, sample_block, label).
  
      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 used to build X and Y for block X identified by :key:.
              schema:
               |-- header: string
               |-- size: integer
               |-- indices: array (Required only if the matrix is sparse)
               |    |-- element: integer
               |-- values: array
               |    |-- element: double
               |-- header_block: string
               |-- sample_block: string
               |-- sort_key: integer
               |-- mu: double
               |-- sig: double
               |-- alpha: double (Required only if the header is tied to a specific value of alpha)
               |-- label: double (Required only if the header is tied to a specific label)
          labeldf : Pandas DataFrame containing label values (i. e., the Y in the normal equation above).
          sample_index : sample_index: dict containing a mapping of sample_block ID to a list of corresponding sample IDs
          covdf: Pandas DataFrame containing covariates that should be included with every block X above (can be empty).
  
      Returns:
          transformed Pandas DataFrame containing XtX and XtY corresponding to a particular block X.
              schema (specified by the normal_eqn_struct):
               |-- header_block: string
               |-- sample_block: string
               |-- label: string
               |-- header: string
               |-- sort_key: integer
               |-- xtx: array
               |    |-- element: double
               |-- xty: array
               |    |-- element: double
      """
      (
          header_block,
          sample_block,
          label,
      ) = parse_header_block_sample_block_label(key, key_pattern)
      sort_in_place(pdf, ["sort_key", "header"])
      n_rows = pdf["size"][0]
      n_cols = len(pdf)
      sample_list = sample_index[sample_block]
  
      if covdf.empty:
          header_col = pdf["header"]
          sort_key_col = pdf["sort_key"]
          X = assemble_block(
              n_rows, n_cols, pdf, np.array([]), np.array([])
          )
      else:
          cov_matrix = slice_label_rows(
              covdf, "all", sample_list, np.array([])
          )
          n_cov = len(covdf.columns)
          header_col = np.concatenate([covdf.columns, pdf["header"]])
          # Add new sort_keys for covariates, starting from -n_cov up to 0 to ensure they come ahead of the headers.
          sort_key_col = np.concatenate(
              (np.arange(-n_cov, 0), pdf["sort_key"])
          )
          X = assemble_block(
              n_rows, n_cols, pdf, cov_matrix, np.array([])
          )
  
      Y = slice_label_rows(labeldf, label, sample_list, np.array([]))
      XtX = X.T @ X
      XtY = X.T @ Y
  
      data = {
          "header_block": header_block,
          "sample_block": sample_block,
          "label": label,
          "header": header_col,
          "sort_key": sort_key_col,
          "xtx": list(XtX),
          "xty": list(XtY),
      }
  
      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 1 for calling a global function with example usage: map_normal_eqn(key, key_pattern, pdf, labeldf, sample_index, covdf) and returns: pd