code: def mutual_information(
      feature_list0: List[np.ndarray],
      feature_list1: List[np.ndarray],
      is_categorical_list0: List[bool],
      is_categorical_list1: List[bool],
      k: int = 3,
      estimate_method: str = "larger_data",
      weight_feature: Optional[np.ndarray] = None,
      filter_feature: Optional[np.ndarray] = None,
      output_each: bool = False,
      seed: Optional[int] = None,
  ) -> Union[float, Tuple[float, np.ndarray]]:
      """Computes MI between two lists of features (numpy arrays).
  
      The mutual information value is scaled by log(2) in the end so that the unit
      is bit.
  
      The paper (1) in the module doc string gives the method for computing MI
      between two lists of ordinal features. The paper (2) provides the method
      for computing MI between a list of ordinal features and a list of categorical
      features. For the general case, suppose we have ordinal feature set C0, C1,
      and categorical feature set D0, D1. Then we can derive
  
      I({C0,D0};{C1,D1}) = I({C0,C1};{D0,D1}) + I(C0;C1) + I(D0;D1) - I(C0;D0)
                           - I(C1;D1),
  
      where the right hand side terms can all be computed by using the methods in
      the two papers.
  
      Args:
        feature_list0: (list(np.ndarray)) A list of features.
        feature_list1: (list(np.ndarray)) A list of features.
        is_categorical_list0: (list(bool)) Whether the first list of features are
          categorical or not.
        is_categorical_list1: (list(bool)) Whether the second list of features are
          categorical or not.
        k: (int) The number of nearest neighbors. It has to be an integer no less
          than 3.
        estimate_method: (str) 'smaller_data' or 'larger_data' estimator in the
          above paper.
        weight_feature: (np.ndarray) A feature that contains weights for each
          sample.
        filter_feature: (np.ndarray) A feature that is used as the filter to drop
          all data where this filter has missing values. By default, it is None and
          no filtering is done.
        output_each: (bool) Whether to output the contribution from each individual
          sample. The output values are not scaled by the number of samples.
        seed: (int) Random seed for the tiny noise.
  
      Returns:
        (float | (float, np.ndarray)) The mutual information between the features in
            feature_list0 and feature_list1. If output_each is True, an np array of
            the contributions from all samples is also output, whose mean is equal
            to the mutual information.
      """
      _validate_args(
          feature_list0,
          feature_list1,
          is_categorical_list0,
          is_categorical_list1,
          k,
          estimate_method,
          weight_feature,
          filter_feature,
          output_each,
          seed,
      )
  
      (
          cf_list0,
          cf_list1,
          df_list0,
          df_list1,
          weights,
      ) = _feature_list_to_numpy_arrays(
          feature_list0,
          feature_list1,
          is_categorical_list0,
          is_categorical_list1,
          weight_feature,
          filter_feature,
      )
  
      # Try to reuse these data in later computations to avoid converting Feature to
      # numpy array multiple times.
      final_mi, each = _mi_for_arrays(
          cf_list0,
          cf_list1,
          df_list0,
          df_list1,
          weights,
          k,
          estimate_method,
          seed,
      )
      if output_each:
          return final_mi, each
      return final_mi
  
imports: import functools
  import itertools
  import math
  from typing import Any, List, Optional, Tuple, Union
  import uuid
  import numpy
  import pandas
  import scipy.special
  import sklearn.neighbors
label_desc: Use image node_id 1 for calling a global function with example usage: mutual_information(feature_list0, feature_list1, is_categorical_list0, is_categorical_list1, k, estimate_method, weight_feature, filter_feature, output_each, seed) and returns: final_mi, final_mi,  each