code: def calculate_nearest_neighbors_distances(
      data: pd.DataFrame,
      cat_cols: List[Hashable],
      numeric_cols: List[Hashable],
      num_neighbors: int,
      samples_to_calc_neighbors_for: pd.DataFrame = None,
  ):
      """
      Calculate distance matrix for a dataset using Gower's method.
  
      Gowers distance is a measurement for distance between two samples. It returns the average of their distances
      per feature. For numeric features it calculates the absolute distance divide by the range of the feature. For
      categorical features it is an indicator whether the values are the same.
      See https://www.jstor.org/stable/2528823 for further details.
      This method minimizes memory usage by saving in memory and returning only the closest neighbors of each sample.
      In addition, it can deal with missing values.
  
      Parameters
      ----------
      data: pd.DataFrame
          DataFrame including all
      cat_cols: List[Hashable]
          List of categorical columns in the data.
      numeric_cols: List[Hashable]
          List of numerical columns in the data.
      num_neighbors: int
          Number of neighbors to return. For example, for n=2 for each sample returns the distances to the two closest
          samples in the dataset.
      samples_to_calc_neighbors_for: pd.DataFrame, default None
          Samples for which to calculate nearest neighbors. If None, calculates for all given samples in data.
          These samples do not have to exist in data, but must share all relevant features.
  
      Returns
      -------
      numpy.ndarray
          representing the distance matrix to the nearest neighbors.
      numpy.ndarray
          representing the indexes of the nearest neighbors.
      """
      num_samples = data.shape[0]
      if samples_to_calc_neighbors_for is not None:
          data = pd.concat([data, samples_to_calc_neighbors_for])
          num_indices_to_calc = samples_to_calc_neighbors_for.shape[0]
      else:
          num_indices_to_calc = data.shape[0]
  
      cat_data = data[cat_cols]
      numeric_data = data[numeric_cols]
      num_features = len(cat_cols + numeric_cols)
  
      distances, indexes = np.zeros(
          (num_indices_to_calc, num_neighbors)
      ), np.zeros((num_indices_to_calc, num_neighbors))
      # handle categorical - transform to an ordinal numpy array
      cat_data = (
          np.asarray(cat_data.apply(lambda x: pd.factorize(x)[0]))
          if not cat_data.empty
          else np.asarray(cat_data)
      )
      # handle numerical - calculate ranges per feature and fill numerical nan to minus np.inf
      numeric_data = np.asarray(
          numeric_data.fillna(value=np.nan).astype("float64")
      )
      numeric_feature_ranges = np.nanmax(
          numeric_data, axis=0
      ) - np.nanmin(numeric_data, axis=0)
      numeric_feature_ranges = np.where(
          numeric_feature_ranges == 0, 1, numeric_feature_ranges
      )
      numeric_data = np.nan_to_num(numeric_data, nan=np.inf)
  
      # do not warn on operations that include usage of math involving inf
      original_error_state = np.geterr()["invalid"]
      np.seterr(invalid="ignore")
  
      if samples_to_calc_neighbors_for is not None:
          numeric_samples_to_calc_neighbors_for = numeric_data[
              num_samples:
          ]
          cat_samples_to_calc_neighbors_for = cat_data[num_samples:]
          numeric_data = numeric_data[:num_samples]
          cat_data = cat_data[:num_samples]
  
      else:
          numeric_samples_to_calc_neighbors_for = numeric_data
          cat_samples_to_calc_neighbors_for = cat_data
  
      for i in range(
          num_indices_to_calc
      ):  # TODO: parallelize this loop
          numeric_sample_i = numeric_samples_to_calc_neighbors_for[i, :]
          cat_sample_i = cat_samples_to_calc_neighbors_for[i, :]
          dist_to_sample_i = _calculate_distances_to_sample(
              categorical_sample=cat_sample_i,
              numeric_sample=numeric_sample_i,
              cat_data=cat_data,
              numeric_data=numeric_data,
              numeric_feature_ranges=numeric_feature_ranges,
              num_features=num_features,
          )
          # sort to find the closest samples (including self)
          min_dist_indexes = np.argpartition(
              dist_to_sample_i, num_neighbors
          )[:num_neighbors]
          min_dist_indexes_ordered = sorted(
              min_dist_indexes,
              key=lambda x, arr=dist_to_sample_i: arr[x],
              reverse=False,
          )
          indexes[i, :] = min_dist_indexes_ordered
          distances[i, :] = dist_to_sample_i[min_dist_indexes_ordered]
  
      np.seterr(invalid=original_error_state)
      return (
          np.nan_to_num(
              distances, nan=np.nan, posinf=np.nan, neginf=np.nan
          ),
          indexes,
      )
  
file_path: deepchecks/deepchecks/utils/gower_distance.py
imports: from typing import Hashable, List
  import numpy
  import pandas
  from deepchecks.utils.array_math import fast_sum_by_row
label_desc: Use image node_id 2 for calling a global function with example usage: calculate_nearest_neighbors_distances(data, cat_cols, numeric_cols, num_neighbors, samples_to_calc_neighbors_for) and returns: np,  indexes