class_docstr: Creates a node-level split with distributional shift based on a given
  node property, as proposed in the `"Evaluating Robustness and Uncertainty
  of Graph Models Under Structural Distributional Shifts"
  <https://arxiv.org/abs/2302.13875>`__ paper
  (functional name: :obj:`node_property_split`).
  
  It splits the nodes in a given graph into five non-intersecting parts
  based on their structural properties.
  This can be used for transductive node prediction tasks with distributional
  shifts.
  It considers the in-distribution (ID) and out-of-distribution (OOD) subsets
  of nodes.
  The ID subset includes training, validation and testing parts, while
  the OOD subset includes validation and testing parts.
  As a result, it creates five associated node mask vectors for each graph,
  three which are for the ID nodes (:obj:`id_train_mask`,
  :obj:`id_val_mask`, :obj:`id_test_mask`), and two which are for the OOD
  nodes (:obj:`ood_val_mask`, :obj:`ood_test_mask`).
  
  This class implements three particular strategies for inducing
  distributional shifts in a graph — based on **popularity**, **locality**
  or **density**.
  
  Args:
      property_name (str): The name of the node property to be used
          (:obj:`"popularity"`, :obj:`"locality"`, :obj:`"density"`).
      ratios ([float]): A list of five ratio values for ID training,
          ID validation, ID test, OOD validation and OOD test parts.
          The values must sum to :obj:`1.0`.
      ascending (bool, optional): Whether to sort nodes in ascending order
          of the node property, so that nodes with greater values of the
          property are considered to be OOD (default: :obj:`True`)
  
  Example:
  .. code-block:: python
  
      from torch_geometric.transforms import NodePropertySplit
      from torch_geometric.datasets.graph_generator import ERGraph
  
      data = ERGraph(num_nodes=1000, edge_prob=0.4)()
  
      property_name = 'popularity'
      ratios = [0.3, 0.1, 0.1, 0.3, 0.2]
      tranaform = NodePropertySplit(property_name, ratios)
  
      data = transform(data)
code: def __init__(
      self,
      property_name: str,
      ratios: List[float],
      ascending: bool = True,
  ):
      if property_name not in {"popularity", "locality", "density"}:
          raise ValueError(
              f"Unexpected 'property_name' " f"(got '{property_name}')"
          )
  
      if len(ratios) != 5:
          raise ValueError(
              f"'ratios' must contain 5 values " f"(got {len(ratios)})"
          )
  
      if sum(ratios) != 1.0:
          raise ValueError(
              f"'ratios' must sum to 1.0 (got {sum(ratios)})"
          )
  
      self.property_name = property_name
      self.compute_fn = _property_name_to_compute_fn[property_name]
      self.ratios = ratios
      self.ascending = ascending
  
file_path: pytorch_geometric/torch_geometric/transforms/node_property_split.py
imports: from typing import Any, Dict, List
  import torch
  from torch import Tensor
  from torch_geometric.data import Data
  from torch_geometric.data.datapipes import functional_transform
  from torch_geometric.transforms import BaseTransform
  from torch_geometric.utils import to_networkx
label_desc: Use image node_id 1 to create a new NodePropertySplit object from inherited base classes: BaseTransform with example: obj = NodePropertySplit(property_name, ratios, ascending)