code: def from_pandas(
      X: pd.DataFrame,
      dist_type_schema: Dict[Union[str, int], str] = None,
      lasso_beta: float = 0.0,
      ridge_beta: float = 0.0,
      use_bias: bool = False,
      hidden_layer_units: Iterable[int] = None,
      max_iter: int = 100,
      w_threshold: float = None,
      tabu_edges: List[Tuple[str, str]] = None,
      tabu_parent_nodes: List[str] = None,
      tabu_child_nodes: List[str] = None,
      use_gpu: bool = True,
      **kwargs,
  ) -> StructureModel:
      """
      Learn the `StructureModel`, the graph structure describing conditional dependencies between variables
      in data presented as a pandas dataframe.
  
      The optimisation is to minimise a score function :math:`F(W)` over the graph's
      weighted adjacency matrix, :math:`W`, subject to the a constraint function :math:`h(W)`,
      where :math:`h(W) == 0` characterises an acyclic graph.
      :math:`h(W) > 0` is a continuous, differentiable function that encapsulated how acyclic the graph is
      (less == more acyclic).
      Full details of this approach to structure learning are provided in the publication:
  
      Based on DAGs with NO TEARS.
      @inproceedings{zheng2018dags,
          author = {Zheng, Xun and Aragam, Bryon and Ravikumar, Pradeep and Xing, Eric P.},
          booktitle = {Advances in Neural Information Processing Systems},
          title = {{DAGs with NO TEARS: Continuous Optimization for Structure Learning}},
          year = {2018},
          codebase = {https://github.com/xunzheng/notears}
      }
  
      Args:
          X: 2d input data, axis=0 is data rows, axis=1 is data columns. Data must be row oriented.
  
          dist_type_schema: The dist type schema corresponding to the passed in data X.
          It maps the pandas column name in X to the string alias of a dist type.
          A list of alias names can be found in ``dist_type/__init__.py``.
          If None, assumes that all data in X is continuous.
  
          lasso_beta: Constant that multiplies the lasso term (l1 regularisation).
          NOTE when using nonlinearities, the l1 loss only applies to the dag_layer.
  
          use_bias: Whether to fit a bias parameter in the NOTEARS algorithm.
  
          ridge_beta: Constant that multiplies the ridge term (l2 regularisation).
          When using nonlinear layers use of this parameter is recommended.
  
          hidden_layer_units: An iterable where its length determine the number of layers used,
          and the numbers determine the number of nodes used for the layer in order.
  
          w_threshold: fixed threshold for absolute edge weights.
  
          max_iter: max number of dual ascent steps during optimisation.
  
          tabu_edges: list of edges(from, to) not to be included in the graph.
  
          tabu_parent_nodes: list of nodes banned from being a parent of any other nodes.
  
          tabu_child_nodes: list of nodes banned from being a child of any other nodes.
  
          use_gpu: use gpu if it is set to True and CUDA is available
  
          **kwargs: additional arguments for NOTEARS MLP model
  
      Returns:
           StructureModel: graph of conditional dependencies between data variables.
  
      Raises:
          ValueError: If X does not contain data.
      """
  
      data = deepcopy(X)
  
      # if dist_type_schema is not None, convert dist_type_schema from cols to idx
      dist_type_schema = (
          dist_type_schema
          if dist_type_schema is None
          else {
              X.columns.get_loc(col): alias
              for col, alias in dist_type_schema.items()
          }
      )
      non_numeric_cols = data.select_dtypes(exclude="number").columns
  
      if len(non_numeric_cols) > 0:
          raise ValueError(
              "All columns must have numeric data. "
              f"Consider mapping the following columns to int {non_numeric_cols}"
          )
  
      col_idx = {c: i for i, c in enumerate(data.columns)}
      idx_col = {i: c for c, i in col_idx.items()}
  
      if tabu_edges:
          tabu_edges = [(col_idx[u], col_idx[v]) for u, v in tabu_edges]
      if tabu_parent_nodes:
          tabu_parent_nodes = [col_idx[n] for n in tabu_parent_nodes]
      if tabu_child_nodes:
          tabu_child_nodes = [col_idx[n] for n in tabu_child_nodes]
  
      g = from_numpy(
          X=data.values,
          dist_type_schema=dist_type_schema,
          lasso_beta=lasso_beta,
          ridge_beta=ridge_beta,
          use_bias=use_bias,
          hidden_layer_units=hidden_layer_units,
          w_threshold=w_threshold,
          max_iter=max_iter,
          tabu_edges=tabu_edges,
          tabu_parent_nodes=tabu_parent_nodes,
          tabu_child_nodes=tabu_child_nodes,
          use_gpu=use_gpu,
          **kwargs,
      )
  
      # set comprehension to ensure only unique dist types are extraced
      # NOTE: this prevents double-renaming caused by the same dist type used on expanded columns
      unique_dist_types = {
          node[1]["dist_type"] for node in g.nodes(data=True)
      }
      # use the dist types to update the idx_col mapping
      idx_col_expanded = deepcopy(idx_col)
      for dist_type in unique_dist_types:
          idx_col_expanded = dist_type.update_idx_col(idx_col_expanded)
  
      sm = StructureModel()
      # add expanded set of nodes
      sm.add_nodes_from(list(idx_col_expanded.values()))
  
      # recover the edge weights from g
      for u, v, edge_dict in g.edges.data(True):
          sm.add_edge(
              idx_col_expanded[u],
              idx_col_expanded[v],
              origin="learned",
              weight=edge_dict["weight"],
              mean_effect=edge_dict["mean_effect"],
          )
  
      # retrieve all graphs attrs
      for key, val in g.graph.items():
          sm.graph[key] = val
  
      # recover the node biases from g
      for node in g.nodes(data=True):
          node_name = idx_col_expanded[node[0]]
          sm.nodes[node_name]["bias"] = node[1]["bias"]
  
      # recover and preseve the node dist_types
      for node_data in g.nodes(data=True):
          node_name = idx_col_expanded[node_data[0]]
          sm.nodes[node_name]["dist_type"] = node_data[1]["dist_type"]
  
      # recover the collapsed model from g
      sm_collapsed = StructureModel()
      sm_collapsed.add_nodes_from(list(idx_col.values()))
      for u, v, edge_dict in g.graph["graph_collapsed"].edges.data(
          True
      ):
          sm_collapsed.add_edge(
              idx_col[u],
              idx_col[v],
              origin="learned",
              weight=edge_dict["weight"],
          )
      sm.graph["graph_collapsed"] = sm_collapsed
  
      return sm
  
file_path: causalnex/causalnex/structure/pytorch/notears.py
imports: import logging
  from copy import deepcopy
  from typing import Dict, Iterable, List, Tuple, Union
  import numpy
  import pandas
  from sklearn.utils import check_array
  from causalnex.structure.pytorch.core import NotearsMLP
  from causalnex.structure.pytorch.dist_type import DistTypeContinuous, dist_type_aliases
  from causalnex.structure.structuremodel import StructureModel
label_desc: Use image node_id 2 for calling a global function with example usage: from_pandas(X, dist_type_schema, lasso_beta, ridge_beta, use_bias, hidden_layer_units, max_iter, w_threshold, tabu_edges, tabu_parent_nodes, tabu_child_nodes, use_gpu) and returns: sm