class_docstr: Stores all actual data and typing information for an entityset
  
  Attributes:
      id
      dataframe_dict
      relationships
      time_type
  
  Properties:
      metadata
class_docstr_tok: ["Stores","all","actual","data","and","typing","information","for","an","entityset","Attributes",":","id","dataframe_dict","relationships","time_type","Properties",":","metadata"]
docstr: Create a new dataframe and relationship from unique values of an existing column.
  
  Args:
      base_dataframe_name (str) : Dataframe name from which to split.
  
      new_dataframe_name (str): Name of the new dataframe.
  
      index (str): Column in old dataframe
          that will become index of new dataframe. Relationship
          will be created across this column.
  
      additional_columns (list[str]):
          List of column names to remove from
          base_dataframe and move to new dataframe.
  
      copy_columns (list[str]): List of
          column names to copy from old dataframe
          and move to new dataframe.
  
      make_time_index (bool or str, optional): Create time index for new dataframe based
          on time index in base_dataframe, optionally specifying which column in base_dataframe
          to use for time_index. If specified as True without a specific column name,
          uses the primary time index. Defaults to True if base dataframe has a time index.
  
      make_secondary_time_index (dict[str -> list[str]], optional): Create a secondary time index
          from key. Values of dictionary are the columns to associate with a secondary time index.
          Only one secondary time index is allowed. If None, only associate the time index.
  
      new_dataframe_time_index (str, optional): Rename new dataframe time index.
  
      new_dataframe_secondary_time_index (str, optional): Rename new dataframe secondary time index.
docstr_tok: ["Create","a","new","dataframe","and","relationship","from","unique","values","of","an","existing","column",".","Args",":","base_dataframe_name","(","str",")",":","Dataframe","name","from","which","to","split",".","new_dataframe_name","(","str",")",":","Name","of","the","new","dataframe",".","index","(","str",")",":","Column","in","old","dataframe","that","will","become","index","of","new","dataframe",".","Relationship","will","be","created","across","this","column",".","additional_columns","(","list","[","str","]",")",":","List","of","column","names","to","remove","from","base_dataframe","and","move","to","new","dataframe",".","copy_columns","(","list","[","str","]",")",":","List","of","column","names","to","copy","from","old","dataframe","and","move","to","new","dataframe",".","make_time_index","(","bool","or","str",",","optional",")",":","Create","time","index","for","new","dataframe","based","on","time","index","in","base_dataframe",",","optionally","specifying","which","column","in","base_dataframe","to","use","for","time_index",".","If","specified","as","True","without","a","specific","column","name",",","uses","the","primary","time","index",".","Defaults","to","True","if","base","dataframe","has","a","time","index",".","make_secondary_time_index","(","dict","[","str","-",">","list","[","str","]","]",",","optional",")",":","Create","a","secondary","time","index","from","key",".","Values","of","dictionary","are","the","columns","to","associate","with","a","secondary","time","index",".","Only","one","secondary","time","index","is","allowed",".","If","None",",","only","associate","the","time","index",".","new_dataframe_time_index","(","str",",","optional",")",":","Rename","new","dataframe","time","index",".","new_dataframe_secondary_time_index","(","str",",","optional",")",":","Rename","new","dataframe","secondary","time","index","."]
code: def normalize_dataframe(
      self,
      base_dataframe_name,
      new_dataframe_name,
      index,
      additional_columns=None,
      copy_columns=None,
      make_time_index=None,
      make_secondary_time_index=None,
      new_dataframe_time_index=None,
      new_dataframe_secondary_time_index=None,
  ):
      """Create a new dataframe and relationship from unique values of an existing column.
  
      Args:
          base_dataframe_name (str) : Dataframe name from which to split.
  
          new_dataframe_name (str): Name of the new dataframe.
  
          index (str): Column in old dataframe
              that will become index of new dataframe. Relationship
              will be created across this column.
  
          additional_columns (list[str]):
              List of column names to remove from
              base_dataframe and move to new dataframe.
  
          copy_columns (list[str]): List of
              column names to copy from old dataframe
              and move to new dataframe.
  
          make_time_index (bool or str, optional): Create time index for new dataframe based
              on time index in base_dataframe, optionally specifying which column in base_dataframe
              to use for time_index. If specified as True without a specific column name,
              uses the primary time index. Defaults to True if base dataframe has a time index.
  
          make_secondary_time_index (dict[str -> list[str]], optional): Create a secondary time index
              from key. Values of dictionary are the columns to associate with a secondary time index.
              Only one secondary time index is allowed. If None, only associate the time index.
  
          new_dataframe_time_index (str, optional): Rename new dataframe time index.
  
          new_dataframe_secondary_time_index (str, optional): Rename new dataframe secondary time index.
  
      """
      base_dataframe = self.dataframe_dict[base_dataframe_name]
      additional_columns = additional_columns or []
      copy_columns = copy_columns or []
  
      for list_name, col_list in {
          "copy_columns": copy_columns,
          "additional_columns": additional_columns,
      }.items():
          if not isinstance(col_list, list):
              raise TypeError(
                  "'{}' must be a list, but received type {}".format(
                      list_name,
                      type(col_list),
                  ),
              )
          if len(col_list) != len(set(col_list)):
              raise ValueError(
                  f"'{list_name}' contains duplicate columns. All columns must be unique.",
              )
          for col_name in col_list:
              if col_name == index:
                  raise ValueError(
                      "Not adding {} as both index and column in {}".format(
                          col_name,
                          list_name,
                      ),
                  )
  
      for col in additional_columns:
          if col == base_dataframe.ww.time_index:
              raise ValueError(
                  "Not moving {} as it is the base time index column. Perhaps, move the column to the copy_columns.".format(
                      col,
                  ),
              )
  
      if isinstance(make_time_index, str):
          if make_time_index not in base_dataframe.columns:
              raise ValueError(
                  "'make_time_index' must be a column in the base dataframe",
              )
          elif make_time_index not in additional_columns + copy_columns:
              raise ValueError(
                  "'make_time_index' must be specified in 'additional_columns' or 'copy_columns'",
              )
      if index == base_dataframe.ww.index:
          raise ValueError(
              "'index' must be different from the index column of the base dataframe",
          )
  
      transfer_types = {}
      # Types will be a tuple of (logical_type, semantic_tags, column_metadata, column_description)
      transfer_types[index] = (
          base_dataframe.ww.logical_types[index],
          base_dataframe.ww.semantic_tags[index],
          base_dataframe.ww.columns[index].metadata,
          base_dataframe.ww.columns[index].description,
      )
      for col_name in additional_columns + copy_columns:
          # Remove any existing time index tags
          transfer_types[col_name] = (
              base_dataframe.ww.logical_types[col_name],
              (
                  base_dataframe.ww.semantic_tags[col_name]
                  - {"time_index"}
              ),
              base_dataframe.ww.columns[col_name].metadata,
              base_dataframe.ww.columns[col_name].description,
          )
  
      # create and add new dataframe
      new_dataframe = self[base_dataframe_name].copy()
  
      if (
          make_time_index is None
          and base_dataframe.ww.time_index is not None
      ):
          make_time_index = True
  
      if isinstance(make_time_index, str):
          # Set the new time index to make_time_index.
          base_time_index = make_time_index
          new_dataframe_time_index = make_time_index
          already_sorted = (
              new_dataframe_time_index == base_dataframe.ww.time_index
          )
      elif make_time_index:
          # Create a new time index based on the base dataframe time index.
          base_time_index = base_dataframe.ww.time_index
          if new_dataframe_time_index is None:
              new_dataframe_time_index = "first_%s_time" % (
                  base_dataframe.ww.name
              )
  
          already_sorted = True
  
          assert (
              base_dataframe.ww.time_index is not None
          ), "Base dataframe doesn't have time_index defined"
  
          if base_time_index not in [col for col in copy_columns]:
              copy_columns.append(base_time_index)
  
              time_index_types = (
                  base_dataframe.ww.logical_types[
                      base_dataframe.ww.time_index
                  ],
                  base_dataframe.ww.semantic_tags[
                      base_dataframe.ww.time_index
                  ],
                  base_dataframe.ww.columns[
                      base_dataframe.ww.time_index
                  ].metadata,
                  base_dataframe.ww.columns[
                      base_dataframe.ww.time_index
                  ].description,
              )
          else:
              # If base_time_index is in copy_columns then we've already added the transfer types
              # but since we're changing the name, we have to remove it
              time_index_types = transfer_types[
                  base_dataframe.ww.time_index
              ]
              del transfer_types[base_dataframe.ww.time_index]
  
          transfer_types[new_dataframe_time_index] = time_index_types
  
      else:
          new_dataframe_time_index = None
          already_sorted = False
  
      if (
          new_dataframe_time_index is not None
          and new_dataframe_time_index == index
      ):
          raise ValueError(
              "time_index and index cannot be the same value, %s"
              % (new_dataframe_time_index),
          )
  
      selected_columns = (
          [index]
          + [col for col in additional_columns]
          + [col for col in copy_columns]
      )
  
      new_dataframe = new_dataframe.dropna(subset=[index])
      new_dataframe2 = new_dataframe.drop_duplicates(
          index, keep="first"
      )[selected_columns]
  
      if make_time_index:
          new_dataframe2 = new_dataframe2.rename(
              columns={base_time_index: new_dataframe_time_index},
          )
      if make_secondary_time_index:
          assert (
              len(make_secondary_time_index) == 1
          ), "Can only provide 1 secondary time index"
          secondary_time_index = list(make_secondary_time_index.keys())[
              0
          ]
  
          secondary_columns = [index, secondary_time_index] + list(
              make_secondary_time_index.values(),
          )[0]
          secondary_df = new_dataframe.drop_duplicates(
              index, keep="last"
          )[secondary_columns]
          if new_dataframe_secondary_time_index:
              secondary_df = secondary_df.rename(
                  columns={
                      secondary_time_index: new_dataframe_secondary_time_index
                  },
              )
              secondary_time_index = new_dataframe_secondary_time_index
          else:
              new_dataframe_secondary_time_index = secondary_time_index
          secondary_df = secondary_df.set_index(index)
          new_dataframe = new_dataframe2.join(secondary_df, on=index)
      else:
          new_dataframe = new_dataframe2
  
      base_dataframe_index = index
  
      if make_secondary_time_index:
          old_ti_name = list(make_secondary_time_index.keys())[0]
          ti_cols = list(make_secondary_time_index.values())[0]
          ti_cols = [
              c if c != old_ti_name else secondary_time_index
              for c in ti_cols
          ]
          make_secondary_time_index = {secondary_time_index: ti_cols}
  
      if is_instance(new_dataframe, ps, "DataFrame"):
          already_sorted = False
  
      # will initialize Woodwork on this DataFrame
      logical_types = {}
      semantic_tags = {}
      column_metadata = {}
      column_descriptions = {}
      for col_name, (
          ltype,
          tags,
          metadata,
          description,
      ) in transfer_types.items():
          logical_types[col_name] = ltype
          semantic_tags[col_name] = tags - {"time_index"}
          column_metadata[col_name] = copy.deepcopy(metadata)
          column_descriptions[col_name] = description
  
      new_dataframe.ww.init(
          name=new_dataframe_name,
          index=index,
          already_sorted=already_sorted,
          time_index=new_dataframe_time_index,
          logical_types=logical_types,
          semantic_tags=semantic_tags,
          column_metadata=column_metadata,
          column_descriptions=column_descriptions,
      )
  
      self.add_dataframe(
          new_dataframe,
          secondary_time_index=make_secondary_time_index,
      )
  
      self.dataframe_dict[base_dataframe_name] = self.dataframe_dict[
          base_dataframe_name
      ].ww.drop(additional_columns)
  
      self.dataframe_dict[base_dataframe_name].ww.add_semantic_tags(
          {base_dataframe_index: "foreign_key"},
      )
  
      self.add_relationship(
          new_dataframe_name,
          index,
          base_dataframe_name,
          base_dataframe_index,
      )
      self.reset_data_description()
      return self
  
imports: import copy
  import logging
  import warnings
  from collections import defaultdict
  import numpy
  import pandas
  from woodwork import init_series
  from woodwork.logical_types import Datetime, LatLong
  from featuretools.entityset import deserialize, serialize
  from featuretools.entityset.relationship import Relationship, RelationshipPath
  from featuretools.feature_base.feature_base import _ES_REF
  from featuretools.utils.gen_utils import Library, import_or_none, is_instance
  from featuretools.utils.plot_utils import check_graphviz, get_graphviz_format, save_graph
  from featuretools.utils.wrangle import _check_timedelta
label_desc: Use image node_id 31 for calling the EntitySet obj's underlying member method code with example usage: obj.normalize_dataframe(base_dataframe_name, new_dataframe_name, index, additional_columns, copy_columns, make_time_index, make_secondary_time_index, new_dataframe_time_index, new_dataframe_secondary_time_index) and returns: self