code: def compute_evaluation_metrics(
      metrics: List[str],
      labels: np.ndarray,
      predictions: np.ndarray,
      model_type: str,
      label_dict: Optional[Dict[LabelType, int]] = None,
      grid_objective: Optional[str] = None,
      probability: bool = False,
      logger: Optional[logging.Logger] = None,
  ) -> ComputeEvalMetricsResults:
      """
      Compute given evaluation metrics.
  
      Compute given metrics to evaluate the given predictions generated
      by the given type of estimator against the given true labels.
  
      Parameters
      ----------
      metrics : List[str]
          List of metrics to compute.
      labels : numpy.ndarray
          True labels to be used for computing the metrics.
      predictions : numpy.ndarray
          The predictions to be used for computing the metrics.
      model_type : str
          One of "classifier" or "regressor".
      label_dict : Optional[Dict[LabelType, int]], default=None
          Dictionary mapping class labels to indices for classification.
      grid_objective : Optional[str], default=None
          The objective used for tuning the hyper-parameters of the model
          that generated the predictions. If ``None``, it means that no
          grid search was done.
      probability : bool, default=False
          Does the model output class probabilities?
      logger : Optional[logging.Logger], default=None
          A logger instance to use for logging messages and warnings.
          If ``None``, a new one is created.
  
      Returns
      -------
      :class:`skll.types.ComputeEvalMetricsResults`
          5-tuple including the confusion matrix, the overall accuracy, the
          per-label PRFs, the grid search objective function score, and the
          additional evaluation metrics, if any. For regressors, the
          first two elements are ``None``.
      """
      # set up the logger
      logger = logger if logger else logging.getLogger(__name__)
  
      # warn if grid objective was also specified in metrics
      if len(metrics) > 0 and grid_objective in metrics:
          logger.warning(
              f"The grid objective '{grid_objective}' is also "
              "specified as an evaluation metric. Since its value "
              "is already included in the results as the objective "
              "score, it will not be printed again in the list of "
              "metrics."
          )
          metrics = [
              metric for metric in metrics if metric != grid_objective
          ]
  
      # initialize a dictionary that will hold all of the metric scores
      metric_scores: Dict[str, Optional[float]] = {
          metric: None for metric in metrics
      }
  
      # if we are doing classification and are a probablistic
      # learner or a soft-voting meta learner, then `yhat` are
      # probabilities so we need to compute the class indices
      # separately and save them too
      if model_type == "classifier" and probability:
          class_probs = predictions
          predictions = np.argmax(class_probs, axis=1)
      # if we are a regressor or classifier not in probability
      # mode, then we have the class indices already and there
      # are no probabilities
      else:
          class_probs = None
  
      # make a single list of metrics including the grid objective
      # since it's easier to compute everything together
      metrics_to_compute = [grid_objective] + metrics
      for metric in metrics_to_compute:
          # skip the None if we are not doing grid search
          if not metric:
              continue
  
          # declare types
          preds_for_metric: Optional[np.ndarray]
  
          # CASE 1: in probability mode for classification which means we
          # need to either use the probabilities directly or infer the labels
          # from them depending on the metric
          if probability and label_dict and class_probs is not None:
              # there are three possible cases here:
              # (a) if we are using a correlation metric or
              #     `average_precision` or `roc_auc` in a binary
              #      classification scenario, then we need to explicitly
              #     pass in the probabilities of the positive class.
              # (b) if we are using `neg_log_loss`, then we
              #     just pass in the full probability array
              # (c) we compute the most likely labels from the
              #     probabilities via argmax and use those
              #     for all other metrics
              if (
                  len(label_dict) == 2
                  and (
                      metric in CORRELATION_METRICS
                      or metric in ["average_precision", "roc_auc"]
                  )
                  and metric != grid_objective
              ):
                  logger.info(
                      "using probabilities for the positive class to "
                      f"compute '{metric}' for evaluation."
                  )
                  preds_for_metric = class_probs[:, 1]
              elif metric == "neg_log_loss":
                  preds_for_metric = class_probs
              else:
                  preds_for_metric = predictions
  
          # CASE 2: no probability mode for classifier or regressor
          # in which case we just use the predictions as they are
          else:
              preds_for_metric = predictions
  
          try:
              metric_scores[metric] = use_score_func(
                  metric, labels, preds_for_metric
              )
          except ValueError:
              metric_scores[metric] = float("NaN")
  
      # now separate out the grid objective score from the additional metric scores
      # if a grid objective was actually passed in. If no objective was passed in
      # then that score should just be none.
      objective_score = None
      additional_scores = metric_scores.copy()
      if grid_objective:
          objective_score = metric_scores[grid_objective]
          del additional_scores[grid_objective]
  
      # declare the type for the results
      res: ComputeEvalMetricsResults
  
      # compute some basic statistics for regressors
      if model_type == "regressor":
          regressor_result_dict: Dict[LabelType, Any] = {
              "descriptive": defaultdict(dict)
          }
          for table_label, y in zip(
              ["actual", "predicted"], [labels, predictions]
          ):
              regressor_result_dict["descriptive"][table_label][
                  "min"
              ] = min(y)
              regressor_result_dict["descriptive"][table_label][
                  "max"
              ] = max(y)
              regressor_result_dict["descriptive"][table_label][
                  "avg"
              ] = np.mean(y)
              regressor_result_dict["descriptive"][table_label][
                  "std"
              ] = np.std(y)
          regressor_result_dict["pearson"] = use_score_func(
              "pearson", labels, predictions
          )
          res = (
              None,
              None,
              regressor_result_dict,
              objective_score,
              additional_scores,
          )
      elif label_dict:
          # compute the confusion matrix and precision/recall/f1
          # note that we are using the class indices here
          # and not the actual class labels themselves
          num_labels = len(label_dict)
          conf_mat: ConfusionMatrix = confusion_matrix(
              labels, predictions, labels=list(range(num_labels))
          ).tolist()
          # Calculate metrics
          overall_accuracy: float = accuracy_score(labels, predictions)
          result_matrix = precision_recall_fscore_support(
              labels,
              predictions,
              labels=list(range(num_labels)),
              average=None,
          )
  
          # Store results
          classifier_result_dict: Dict[LabelType, Any] = defaultdict(
              dict
          )
          for actual_label in sorted(label_dict):
              col = label_dict[actual_label]
              classifier_result_dict[actual_label][
                  "Precision"
              ] = result_matrix[0][col]
              classifier_result_dict[actual_label][
                  "Recall"
              ] = result_matrix[1][col]
              classifier_result_dict[actual_label][
                  "F-measure"
              ] = result_matrix[2][col]
  
          res = (
              conf_mat,
              overall_accuracy,
              classifier_result_dict,
              objective_score,
              additional_scores,
          )
  
      return res
  
imports: from __future__ import annotations
  import inspect
  import logging
  import sys
  import time
  from collections import Counter, defaultdict
  from csv import DictWriter, excel_tab
  from functools import wraps
  from importlib import import_module
  from pathlib import Path
  from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Set, Tuple, Type, Union
  import joblib
  import numpy
  import scipy.sparse
  from sklearn.base import BaseEstimator, TransformerMixin
  from sklearn.feature_selection import SelectKBest
  from sklearn.metrics import accuracy_score, confusion_matrix, precision_recall_fscore_support
  from sklearn.model_selection import KFold, LeaveOneGroupOut, ShuffleSplit, StratifiedKFold
  from skll.data import FeatureSet
  from skll.metrics import _CUSTOM_METRICS, _PREDEFINED_CUSTOM_METRICS, use_score_func
  from skll.types import ComputeEvalMetricsResults, ConfusionMatrix, FeaturesetIterator, FoldMapping, IdType, IndexIterator, LabelType, PathOrStr, SparseFeatureMatrix
  from skll.utils.constants import CLASSIFICATION_ONLY_METRICS, CORRELATION_METRICS, REGRESSION_ONLY_METRICS, UNWEIGHTED_KAPPA_METRICS, WEIGHTED_KAPPA_METRICS
  from skll.version import VERSION
label_desc: Use image node_id 2 for calling a global function with example usage: compute_evaluation_metrics(metrics, labels, predictions, model_type, label_dict, grid_objective, probability, logger) and returns: res