code: def distribution_change_of_graphs(
      causal_model_old: ProbabilisticCausalModel,
      causal_model_new: ProbabilisticCausalModel,
      target_node: Any,
      num_samples: int = 2000,
      difference_estimation_func: Callable[
          [np.ndarray, np.ndarray], float
      ] = auto_estimate_kl_divergence,
      shapley_config: Optional[ShapleyConfig] = None,
      graph_factory: Callable[[Any], DirectedGraph] = nx.DiGraph,
  ) -> Dict[Any, float]:
      """Attributes the change of the marginal distribution of target_node to upstream nodes based on the distributions
      generated by the 'old' and 'new' causal graphs. These graphs are assumed to represent the same causal structure and
      to be fitted on the respective data.
  
      Note: This method creates a copy of the given causal models, i.e. the original objects will not be modified.
  
      Related paper:
      Budhathoki, K., Janzing, D., Bloebaum, P., & Ng, H. (2021). Why did the distribution change?
      arXiv preprint arXiv:2102.13384.
  
      :param causal_model_old: The ProbabilisticCausalModel fitted on the 'old' data.
      :param causal_model_new: The ProbabilisticCausalModel fitted on the 'new' data.
      :param target_node: Node of interest for attributing the marginal distribution change.
      :param num_samples: Number of samples used for the estimation. This can have a significant influence on the runtime
                          and accuracy.
      :param difference_estimation_func: Function for quantifying the distribution change. This function should expect
                                         two inputs which represent samples from two different distributions. An example
                                         could be the KL divergence.
      :param shapley_config: Config for the Shapley estimator.
      :param graph_factory: Allows customization in case a graph class different than networkx.DiGraph should be used.
                            This function *must* copy nodes and edges. Attributes of nodes will be overridden in the copy,
                            so the algorithm is independent of the attribute copy behavior of this factory.
      :return: A dictionary containing the contributions of upstream nodes to the marginal distribution change in the
               target node.
      """
      validate_causal_dag(causal_model_old.graph)
      validate_causal_dag(causal_model_new.graph)
  
      return _estimate_marginal_distribution_change(
          ProbabilisticCausalModel(
              node_connected_subgraph_view(
                  causal_model_old.graph, target_node
              )
          ),
          ProbabilisticCausalModel(
              node_connected_subgraph_view(
                  causal_model_new.graph, target_node
              )
          ),
          target_node,
          num_samples,
          difference_estimation_func,
          shapley_config,
          graph_factory,
      )
  
imports: import logging
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
  import networkx
  import numpy
  import pandas
  from numpy.matlib import repmat
  from statsmodels.stats.multitest import multipletests
  from tqdm import tqdm
  from dowhy.gcm.auto import AssignmentQuality, assign_causal_mechanism_node, assign_causal_mechanisms
  from dowhy.gcm.causal_mechanisms import ConditionalStochasticModel
  from dowhy.gcm.causal_models import PARENTS_DURING_FIT, ProbabilisticCausalModel, clone_causal_models, validate_causal_dag
  from dowhy.gcm.divergence import auto_estimate_kl_divergence
  from dowhy.gcm.fitting_sampling import draw_samples, fit_causal_model_of_target
  from dowhy.gcm.independence_test.kernel import kernel_based
  from dowhy.gcm.shapley import ShapleyConfig, estimate_shapley_values
  from dowhy.gcm.util.general import shape_into_2d
  from dowhy.graph import DirectedGraph, get_ordered_predecessors, is_root_node, node_connected_subgraph_view
label_desc: Use image node_id 3 for calling a global function with example usage: distribution_change_of_graphs(causal_model_old, causal_model_new, target_node, num_samples, difference_estimation_func, shapley_config, graph_factory) and returns: _estimate_marginal_distribution_change