code: def add_row(self, label, row_data, columns=""):
          
          # use provided column order, making sure you don't lose any values
          # from self.df.columns
          if len(columns):
              if sorted(self.df.columns) == sorted(columns):
                  self.df.columns = columns
              else:
                  new_columns = []
                  new_columns.extend(columns)
                  for col in self.df.columns:
                      if col not in new_columns:
                          new_columns.append(col)
          # makes sure all columns have data or None
          if sorted(row_data.keys()) != sorted(self.df.columns):
              # add any new column names
              for key in row_data:
                  if key not in self.df.columns:
                      self.df[key] = None
              # add missing column names into row_data
              for col_label in self.df.columns:
                  if col_label not in list(row_data.keys()):
                      row_data[col_label] = None
  
          # (make sure you are working with strings)
          self.df.index = self.df.index.astype(str)
          label = str(label)
  
          # create a new row with suffix "new"
          # (this ensures that you get a unique, new row,
          #  instead of adding on to an existing row with the same label)
          self.df.loc[label + "new"] = pd.Series(row_data)
          # rename it to be correct
          self.df.rename(index={label + "new": label}, inplace=True)
          # use next line to sort index inplace
          #self.df.sort_index(inplace=True)
          return self.df
docs: Add a row with data.
          If any new keys are present in row_data dictionary,
          that column will be added to the dataframe.
          This is done inplace