docstr: Train the auto_timeseries object
  # TODO: Complete docstring
  
  :param traindata Path for the data file or a dataframe. It accepts both.
  :type traindata Union[str, pd.DataFrame]
  
  :param ts_column Name of the datetime column in your dataset.
      If it is of type 'str', it will be treated as a column name.
      If it is of type 'int', it will be treated as the column number.
      If it is of type 'List', the first one will be picked and will be treated as the column name.
  :type ts_column Union[str, int, List[str]]
  
  :param target: Name of the column you are trying to predict. Target could also be the only column in your data
  
  :type target Union[str, List[str]]
      If it is of type 'str', it will be treated as a column name.
      If it is of type 'List', the first one will be picked and will be treated as the column name.
  
  :param cv Number of folds to use for cross validation.
      Number of observations in the Validation set for each fold = forecast period
      default is 5 fold cross validation.
  :type cv Optional[int]
  
  :param sep: Note that optionally you can give a separator for the data in your file.
      Default is None which treats the sep as a comma (datafile as a 'csv').
  :type sep Optional[str]
docstr_tok: ["Train","the","auto_timeseries","object","#","TODO",":","Complete","docstring",":","param","traindata","Path","for","the","data","file","or","a","dataframe",".","It","accepts","both",".",":","type","traindata","Union","[","str",",","pd.DataFrame","]",":","param","ts_column","Name","of","the","datetime","column","in","your","dataset",".","If","it","is","of","type","'str","'",",","it","will","be","treated","as","a","column","name",".","If","it","is","of","type","'int","'",",","it","will","be","treated","as","the","column","number",".","If","it","is","of","type","'List","'",",","the","first","one","will","be","picked","and","will","be","treated","as","the","column","name",".",":","type","ts_column","Union","[","str",",","int",",","List","[","str","]","]",":","param","target",":","Name","of","the","column","you","are","trying","to","predict",".","Target","could","also","be","the","only","column","in","your","data",":","type","target","Union","[","str",",","List","[","str","]","]","If","it","is","of","type","'str","'",",","it","will","be","treated","as","a","column","name",".","If","it","is","of","type","'List","'",",","the","first","one","will","be","picked","and","will","be","treated","as","the","column","name",".",":","param","cv","Number","of","folds","to","use","for","cross","validation",".","Number","of","observations","in","the","Validation","set","for","each","fold","=","forecast","period","default","is","5","fold","cross","validation",".",":","type","cv","Optional","[","int","]",":","param","sep",":","Note","that","optionally","you","can","give","a","separator","for","the","data","in","your","file",".","Default","is","None","which","treats","the","sep","as","a","comma","(","datafile","as","a","'csv","'",")",".",":","type","sep","Optional","[","str","]"]
code: def fit(
      self,
      traindata: Union[str, pd.DataFrame],
      ts_column: Union[str, int, List[str]],
      target: Union[str, List[str]],
      sep: Optional[
          str
      ] = ",",  ## default is comma, string can be anything.
      cv: Optional[
          int
      ] = 5,  ### Integer field cannot be defaulted to None
  ):
      """
      Train the auto_timeseries object
      # TODO: Complete docstring
  
      :param traindata Path for the data file or a dataframe. It accepts both.
      :type traindata Union[str, pd.DataFrame]
  
      :param ts_column Name of the datetime column in your dataset.
          If it is of type 'str', it will be treated as a column name.
          If it is of type 'int', it will be treated as the column number.
          If it is of type 'List', the first one will be picked and will be treated as the column name.
      :type ts_column Union[str, int, List[str]]
  
      :param target: Name of the column you are trying to predict. Target could also be the only column in your data
  
      :type target Union[str, List[str]]
          If it is of type 'str', it will be treated as a column name.
          If it is of type 'List', the first one will be picked and will be treated as the column name.
  
      :param cv Number of folds to use for cross validation.
          Number of observations in the Validation set for each fold = forecast period
          default is 5 fold cross validation.
      :type cv Optional[int]
  
      :param sep: Note that optionally you can give a separator for the data in your file.
          Default is None which treats the sep as a comma (datafile as a 'csv').
      :type sep Optional[str]
  
      """
      self.sep = sep
      list_of_valid_time_ints = [
          "B",
          "C",
          "D",
          "W",
          "M",
          "SM",
          "BM",
          "CBM",
          "MS",
          "SMS",
          "BMS",
          "CBMS",
          "Q",
          "BQ",
          "QS",
          "BQS",
          "A,Y",
          "BA,BY",
          "AS,YS",
          "BAS,BYS",
          "BH",
          "H",
          "T,min",
          "S",
          "L,ms",
          "U,us",
          "N",
          "W-SUN",
          "W-MON",
          "W-TUE",
          "W-WED",
          "W-THU",
          "W-FRI",
          "W-SAT",
      ]
  
      start = time()
      print("Start of Fit.....")
  
      ##### Best hyper-parameters in statsmodels chosen using the best aic, bic or whatever. Select here.
      stats_scoring = "aic"
  
      ### If run_prophet is set to True, then only 1 model will be run and that is FB Prophet ##
      lag = self.lag
  
      # if type(self.non_seasonal_pdq) == tuple:
      if isinstance(self.non_seasonal_pdq, tuple):
          p_max = self.non_seasonal_pdq[0]
          d_max = self.non_seasonal_pdq[1]
          q_max = self.non_seasonal_pdq[2]
      else:
          p_max = 3
          d_max = 1
          q_max = 3
  
      # Check 'ts_column' type
      if isinstance(ts_column, int):
          ### If ts_column is a number, then it means you need to convert it to a named variable
          # TODO: Fix this. ts_df is not defined.
          # You will need an argument to the load_ts_data function that will simply read the data
          # from the string location and return it as a dataframe as is
          ts_column = list(ts_df)[ts_column]
      if isinstance(ts_column, list):
          # If it is of type List, just pick the first one
          print(
              "\nYou have provided a list as the 'ts_column' argument. Will pick the first value as the 'ts_column' name."
          )
          ts_column = ts_column[0]
  
      ### Now you need to save the time series column
      self.ts_column = ts_column
  
      # Check 'target' type #### remember that ML can now handle multi-output targets!
      if isinstance(target, list):
          targets = copy.deepcopy(target)
          if len(target) == 1:
              print(
                  "Single label model...Hence taking %s as target."
                  % target[0]
              )
          else:
              print(
                  "    Auto_TS Machine Learning models can handle multi-label targets = %s"
                  % targets
              )
              print(
                  "        However, ARIMA, VAR and FB Prophet cannot handle Multi-Label. Hence taking first column = %s as target."
                  % target
              )
          target = target[0]
      else:
          ### if it is a string, turn it into a list ###########
          targets = [target]
          print("    Target variable given as = %s" % target)
  
      print("Start of loading of data.....")
  
      if sep is None:
          sep = ","
  
      ########## This is where we start the loading of the data file ######################
      if isinstance(traindata, str):
          if traindata != "":
              try:
                  if self.dask_xgboost_flag:
                      dask_df, ts_df, str_format = load_ts_data(
                          traindata,
                          self.ts_column,
                          sep,
                          targets,
                          self.dask_xgboost_flag,
                      )
                  else:
                      _, ts_df, str_format = load_ts_data(
                          traindata,
                          self.ts_column,
                          sep,
                          targets,
                          self.dask_xgboost_flag,
                      )
                  if isinstance(ts_df, str):
                      print(
                          """Error: Time Series column '%s' could not be converted to a Pandas date time column.
                              Please convert your ts_column into a pandas date-time column and try again"""
                          % self.ts_column
                      )
                      return None
                  else:
                      if type(ts_df) == dask.dataframe.core.DataFrame:
                          print(
                              "    Dask Dataframe loaded successfully. Shape of data set = (%s,%s)"
                              % (
                                  ts_df.shape[0].compute(),
                                  ts_df.shape[1],
                              )
                          )
                      else:
                          print(
                              "    File loaded into pandas dataframe successfully. Shape of data set = %s"
                              % (ts_df.shape,)
                          )
              except Exception:
                  print(
                      "File could not be loaded. Check the path or filename and try again"
                  )
                  return None
      elif isinstance(traindata, pd.DataFrame):
          print(
              f"    Inputs: ts_column = {self.ts_column}, sep = {sep}, target = {targets}"
          )
          if self.dask_xgboost_flag:
              dask_df, ts_df, str_format = load_ts_data(
                  traindata,
                  self.ts_column,
                  sep,
                  targets,
                  self.dask_xgboost_flag,
              )
          else:
              _, ts_df, str_format = load_ts_data(
                  traindata,
                  self.ts_column,
                  sep,
                  targets,
                  self.dask_xgboost_flag,
              )
          if isinstance(ts_df, str):
              print(
                  """Time Series column '%s' could not be converted to a Pandas date time column.
                      Please convert your input into a date-time column  and try again"""
                  % self.ts_column
              )
              return None
      else:
          print(
              "File name is an empty string. Please check your input and try again"
          )
          return None
  
      print("    train data shape = %s" % (ts_df.shape,))
  
      ### save the str_format of date_time for testing later ########
      self.strf_time_format = str_format
  
      if str_format:
          print(
              "    detected strf_time_format as %s in train data. You can change it in setup if this is incorrect."
              % str_format
          )
      else:
          print(
              'Alert: Could not detect strf_time_format of %s. Provide strf_time format during "setup" for better results.'
              % self.ts_column
          )
  
      ### first test the data for Stationary-ness #############
      if self.__any_contained_in_list(
          what_list=["var", "Var", "VAR", "stats", "best"],
          in_list=self.model_type,
      ):
          ### If it is VAR, you must test all vars equally for stationarity and convert to differencing
          diff_limit = test_stationarity(
              ts_df, plot=False, verbose=True, var_only=True
          )
          if diff_limit:
              print(
                  "There is %s differencing needed in this datasets for VAR model"
                  % diff_limit
              )
              for i in range(1, diff_limit):
                  ts_df = ts_df.diff().dropna()
          else:
              print(
                  "There is no differencing needed in this datasets for VAR model"
              )
      else:
          ### If it is not VAR, you need to test only target var for stationarity!
          diff_limit = test_stationarity(
              ts_df[[target]], plot=False, verbose=True, var_only=False
          )
          if diff_limit:
              print(
                  "There is some differencing needed in this datasets for stat models"
              )
          else:
              print(
                  "There is no differencing needed in this datasets for stat models"
              )
  
      if ts_df.shape[1] == 1:
          ### If there is only one column, you assume that to be the target column ####
          target = list(ts_df)[0]
  
      preds = [
          x for x in list(ts_df) if x not in [self.ts_column, target]
      ]
  
      if self.verbose >= 1:
          if self.__any_contained_in_list(
              what_list=["var", "Var", "VAR", "stats", "best"],
              in_list=self.model_type,
          ):
              time_series_plot(
                  ts_df[target],
                  lags=31,
                  title="Original Time Series after %s differencing"
                  % diff_limit,
                  chart_type="line",
                  chart_freq=self.time_interval,
              )
          else:
              if ts_df.shape[0] <= 100000:
                  time_series_plot(
                      ts_df[target],
                      lags=31,
                      title="Original Time Series",
                      chart_type="line",
                      chart_freq=self.time_interval,
                  )
              else:
                  print(
                      "    No time series plot since number of rows > 100K. Continuing..."
                  )
      else:
          print("    No time series plot since verbose = 0. Continuing")
      ##################################################################################################
      ### Turn the time series index into a variable and calculate the difference.
      ### If the difference is not in days, then it is a hourly or minute based time series
      ### If the difference a multiple of days, then test it for weekly, monthly, quarterly, annual etc.
      ##################################################################################################
      if ts_df.index.dtype == "int" or ts_df.index.dtype == "float":
          ### You must convert the ts_df index into a date-time series using the ts_column given ####
          ts_df = ts_df.set_index(self.ts_column)
      ts_index = ts_df.index
  
      ## TODO: Be sure to also assign a frequency to the index column
      ## This will be helpful when finding the "future dataframe" especially for ARIMA, and ML.
      # https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases
  
      ##################    IF TIME INTERVAL IS NOT GIVEN DO THIS   ########################
      #### This is where the program tries to tease out the time period in the data set ####
      ######################################################################################
      if self.time_interval is None:
          print(
              "Time Interval between observations has not been provided. Auto_TS will try to infer this now..."
          )
          ts_index = pd.to_datetime(ts_df.index)
          diff = (ts_index[1] - ts_index[0]).to_pytimedelta()
          diffdays = diff.days
          diffsecs = diff.seconds
  
          if diffsecs == 0:
              diff_in_hours = 0
              diff_in_days = abs(diffdays)
          else:
              diff_in_hours = (
                  abs(diffdays * 24 * 3600 + diffsecs) / 3600
              )
  
          if diff_in_hours == 0 and diff_in_days >= 1:
              print("Time series input in days = %s" % diff_in_days)
              if diff_in_days == 7:
                  print("It is a Weekly time series.")
                  self.time_interval = "weeks"
              elif diff_in_days == 1:
                  print("It is a Daily time series.")
                  self.time_interval = "days"
              elif 28 <= diff_in_days < 89:
                  print("It is a Monthly time series.")
                  self.time_interval = "months"
              elif 89 <= diff_in_days < 178:
                  print("It is a Quarterly time series.")
                  self.time_interval = "qtr"
              elif 178 <= diff_in_days < 360:
                  print("It is a Semi Annual time series.")
                  self.time_interval = "semi"
              elif diff_in_days >= 360:
                  print("It is an Annual time series.")
                  self.time_interval = "years"
              else:
                  print("Time Series time delta is unknown")
                  return None
          if diff_in_days == 0:
              if diff_in_hours == 0:
                  print(
                      "Time series input in Minutes or Seconds = %s"
                      % diff_in_hours
                  )
                  print("It is a Minute time series.")
                  self.time_interval = "minutes"
              elif diff_in_hours >= 1:
                  print("It is an Hourly time series.")
                  self.time_interval = "hours"
              else:
                  print("It is an Unknown Time Series delta")
                  return None
      else:
          print("Time Interval is given as %s" % self.time_interval)
          if self.time_interval in list_of_valid_time_ints:
              print(
                  "    Correct Time interval given as a valid Pandas date-range frequency..."
              )
          else:
              print(
                  "    Error: You must give a valid time interval frequency from Pandas date-range frequency codes"
              )
              print(
                  "    from source: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases"
              )
              return None
  
      ################# This is where you test the data and find the time interval #######
      if self.time_interval is not None:
          if self.time_interval in list_of_valid_time_ints:
              if self.time_interval in ["T,min"]:
                  diff = (ts_index[1] - ts_index[0]).to_pytimedelta()
                  ts_seconds = diff.seconds
                  ts_mins = int(ts_seconds / 60)
          else:
              self.time_interval = self.time_interval.strip().lower()
              if self.time_interval in ["months", "month", "m"]:
                  self.time_interval = "M"
              elif self.time_interval in ["days", "daily", "d"]:
                  self.time_interval = "D"
              elif self.time_interval in ["weeks", "weekly", "w"]:
                  self.time_interval = "W"
              elif self.time_interval in ["qtr", "quarter", "q"]:
                  self.time_interval = "Q"
              elif self.time_interval in ["semi", "semi-annual", "2q"]:
                  self.time_interval = "2Q"
              elif self.time_interval in [
                  "years",
                  "year",
                  "annual",
                  "y",
                  "a",
              ]:
                  self.time_interval = "Y,A"
              elif self.time_interval in ["hours", "hourly", "h"]:
                  self.time_interval = "H"
              elif self.time_interval in [
                  "minutes",
                  "minute",
                  "min",
                  "n",
              ]:
                  self.time_interval = "T,min"
              elif self.time_interval in [
                  "seconds",
                  "second",
                  "sec",
                  "s",
              ]:
                  self.time_interval = "S"
              else:
                  self.time_interval = "M"  # Default is Monthly
                  print(
                      "Time Interval not provided. Setting default as Monthly. Feel free to modify it..."
                  )
      else:
          print(
              "(Error: 'self.time_interval' is None. This condition should not have occurred."
          )
          return
      # Impute seasonal_period if not provided by the user
      if self.seasonal_period is None:
          if self.time_interval in ["M", "MS", "ME", "BM", "BMS"]:
              self.seasonal_period = 12
          elif self.time_interval in ["D", "C", "B"]:
              self.seasonal_period = 30
          elif self.time_interval in [
              "W",
              "W-SUN",
              "W-MON",
              "W-TUE",
              "W-WED",
              "W-THU",
              "W-FRI",
              "W-SAT",
          ]:
              self.seasonal_period = 52
          elif self.time_interval in ["Q", "QS", "BQ", "BQS"]:
              self.seasonal_period = 4
          elif self.time_interval in ["SMS", "SM"]:
              self.seasonal_period = 24
          elif self.time_interval in [
              "A,Y",
              "BA,BY",
              "BAS,BYS",
              "AS,YS",
          ]:
              self.seasonal_period = 1
          elif self.time_interval in ["BH", "H"]:
              self.seasonal_period = 24
          elif self.time_interval in ["T,min"]:
              self.seasonal_period = 60
              ts_index = ts_df.index
          elif self.time_interval in ["L,ms", "U,us", "S"]:
              self.seasonal_period = 60
          else:
              self.seasonal_period = 12  # Default is Monthly
  
      ########################### This is where we store all models in a nested dictionary ##########
      mldict = lambda: defaultdict(mldict)
      self.ml_dict = mldict()
      try:
          if self.__any_contained_in_list(
              what_list=["best"], in_list=self.model_type
          ):
              print(
                  colorful.BOLD
                  + "WARNING: Running best models will take time... Be Patient..."
                  + colorful.END
              )
      except Exception:
          print(
              "Check if your model type is a string or one of the available types of models"
          )
  
      ######### This is when you need to use FB Prophet ###################################
      ### When the time interval given does not match the tested_time_interval, then use FB.
      #### Also when the number of rows in data set is very large, use FB Prophet, It is fast.
      #########                 FB Prophet              ###################################
      if self.time_interval in ["T,min"]:
          ### Now you have to modify it to fit FB Prophet ####
          ts_time_interval = self.time_interval
          self.time_interval = str(ts_mins) + "min"
  
      if self.__any_contained_in_list(
          what_list=["prophet", "Prophet", "best"],
          in_list=self.model_type,
      ):
          print("\n")
          print("=" * 50)
          print("Building Prophet Model")
          print("=" * 50)
          print("\n")
  
          name = "Prophet"
          # Placeholder for cases when model can not be built
          score_val = np.inf
          model_build: Optional[BuildBase] = None
          model = None
          forecasts = None
          print(
              colorful.BOLD
              + "\nRunning Facebook Prophet Model..."
              + colorful.END
          )
          try:
              #### If FB prophet needs to run, it needs to be installed. Check it here ###
  
              model_build = BuildProphet(
                  self.forecast_period,
                  self.time_interval,
                  self.seasonal_period,
                  self.score_type,
                  self.verbose,
                  self.conf_int,
                  self.holidays,
                  self.growth,
                  self.seasonality,
                  **self.kwargs,
              )
              (
                  model,
                  forecast_df_folds,
                  rmse_folds,
                  norm_rmse_folds,
              ) = model_build.fit(
                  ts_df=ts_df[[target] + preds],
                  target_col=target,
                  cv=cv,
                  time_col=self.ts_column,
              )
  
              # forecasts = forecast_df['yhat'].values
  
              ##### Make sure that RMSE works, if not set it to np.inf  #########
              if self.score_type == "rmse":
                  score_val = rmse_folds
              else:
                  score_val = norm_rmse_folds
          except Exception as e:
              print(
                  "Exception occurred while building Prophet model..."
              )
              print(e)
              print(
                  "    FB Prophet may not be installed or Model is not running..."
              )
              forecast_df_folds = None
  
          self.ml_dict[name]["model"] = model
          self.ml_dict[name]["forecast"] = forecast_df_folds
          self.ml_dict[name][self.score_type] = score_val
          self.ml_dict[name]["model_build"] = model_build
  
      ### Once Prophet is finished, you can put the variable back ####
      if self.time_interval in ["T,min"]:
          ### put the time interval back in shape ###
          self.time_interval = ts_time_interval
  
      # if self.__any_contained_in_list(what_list=['ARIMA', 'stats', 'best'], in_list=self.model_type):
      #     ################### Let's build an ARIMA Model and add results #################
      #     print("\n")
      #     print("="*50)
      #     print("Building ARIMA Model")
      #     print("="*50)
      #     print("\n")
  
      #     name = 'ARIMA'
  
      #     # Placeholder for cases when model can not be built
      #     score_val = np.inf
      #     model_build = None
      #     model = None
      #     forecasts = None
      #     print(colorful.BOLD + '\nRunning Non Seasonal ARIMA Model...' + colorful.END)
      #     try:
      #         model_build = BuildArima(
      #             stats_scoring, p_max, d_max, q_max,
      #             forecast_period=self.forecast_period, method='mle', verbose=self.verbose
      #         )
      #         model, forecasts, rmse, norm_rmse = model_build.fit(
      #             ts_df[target]
      #         )
  
      #         if self.score_type == 'rmse':
      #             score_val = rmse
      #         else:
      #             score_val = norm_rmse
      #     except Exception as e:
      #         print("Exception occurred while building ARIMA model...")
      #         print(e)
      #         print('    ARIMA model error: predictions not available.')
  
      #     self.ml_dict[name]['model'] = model
      #     self.ml_dict[name]['forecast'] = forecasts
      #     self.ml_dict[name][self.score_type] = score_val
      #     self.ml_dict[name]['model_build'] = model_build
  
      if self.__any_contained_in_list(
          what_list=[
              "ARIMA",
              "arima",
              "auto_arima",
              "auto_SARIMAX",
              "stats",
              "best",
          ],
          in_list=self.model_type,
      ):
          ############# Let's build a SARIMAX Model and get results ########################
          print("\n")
          print("=" * 50)
          print("Building Auto SARIMAX Model")
          print("=" * 50)
          print("\n")
  
          name = "auto_SARIMAX"
          # Placeholder for cases when model can not be built
          score_val = np.inf
          model_build = None
          model = None
          forecast_df_folds = None
  
          print(
              colorful.BOLD
              + "\nRunning Auto SARIMAX Model..."
              + colorful.END
          )
          try:
              model_build = BuildAutoSarimax(
                  scoring=stats_scoring,
                  seasonality=self.seasonality,
                  seasonal_period=self.seasonal_period,
                  p_max=p_max,
                  d_max=d_max,
                  q_max=q_max,
                  forecast_period=self.forecast_period,
                  verbose=self.verbose,
              )
              (
                  model,
                  forecast_df_folds,
                  rmse_folds,
                  norm_rmse_folds,
              ) = model_build.fit(
                  ts_df=ts_df[[target] + preds],
                  target_col=target,
                  cv=cv,
              )
  
              if self.score_type == "rmse":
                  score_val = rmse_folds
              else:
                  score_val = norm_rmse_folds
          except Exception as e:
              print(
                  "Exception occurred while building Auto SARIMAX model..."
              )
              print(e)
              print(
                  "    Auto SARIMAX model error: predictions not available."
              )
  
          self.ml_dict[name]["model"] = model
          self.ml_dict[name]["forecast"] = forecast_df_folds
          self.ml_dict[name][self.score_type] = score_val
          self.ml_dict[name]["model_build"] = model_build
  
      if self.__any_contained_in_list(
          what_list=["var", "Var", "VAR", "best"],
          in_list=self.model_type,
      ):
          ########### Let's build a VAR Model - but first we have to shift the predictor vars ####
  
          if ts_df.shape[0] > 1000 and self.__any_contained_in_list(
              what_list=["stats", "best"], in_list=self.model_type
          ):
              print(
                  colorful.BOLD
                  + "\n==============================================="
                  + colorful.END
              )
              print(
                  "Skipping VAR Model since dataset is > 1000 rows and it will take too long"
              )
              print(
                  colorful.BOLD
                  + "==============================================="
                  + colorful.END
              )
          else:
              print("\n")
              print("=" * 50)
              print(
                  "Building VAR Model - best suited for small datasets < 1000 rows and < 10 columns"
              )
              print("=" * 50)
              print("\n")
  
              name = "VAR"
              # Placeholder for cases when model can not be built
              score_val = np.inf
              model_build = None
              model = None
              forecasts = None
  
              if len(preds) == 0:
                  print(
                      colorful.BOLD
                      + "\nNo VAR model created since no explanatory variables given in data set"
                      + colorful.END
                  )
              else:
                  try:
                      print(
                          colorful.BOLD
                          + "\nRunning VAR Model..."
                          + colorful.END
                      )
                      print(
                          "    Shifting %d predictors by 1 to align prior predictor values with current target values..."
                          % len(preds)
                      )
  
                      # TODO: This causes an issue later in ML (most likely cause of https://github.com/AutoViML/Auto_TS/issues/15)
                      # Since we are passing ts_df there. Make sure you don't assign it
                      # back to the same variable. Make a copy and make changes to that copy.
                      ts_df_shifted = ts_df.copy(deep=True)
                      ts_df_shifted[preds] = ts_df_shifted[preds].shift(
                          1
                      )
                      ts_df_shifted.dropna(axis=0, inplace=True)
  
                      model_build = BuildVAR(
                          scoring=stats_scoring,
                          forecast_period=self.forecast_period,
                          p_max=p_max,
                          q_max=q_max,
                      )
                      (
                          model,
                          forecasts,
                          rmse,
                          norm_rmse,
                      ) = model_build.fit(
                          ts_df_shifted[[target] + preds],
                          target_col=target,
                          cv=cv,
                      )
  
                      if self.score_type == "rmse":
                          score_val = rmse
                      else:
                          score_val = norm_rmse
                  except Exception as e:
                      print(
                          "Exception occurred while building VAR model..."
                      )
                      print(e)
                      warnings.warn(
                          "    VAR model error: predictions not available."
                      )
  
              self.ml_dict[name]["model"] = model
              self.ml_dict[name]["forecast"] = forecasts
              self.ml_dict[name][self.score_type] = score_val
              self.ml_dict[name]["model_build"] = model_build
  
      if self.__any_contained_in_list(
          what_list=["ml", "ML", "best"], in_list=self.model_type
      ):
          ########## Let's build a Machine Learning Model now with Time Series Data ################
          ### Remember that if the dask_xgboost_flag is set to True, we will use Dask DF to build model
          ###  Otherwise, if the dask_xgboost_flag is set to False, we will use pandas DF to build model
          print("\n")
          print("=" * 50)
          print("Building ML Model")
          print("=" * 50)
          print("\n")
  
          name = "ML"
          # Placeholder for cases when model can not be built
          score_val = np.inf
          model_build = None
          model = None
          forecasts = None
  
          if len(preds) == 0:
              ### Set the lag to be 1 since we don't need too many lagged variables for univariate case
              self.lags = self.lag
              lag = self.lag
          else:
              print(
                  colorful.BOLD
                  + "\nRunning Machine Learning Models..."
                  + colorful.END
              )
              #### Do not create excessive lagged variables for ML models. They make it too slow ##########
              if lag <= 2:
                  lag = 2  ### set the minimum lags to be at least 2 for ML models
              elif lag >= 10:
                  lag = 10  ### set the maximum lags to be not more than 10 for ML models
              self.lags = lag
          ####### Now make sure that there is only as few lags as needed ######
          print(
              colorful.BOLD
              + f"\nCreating {self.lag-1} lagged variables for Machine Learning model..."
              + colorful.END
          )
          print(
              "    You have set lag = %d in auto_timeseries setup to feed prior targets. You cannot set lags > 10 ..."
              % (lag,)
          )
  
          model_build = BuildML(
              scoring=self.score_type,
              forecast_period=self.forecast_period,
              ts_column=self.ts_column,
              time_interval=self.time_interval,
              sep=self.sep,
              dask_xgboost_flag=self.dask_xgboost_flag,
              strf_time_format=self.strf_time_format,
              num_boost_rounds=self.num_boost_rounds,
              verbose=self.verbose,
          )
          try:
              if self.dask_xgboost_flag:
                  ### We must use dask_df to build a model #####
                  model, forecasts, rmse, norm_rmse = model_build.fit(
                      ts_df=dask_df,
                      target_col=targets,
                      ts_column=self.ts_column,
                      cv=cv,
                      lags=lag,
                  )
              else:
                  ### We must use pandas ts_df to build a model #####
                  model, forecasts, rmse, norm_rmse = model_build.fit(
                      ts_df=ts_df,
                      target_col=targets,
                      ts_column=self.ts_column,
                      cv=cv,
                      lags=lag,
                  )
  
              if self.score_type == "rmse":
                  score_val = rmse
              else:
                  score_val = norm_rmse
              # bestmodel = best[0]
  
              # #### Plotting actual vs predicted for ML Model #################
              # # TODO: Move inside the Build Class
              # plt.figure(figsize=(5, 5))
              # plt.scatter(train.append(test)[target].values,
              #             np.r_[bestmodel.predict(train[preds]), bestmodel.predict(test[preds])])
              # plt.xlabel('Actual')
              # plt.ylabel('Predicted')
              # plt.show(block=False)
              # ############ Draw a plot of the Time Series data ######
              # time_series_plot(dfxs[target], chart_time=self.time_interval)
  
          except Exception as e:
              print("Exception occurred while building ML model...")
              print(e)
              print(
                  "    For ML model, evaluation score is not available."
              )
  
          self.ml_dict[name]["model"] = model
          self.ml_dict[name]["forecast"] = forecasts
          self.ml_dict[name][self.score_type] = score_val
          self.ml_dict[name]["model_build"] = model_build
  
      if not self.__all_contained_in_list(
          what_list=self.model_type, in_list=self.allowed_models
      ):
          print(
              f"The model_type should be any of the following: {self.allowed_models}. You entered {self.model_type}. Some models may not have been developed..."
          )
          if len(list(self.ml_dict.keys())) == 0:
              return None
  
      ######## Selecting the best model based on the lowest rmse score ######
      best_model_name = self.get_best_model_name()
      print(
          colorful.BOLD
          + "\nBest Model is: "
          + colorful.END
          + best_model_name
      )
  
      best_model_dict = self.ml_dict[best_model_name]
      if best_model_dict is not None:
          cv_scores = best_model_dict.get(self.score_type)
          if isinstance(cv_scores, list):
              if len(cv_scores) == 0:
                  mean_cv_score = np.inf
              else:
                  mean_cv_score = get_mean_cv_score(cv_scores)
          else:
              mean_cv_score = np.mean(cv_scores)
      print(
          "    Best Model (Mean CV) Score: %0.2f" % mean_cv_score
      )  # self.ml_dict[best_model_name][self.score_type])
  
      end = time()
      elapsed = end - start
      print("\n\n" + "-" * 50)
      print(f"Total time taken: {elapsed:.0f} seconds.")
      print("-" * 50 + "\n\n")
      print(
          "Leaderboard with best model on top of list:\n",
          self.get_leaderboard(),
      )
      return self
  
imports: import warnings
  from typing import List, Dict, Optional, Tuple, Union
  from datetime import datetime
  import copy
  from collections import defaultdict
  import operator
  from time import time
  import pdb
  import pandas
  import numpy
  import dask
  import dask.dataframe
  from sklearn.exceptions import DataConversionWarning
  import seaborn
  import seaborn
  import matplotlib.pyplot
  from .models import BuildBase, BuildArima, BuildAutoSarimax, BuildVAR, BuildML
  from .models.build_prophet import BuildProphet
  from .utils import colorful, load_ts_data, convert_timeseries_dataframe_to_supervised, time_series_plot, print_static_rmse, print_dynamic_rmse, quick_ts_plot, test_stationarity, print_ts_model_stats
  from .__version__ import __version__
label_desc: Use image node_id 2 for calling the auto_timeseries obj's underlying member method code with example usage: obj.fit(traindata, ts_column, target, sep, cv) and returns: self, None, None, None, None, None, None, None, None