code: def abdm(X: np.ndarray, cat_vars: dict, cat_vars_bin: dict = dict()):
      """
      Calculate the pair-wise distances between categories of a categorical variable using
      the Association-Based Distance Metric based on Le et al (2005).
      http://www.jaist.ac.jp/~bao/papers/N26.pdf
  
      Parameters
      ----------
      X
          Batch of arrays.
      cat_vars
          Dict with as keys the categorical columns and as optional values
          the number of categories per categorical variable.
      cat_vars_bin
          Dict with as keys the binned numerical columns and as optional values
          the number of bins per variable.
  
      Returns
      -------
      Dict with as keys the categorical columns and as values the pairwise distance matrix for the variable.
      """
      # TODO: handle triangular inequality
      # ensure numerical stability
      eps = 1e-12
  
      # infer number of categories per categorical variable
      cat_cols = list(cat_vars.keys())
      for col in cat_cols:
          if cat_vars[col] is not None:
              continue
          cat_vars[col] = len(np.unique(X[:, col]))
  
      # combine dict for categorical with binned features
      cat_vars_combined = {**cat_vars, **cat_vars_bin}
  
      d_pair: Dict = {}
      X_cat_eq: Dict = {}
      for col, n_cat in cat_vars.items():
          X_cat_eq[col] = []
          for i in range(
              n_cat
          ):  # for each category in categorical variable, store instances of each category
              idx = np.where(X[:, col] == i)[0]
              X_cat_eq[col].append(X[idx, :])
  
          # conditional probabilities, also use the binned numerical features
          p_cond = []
          for col_t, n_cat_t in cat_vars_combined.items():
              if col == col_t:
                  continue
              p_cond_t = np.zeros([n_cat_t, n_cat])
              for i in range(n_cat_t):
                  for j, X_cat_j in enumerate(X_cat_eq[col]):
                      idx = np.where(X_cat_j[:, col_t] == i)[0]
                      p_cond_t[i, j] = len(idx) / (
                          X_cat_j.shape[0] + eps
                      )
              p_cond.append(p_cond_t)
  
          # pairwise distance matrix
          d_pair_col = np.zeros([n_cat, n_cat])
          for i in range(n_cat):
              j = 0
              while j < i:
                  d_ij_tmp = 0
                  for (
                      p
                  ) in p_cond:  # loop over other categorical variables
                      for t in range(
                          p.shape[0]
                      ):  # loop over categories of each categorical variable
                          a, b = p[t, i], p[t, j]
                          d_ij_t = a * np.log(
                              (a + eps) / (b + eps)
                          ) + b * np.log(
                              (b + eps) / (a + eps)
                          )  # KL divergence
                          d_ij_tmp += d_ij_t
                  d_pair_col[i, j] = d_ij_tmp
                  j += 1
          d_pair_col += d_pair_col.T
          d_pair[col] = d_pair_col
      return d_pair
  
file_path: alibi/alibi/utils/distance.py
imports: import numpy
  from sklearn.manifold import MDS
  from typing import Dict, Tuple, Callable, Optional, Union
label_desc: Use image node_id 3 for calling a global function with example usage: abdm(X, cat_vars, cat_vars_bin) and returns: d_pair