code: def bin_wise_bound_eval(
      fold_bounds_all_targets: list,
      fold_errors: pd.DataFrame,
      fold_bins: pd.DataFrame,
      targets: list,
      uncertainty_type: str,
      num_bins: int = 5,
      show_fig: bool = False,
  ) -> dict:
      """
      Helper function for `evaluate_bounds`. Evaluates the accuracy of estimated error bounds for each quantile bin
      for a given uncertainty type, over a single fold and for multiple targets.
  
      Args:
          fold_bounds_all_targets (list): A list of lists of estimated error bounds for each target.
          fold_errors (pd.DataFrame): A Pandas DataFrame containing the true errors for this fold.
          fold_bins (pd.DataFrame): A Pandas DataFrame containing the predicted quantile bins for this fold.
          targets (list): A list of targets to measure uncertainty estimation.
          uncertainty_type (str): The name of the uncertainty type to calculate accuracy for.
          num_bins (int): The number of quantile bins.
          show_fig (bool): Whether to show a figure depicting error bound accuracy (default=False).
  
      Returns:
          dict: A dictionary containing the following error bound accuracy statistics:
                - 'mean all targets': The mean accuracy over all targets and quantile bins.
                - 'mean all bins': A list of mean accuracy values for each quantile bin (all targets included).
                - 'mean all': A list of accuracy values for each quantile bin and target, weighted by # targets in each bin.
                - 'all bins concatenated targets separated': A list of accuracy values for each quantile bin, concatenated
                 for each target separately.
  
      Example:
          >>> bin_wise_bound_eval(fold_bounds_all_targets, fold_errors, fold_bins, [0,1], 'S-MHA', num_bins=5)
      """
      all_target_perc = []
      all_qs_perc: List[List[float]] = [[] for x in range(num_bins)]  #
      all_qs_size: List[List[float]] = [[] for x in range(num_bins)]
  
      all_qs_errorbound_concat_targets_sep: List[List[List[float]]] = [
          [[] for y in range(num_bins)] for x in range(len(targets))
      ]
  
      for i_ti, target_idx in enumerate(targets):
          true_errors_ti = fold_errors[
              (fold_errors["target_idx"] == target_idx)
          ][["uid", uncertainty_type + " Error"]]
          pred_bins_ti = fold_bins[
              (fold_errors["target_idx"] == target_idx)
          ][["uid", uncertainty_type + " Uncertainty bins"]]
  
          # Zip to dictionary
          true_errors_ti = dict(
              zip(
                  true_errors_ti.uid,
                  true_errors_ti[uncertainty_type + " Error"],
              )
          )
          pred_bins_ti = dict(
              zip(
                  pred_bins_ti.uid,
                  pred_bins_ti[uncertainty_type + " Uncertainty bins"],
              )
          )
  
          # The error bounds are from B1 -> B5 i.e. best quantile of predictions to worst quantile of predictions
          fold_bounds = fold_bounds_all_targets[i_ti]
  
          # For each bin, see what % of targets are between the error bounds.
          # If bin=0 then lower bound = 0, if bin=Q then no upper bound
          # Keep track of #samples in each bin for weighted mean.
  
          # turn dictionary of predicted bins into [[num_bins]] array
          pred_bins_keys = []
          pred_bins_errors = []
          for i in range(num_bins):
              inner_list_bin = list(
                  [
                      key
                      for key, val in pred_bins_ti.items()
                      if str(i) == str(val)
                  ]
              )
              inner_list_errors = []
  
              for id_ in inner_list_bin:
                  inner_list_errors.append(
                      list(
                          [
                              val
                              for key, val in true_errors_ti.items()
                              if str(key) == str(id_)
                          ]
                      )[0]
                  )
  
              pred_bins_errors.append(inner_list_errors)
              pred_bins_keys.append(inner_list_bin)
  
          bins_acc = []
          bins_sizes = []
          for q in range((num_bins)):
              inner_bin_correct = 0
  
              inbin_errors = pred_bins_errors[q]
  
              for error in inbin_errors:
                  if q == 0:
                      lower = 0
                      upper = fold_bounds[q]
  
                      if error <= upper and error > lower:
                          inner_bin_correct += 1
  
                  elif q < (num_bins) - 1:
                      lower = fold_bounds[q - 1]
                      upper = fold_bounds[q]
  
                      if error <= upper and error > lower:
                          inner_bin_correct += 1
  
                  else:
                      lower = fold_bounds[q - 1]
                      upper = 999999999999999999999999999999
  
                      if error > lower:
                          inner_bin_correct += 1
  
              if inner_bin_correct == 0:
                  accuracy_bin = 0.0
              elif len(inbin_errors) == 0:
                  accuracy_bin = 1.0
              else:
                  accuracy_bin = inner_bin_correct / len(inbin_errors)
              bins_sizes.append(len(inbin_errors))
              bins_acc.append(accuracy_bin)
  
              all_qs_perc[q].append(accuracy_bin)
              all_qs_size[q].append(len(inbin_errors))
              all_qs_errorbound_concat_targets_sep[i_ti][q].append(
                  accuracy_bin
              )
  
          # Weighted average over all bins
          weighted_mean_ti = 0.0
          total_weights = 0.0
          for l_idx in range(len(bins_sizes)):
              bin_acc = bins_acc[l_idx]
              bin_size = bins_sizes[l_idx]
              weighted_mean_ti += bin_acc * bin_size
              total_weights += bin_size
          weighted_ave = weighted_mean_ti / total_weights
          all_target_perc.append(weighted_ave)
  
      # Weighted average for each of the quantile bins.
      weighted_ave_binwise = []
      for binidx in range(len(all_qs_perc)):
          bin_accs = all_qs_perc[binidx]
          bin_asizes = all_qs_size[binidx]
  
          weighted_mean_bin = 0.0
          total_weights_bin = 0.0
          for l_idx in range(len(bin_accs)):
              b_acc = bin_accs[l_idx]
              b_siz = bin_asizes[l_idx]
              weighted_mean_bin += b_acc * b_siz
              total_weights_bin += b_siz
  
          # Avoid div by 0
          if weighted_mean_bin == 0 or total_weights_bin == 0:
              weighted_ave_bin = 0.0
          else:
              weighted_ave_bin = weighted_mean_bin / total_weights_bin
          weighted_ave_binwise.append(weighted_ave_bin)
  
      # No weighted average, just normal average
      normal_ave_bin_wise = []
      for binidx in range(len(all_qs_perc)):
          bin_accs = all_qs_perc[binidx]
          normal_ave_bin_wise.append(np.mean(bin_accs))
  
      return {
          "mean all targets": np.mean(all_target_perc),
          "mean all bins": weighted_ave_binwise,
          "mean all": all_qs_perc,
          "all bins concatenated targets seperated": all_qs_errorbound_concat_targets_sep,
      }
  
file_path: pykale/kale/evaluate/uncertainty_metrics.py
imports: from typing import Dict, List
  import numpy
  import pandas
  from kale.evaluate.similarity_metrics import jaccard_similarity
  from kale.prepdata.string_transform import strip_for_bound
label_desc: Use image node_id 2 for calling a global function with example usage: bin_wise_bound_eval(fold_bounds_all_targets, fold_errors, fold_bins, targets, uncertainty_type, num_bins, show_fig) and returns: dict