class_docstr: Class which stores vertex and edge properties that can be used to construct
  Graphs from individual property selections and used later to annotate graph
  algorithm results with corresponding properties.
class_docstr_tok: ["Class","which","stores","vertex","and","edge","properties","that","can","be","used","to","construct","Graphs","from","individual","property","selections","and","used","later","to","annotate","graph","algorithm","results","with","corresponding","properties","."]
docstr: Add a dataframe describing edge properties to the PropertyGraph.
  Columns not specified as vertex columns are considered properties.
  
  Parameters
  ----------
  dataframe : DataFrame-compatible instance
      A DataFrame instance with a compatible Pandas-like DataFrame
      interface.
  vertex_col_names : list of strings
      The column names that contain the values to be used as the source
      and destination vertex IDs for the edges.
  edge_id_col_name : string, optional
      The column name that contains the values to be used as edge IDs,
      or the name of the index if the index is edge IDs.
      Specifying the index may be more efficient.
      If unspecified, edge IDs will be automatically assigned.
      Currently, all edge data must be added with the same method: either
      with automatically generated IDs, or from user-provided edge IDs.
  type_name : string, optional
      The name to be assigned to the type of property being added. For
      example, if dataframe contains data about transactions, type_name
      might be "transactions". If not specified, the type of properties
      will be added as the empty string "".
  property_columns : list of strings, optional
      List of column names in the dataframe to be added as properties. All
      other columns in dataframe will be ignored. If not specified, all
      property columns in the dataframe are added.
  vector_properties : dict of string to list of strings, optional
      A dict of vector properties to create from columns in the dataframe.
      Each vector property stores an array for each edge.
      The dict keys are the new vector property names, and the dict values
      should be Python lists of column names from which to create the vector
      property. Columns used to create vector properties won't be added to
      the property graph by default, but may be included as properties by
      including them in the property_columns argument.
      Use ``PropertyGraph.edge_vector_property_to_array`` to convert an
      edge vector property to an array.
  vector_property : string, optional
      If provided, all columns not included in other arguments will be used
      to create a vector property with the given name. This is often used
      for convenience instead of ``vector_properties`` when all input
      properties should be converted to a vector property.
  
  Returns
  -------
  None
  
  Examples
  --------
  >>> import cugraph
  >>> import cudf
  >>> from cugraph.experimental import PropertyGraph
  >>> df = cudf.DataFrame(columns=["src", "dst", "some_property"],
  ...                     data=[(99, 22, "a"),
  ...                           (98, 34, "b"),
  ...                           (97, 56, "c"),
  ...                           (96, 88, "d"),
  ...                          ])
  >>> pG = PropertyGraph()
  >>> pG.add_edge_data(df, vertex_col_names=("src", "dst"))
  >>> pG.get_num_vertices()
  8
code: def add_edge_data(
      self,
      dataframe,
      vertex_col_names,
      edge_id_col_name=None,
      type_name=None,
      property_columns=None,
      vector_properties=None,
      vector_property=None,
  ):
      """
      Add a dataframe describing edge properties to the PropertyGraph.
      Columns not specified as vertex columns are considered properties.
  
      Parameters
      ----------
      dataframe : DataFrame-compatible instance
          A DataFrame instance with a compatible Pandas-like DataFrame
          interface.
      vertex_col_names : list of strings
          The column names that contain the values to be used as the source
          and destination vertex IDs for the edges.
      edge_id_col_name : string, optional
          The column name that contains the values to be used as edge IDs,
          or the name of the index if the index is edge IDs.
          Specifying the index may be more efficient.
          If unspecified, edge IDs will be automatically assigned.
          Currently, all edge data must be added with the same method: either
          with automatically generated IDs, or from user-provided edge IDs.
      type_name : string, optional
          The name to be assigned to the type of property being added. For
          example, if dataframe contains data about transactions, type_name
          might be "transactions". If not specified, the type of properties
          will be added as the empty string "".
      property_columns : list of strings, optional
          List of column names in the dataframe to be added as properties. All
          other columns in dataframe will be ignored. If not specified, all
          property columns in the dataframe are added.
      vector_properties : dict of string to list of strings, optional
          A dict of vector properties to create from columns in the dataframe.
          Each vector property stores an array for each edge.
          The dict keys are the new vector property names, and the dict values
          should be Python lists of column names from which to create the vector
          property. Columns used to create vector properties won't be added to
          the property graph by default, but may be included as properties by
          including them in the property_columns argument.
          Use ``PropertyGraph.edge_vector_property_to_array`` to convert an
          edge vector property to an array.
      vector_property : string, optional
          If provided, all columns not included in other arguments will be used
          to create a vector property with the given name. This is often used
          for convenience instead of ``vector_properties`` when all input
          properties should be converted to a vector property.
  
      Returns
      -------
      None
  
      Examples
      --------
      >>> import cugraph
      >>> import cudf
      >>> from cugraph.experimental import PropertyGraph
      >>> df = cudf.DataFrame(columns=["src", "dst", "some_property"],
      ...                     data=[(99, 22, "a"),
      ...                           (98, 34, "b"),
      ...                           (97, 56, "c"),
      ...                           (96, 88, "d"),
      ...                          ])
      >>> pG = PropertyGraph()
      >>> pG.add_edge_data(df, vertex_col_names=("src", "dst"))
      >>> pG.get_num_vertices()
      8
      """
      if type(dataframe) not in _dataframe_types:
          raise TypeError(
              "dataframe must be one of the following types: "
              f"{_dataframe_types}, got: {type(dataframe)}"
          )
      if type(vertex_col_names) not in [list, tuple]:
          raise TypeError(
              "vertex_col_names must be a list or tuple, got: "
              f"{type(vertex_col_names)}"
          )
      if edge_id_col_name is not None:
          if not isinstance(edge_id_col_name, str):
              raise TypeError(
                  "edge_id_col_name must be a string, got: "
                  f"{type(edge_id_col_name)}"
              )
          if edge_id_col_name not in dataframe.columns:
              if edge_id_col_name != dataframe.index.name:
                  raise ValueError(
                      "edge_id_col_name argument not in columns, "
                      f"got {edge_id_col_name!r}"
                  )
              index_is_set = True
          else:
              index_is_set = False
      invalid_columns = set(vertex_col_names).difference(
          dataframe.columns
      )
      if invalid_columns:
          raise ValueError(
              "vertex_col_names contains column(s) not found "
              f"in dataframe: {list(invalid_columns)}"
          )
      if type_name is not None and not isinstance(type_name, str):
          raise TypeError(
              f"type_name must be a string, got: {type(type_name)}"
          )
      if type_name is None:
          type_name = self._default_type_name
      if property_columns:
          if type(property_columns) is not list:
              raise TypeError(
                  f"property_columns must be a list, got: {type(property_columns)}"
              )
          invalid_columns = set(property_columns).difference(
              dataframe.columns
          )
          if invalid_columns:
              raise ValueError(
                  "property_columns contains column(s) not found in dataframe: "
                  f"{list(invalid_columns)}"
              )
          existing_vectors = (
              set(property_columns)
              & self.__vertex_vector_property_lengths.keys()
          )
          if existing_vectors:
              raise ValueError(
                  "Non-vector property columns cannot be added to existing "
                  f"vector properties: {', '.join(sorted(existing_vectors))}"
              )
  
      # Save the DataFrame and Series types for future instantiations
      if self.__dataframe_type is None or self.__series_type is None:
          self.__dataframe_type = type(dataframe)
          self.__series_type = type(dataframe[dataframe.columns[0]])
      else:
          if type(dataframe) is not self.__dataframe_type:
              raise TypeError(
                  f"dataframe is type {type(dataframe)} but "
                  "the PropertyGraph was already initialized "
                  f"using type {self.__dataframe_type}"
              )
      if (
          self.__is_edge_id_autogenerated is False
          and edge_id_col_name is None
      ):
          raise NotImplementedError(
              "Unable to automatically generate edge IDs. "
              "`edge_id_col_name` must be specified if edge data has been "
              "previously added with edge_id_col_name."
          )
      if (
          self.__is_edge_id_autogenerated is True
          and edge_id_col_name is not None
      ):
          raise NotImplementedError(
              "Invalid use of `edge_id_col_name`. Edge data has already "
              "been added with automatically generated IDs, so now all "
              "edge data must be added using automatically generated IDs."
          )
  
      TCN = self.type_col_name
      if vector_properties is not None:
          invalid_keys = {self.src_col_name, self.dst_col_name, TCN}
          if property_columns:
              invalid_keys.update(property_columns)
          self._check_vector_properties(
              dataframe,
              vector_properties,
              self.__edge_vector_property_lengths,
              invalid_keys,
          )
      if vector_property is not None:
          invalid_keys = {
              self.src_col_name,
              self.dst_col_name,
              TCN,
              vertex_col_names[0],
              vertex_col_names[1],
          }
          if property_columns:
              invalid_keys.update(property_columns)
          if vector_properties:
              invalid_keys.update(*vector_properties.values())
          d = {
              vector_property: [
                  col
                  for col in dataframe.columns
                  if col not in invalid_keys
              ]
          }
          invalid_keys.difference_update(vertex_col_names)
          self._check_vector_properties(
              dataframe,
              d,
              self.__edge_vector_property_lengths,
              invalid_keys,
          )
          # Update vector_properties, but don't mutate the original
          if vector_properties is not None:
              d.update(vector_properties)
          vector_properties = d
  
      # Clear the cached value for num_vertices since more could be added in
      # this method. This method cannot affect __node_type_value_counts
      self.__num_vertices = None
      self.__edge_type_value_counts = None  # Could update instead
  
      # Add `type_name` to the categorical dtype if necessary
      is_first_data = self.__edge_prop_dataframe is None
      if is_first_data:
          self.__edge_prop_dataframe = self.__dataframe_type(
              columns=[self.src_col_name, self.dst_col_name, TCN]
          )
          # Initialize the new columns to the same dtype as the appropriate
          # column in the incoming dataframe, since the initial merge may not
          # result in the same dtype. (see
          # https://github.com/rapidsai/cudf/issues/9981)
          self.__edge_prop_dataframe = self.__update_dataframe_dtypes(
              self.__edge_prop_dataframe,
              {
                  self.src_col_name: dataframe[
                      vertex_col_names[0]
                  ].dtype,
                  self.dst_col_name: dataframe[
                      vertex_col_names[1]
                  ].dtype,
              },
          )
          self.__edge_prop_dataframe.index.name = self.edge_id_col_name
  
          # Use categorical dtype for the type column
          if self.__series_type is cudf.Series:
              cat_class = cudf.CategoricalDtype
          else:
              cat_class = pd.CategoricalDtype
          cat_dtype = cat_class([type_name], ordered=False)
          self.__is_edge_id_autogenerated = edge_id_col_name is None
      else:
          cat_dtype = self.__update_categorical_dtype(
              self.__edge_prop_dataframe, TCN, type_name
          )
  
      # NOTE: This copies the incoming DataFrame in order to add the new
      # columns. The copied DataFrame is then merged (another copy) and then
      # deleted when out-of-scope.
      tmp_df = dataframe.copy(deep=True)
      tmp_df[self.src_col_name] = tmp_df[vertex_col_names[0]]
      tmp_df[self.dst_col_name] = tmp_df[vertex_col_names[1]]
  
      tmp_df[TCN] = self.__series_type(
          type_name, index=tmp_df.index
      ).astype(cat_dtype)
  
      # Add unique edge IDs to the new rows. This is just a count for each
      # row starting from the last edge ID value, with initial edge ID 0.
      if edge_id_col_name is None:
          start_eid = (
              0 if self.__last_edge_id is None else self.__last_edge_id
          )
          end_eid = start_eid + len(tmp_df)  # exclusive
          if self.__series_type is cudf.Series:
              index_class = cudf.RangeIndex
          else:
              index_class = pd.RangeIndex
          tmp_df.index = index_class(
              start_eid, end_eid, name=self.edge_id_col_name
          )
          self.__last_edge_id = end_eid
      else:
          if not index_is_set:
              tmp_df.set_index(edge_id_col_name, inplace=True)
          tmp_df.index.name = self.edge_id_col_name
  
      if property_columns:
          # all columns
          column_names_to_drop = set(tmp_df.columns)
          # remove the ones to keep
          column_names_to_drop.difference_update(
              property_columns
              + [self.src_col_name, self.dst_col_name, TCN]
          )
      else:
          column_names_to_drop = {
              vertex_col_names[0],
              vertex_col_names[1],
          }
  
      if vector_properties:
          # Drop vector property source columns by default
          more_to_drop = set().union(*vector_properties.values())
          if property_columns is not None:
              more_to_drop.difference_update(property_columns)
          column_names_to_drop |= more_to_drop
          column_names_to_drop -= vector_properties.keys()
          self._create_vector_properties(tmp_df, vector_properties)
  
      tmp_df.drop(labels=column_names_to_drop, axis=1, inplace=True)
  
      # Save the original dtypes for each new column so they can be restored
      # prior to constructing subgraphs (since column dtypes may get altered
      # during merge to accommodate NaN values).
      if is_first_data:
          new_col_info = tmp_df.dtypes.items()
      else:
          new_col_info = self.__get_new_column_dtypes(
              tmp_df, self.__edge_prop_dataframe
          )
      self.__edge_prop_dtypes.update(new_col_info)
  
      tmp_df = self.__update_dataframe_dtypes(
          tmp_df, self.__edge_prop_dtypes
      )
  
      if is_first_data:
          self.__edge_prop_dataframe = tmp_df
      else:
          # Join on edge ids (the index)
          # TODO: can we automagically determine when we to use concat?
          df = self.__edge_prop_dataframe.join(
              tmp_df, how="outer", rsuffix="_NEW_"
          )
          cols = self.__edge_prop_dataframe.columns.intersection(
              tmp_df.columns
          ).to_list()
          rename_cols = {f"{col}_NEW_": col for col in cols}
          new_cols = list(rename_cols)
          sub_df = df[new_cols].rename(columns=rename_cols)
          df.drop(columns=new_cols, inplace=True)
          # This only adds data--it doesn't replace existing data
          df.fillna(sub_df, inplace=True)
          self.__edge_prop_dataframe = df
  
      # Update the edge eval dict with the latest column instances
      self._update_eval_dict(
          self.__edge_prop_eval_dict,
          self.__edge_prop_dataframe,
          self.edge_id_col_name,
      )
  
imports: import cudf
  import numpy
  import cugraph
  from cugraph.utilities.utils import import_optional, MissingModule, create_list_series_from_2d_ar
  from typing import Union
label_desc: Use image node_id 21 for calling the EXPERIMENTAL__PropertyGraph obj's underlying member method code with example usage: obj.add_edge_data(dataframe, vertex_col_names, edge_id_col_name, type_name, property_columns, vector_properties, vector_property) without return types