code: import utility
  import static_sim_functions as smf
  import numpy as np
  from sklearn.model_selection import train_test_split
  from sklearn.metrics import *
  from time_series_grp import TimeSeriesGroupProcessing
  from RandomNeighbors import RandomNeighbors
  from sklearn.neighbors import NearestNeighbors
  from sklearn.model_selection import KFold
  
  import ml_modelling_ts as ml_ts
  
  '''
  This is just a run of the approaches using the methodologies, save the neighborhood for UI.
  '''
  
  
  def common_processing(df):
      # Getting percentage between 0 to 1 rather than score values
      df["tschq12"] = df["tschq12"].apply(lambda x: x / 100)
      df["tschq16"] = df["tschq16"].apply(lambda x: x / 100)
      df["tschq17"] = df["tschq17"].apply(lambda x: x / 100)
  
      # Feature engineering family history
      df["tschq04"] = df.apply(smf.create_cols_family_hist, axis=1)
  
      return df
  
  
  def get_common_cols(col1, col2):
      common_elements = set(col1).intersection(col2)
      return common_elements
  
  import properties
  import pandas as pd
  def initial_processing():
      # Read the csv of the tschq data and make the necessary things
      tschq = pd.read_pickle(properties.data_location + "/input_pckl/" + "3_q.pckl")
  
      # Cleaning tschq05 question. There is an abstraction for a row we add common value
  
      def filter_age(x):
          if isinstance(x, int):
              # Append the most common value obtained
              return tschq["tschq05"].value_counts().head(1).index[0]
          else:
              return x
  
      tschq["tschq05"] = tschq["tschq05"].apply(filter_age)
  
      # Drop the questionnaire_id and created_at
      tschq.drop(["questionnaire_id", "created_at"], axis=1, inplace=True)
  
      # Lets read and join two questionnaires tschq and hq
      hq = pd.read_pickle("data/input_pckl/4_q.pckl")
      hq.isna().sum(axis=0)
      # By looking at the output we are sure that h5 and h6 do not contribute much and can be dropped
      hq.drop(["hq05", "hq06"], axis=1, inplace=True)
      hq_df = hq.set_index("user_id")
      df = tschq.join(hq_df.iloc[:, 2:], on="user_id")
  
      drop_cols = ["tschq01", "tschq25", "tschq07-2",
                       "tschq13", "tschq04-1", "tschq04-2"]
  
      # Getting percentage between 0 to 1 rather than score values
      df["tschq12"] = df["tschq12"].apply(lambda x: x / 100)
      df["tschq16"] = df["tschq16"].apply(lambda x: x / 100)
      df["tschq17"] = df["tschq17"].apply(lambda x: x / 100)
  
      df["tschq04"] = df.apply(smf.create_cols_family_hist, axis=1)
  
      df.drop(drop_cols, axis=1, inplace=True)
  
      # Set the heom object, while using the required similarity
      # Alternative
      # Categorical boolean mask
      categorical_feature_mask = df.iloc[:, 1:].infer_objects().dtypes == object
      other_feature_mask = df.iloc[:, 1:].infer_objects().dtypes != object
      # filter categorical columns using mask and turn it into a list
      categorical_cols = df.iloc[:, 1:].columns[categorical_feature_mask].tolist()
      num_cols = df.iloc[:, 1:].columns[other_feature_mask].tolist()
      cat_idx = [df.iloc[:, 1:].columns.get_loc(val) for val in categorical_cols]
      num_idx = [df.iloc[:, 1:].columns.get_loc(val) for val in num_cols]
  
      return cat_idx, num_idx, df
  
  import os
  import traceback
  def save_data_objs(df, quest_cmbs="all"):
      try:
          if not os.path.isdir(properties.model_location + quest_cmbs):
              os.makedirs(properties.model_location + quest_cmbs)
          utility.save_model("".join(quest_cmbs + "/" + quest_cmbs + "_stat_q_data"), df)
  
          encoded_combined_df = smf.preprocess(df, quest_cmbs, age_bin=False,
                                                   process_model_name="".join(quest_cmbs + "/" +
                                                                              quest_cmbs + "_stat_q_data_oe_model"),
                                                   prediction=False)
  
          # Save this encoded_data
          utility.save_model("".join(quest_cmbs + "/" +
                                         quest_cmbs + "_stat_q_data_encoded"), encoded_combined_df)
  
          return encoded_combined_df
  
          # Use this data to build the data over static data.
      except Exception:
          print(traceback.print_exc())
  
  
  def weighted_average(distress_list):
      average = np.asarray(distress_list, dtype=float).mean()
      return average
  
  
  
  # Function computes the weighted average as predictions for given prediction time point
  def compute_weighted_avg(n_idx, encoded_data, pred_at_list, method="mean", dist_nn=None, wt_flag=False):
  
      preds = list()
      # Prediction for four time points
      for pval in pred_at_list:
          distress_list = list()
          for vals in n_idx:
              u_id = encoded_data["user_id"].iloc[vals]
              user_ts = tsg_data.get_usr_mday_ts_predict(int(u_id))
              # 3rd val of the series is s03 of the neighbor
              print("{}, {} Values ".format(int(pval), int(u_id)))
              if len(user_ts) > int(pval):
                  value = user_ts[int(pval), :][3]
              elif len(user_ts) <= int(pval):
                  value = user_ts[len(user_ts)-1, :][3]
  
              distress_list.append(value)
  
  
          if wt_flag:
              print("Calling by weighted distance prediction for distress")
              preds.append(weighted_distance_prediction(distress_list, dist_nn))
          else:
              print("Calling weighted average to predict distress")
              preds.append(weighted_average(distress_list))
      return preds
  
  
  def weighted_distance_prediction(p_preds, distance):
      # Inverse distance so that highest weight is given to the nearest one and least to the farther
      inv_dist = np.divide(1, distance)
  
      #s03 - tinnitus distress weighted by distance is given as
      s03_pred = (np.sum(np.multiply(p_preds, inv_dist)) / (np.sum(inv_dist)))
  
      return s03_pred
  
  
  def compute(test_nn, encoded_data,
              pred_list, method="mean", dist_nn=None, wt_dist=False):
      from sklearn.linear_model import LinearRegression
  
      preds = list()
      for point in pred_list:
          nn_preds = list()
          intercepts_list = list()
          coeff_list = list()
          for nn in test_nn:
              u_id = encoded_data["user_id"].iloc[nn]
              user_ts = tsg_data.get_usr_mday_ts_predict(int(u_id))
              # Obtain the time series until time point and fit the data for linear regression
              diff_arr = np.abs(np.subtract(point, user_ts[:, 1]))
              diff_near_idx = np.where(diff_arr == diff_arr.min())
              print("minimum to the time point is at -- ", diff_near_idx)
              # difference near index. Handling for the length of users
              usr_idx = diff_near_idx[0][0]
  
              user_ts_p = user_ts[:usr_idx]
              user_ts_df = pd.DataFrame(user_ts_p, columns=["day", "day_sess_index",
                                                          "s02", "s03", "s04",
                                                          "s05", "s06", "s07"])
              X = user_ts_df[["day_sess_index"]]
              # We show for tinnitus distress. This can be extended to other physiological variables as well.
              y = user_ts_df[["s03"]]
  
              # Fit on X axis as time and Y as the s03 predictive value.
              reg_fit = LinearRegression(normalize=True)
              reg_fit.fit(X, y)
  
              # If weighted_distance is true, then predict by each of the nn_user and add to list. This will be used for
              # calculating weighted_distance_predictions.
              if wt_dist:
                  nn_pred = reg_fit.predict(np.asarray(point).reshape(1, -1))
                  nn_preds.append(nn_pred[0][0])
              else:
                  intercepts_list.append(reg_fit.intercept_)
                  coeff_list.append(reg_fit.coef_)
  
          if wt_dist:
              print("Predicting the value of s03 for the user by a weighted average weighted by distance")
              preds.append(weighted_distance_prediction(nn_preds, dist_nn))
          else:
              print("Predicting the value of s3 over the averaged slope and intercepts of "
                "observations of the neighbors")
  
              # y = mx + c, where m is the average slope of the neighbors and c is the average intercept obtained.
              print("The equation to estimate s03 for the user is {}".format("".join(str(np.asarray(coeff_list).mean())) +
                                                                         "* time_index + " +
                                                                         str(np.asarray(intercepts_list).mean())))
              y = np.multiply(np.asarray(coeff_list).mean(), point) + np.asarray(intercepts_list).mean()
              preds.append(y)
  
      return preds
  
  
  def compute_linear_regression(test_nn, encoded_data, pred_list, method="mean"):
      #test_nn = test_user_nn
      #pred_list = prediction_at_list
      from sklearn.linear_model import LinearRegression
      preds = list()
      for point in pred_list:
          attr_list = list()
          intercepts_list = list()
          coeff_list = list()
          for nn in test_nn:
              u_id = encoded_data["user_id"].iloc[nn]
              user_ts = tsg_data.get_m_day_ts_enumerate(int(11))
              diff_arr = np.abs(np.subtract(point, user_ts[:, 1]))
              diff_near_idx = np.where(diff_arr == diff_arr.min())
              print(diff_near_idx)
              # difference near index
              usr_vals = np.array([user_ts[n_id] for n_id in diff_near_idx[0]])
              if len(usr_vals) > 1:
                  value = usr_vals.mean(axis=0)
                  print("vavg" + str(value))
              else:
                  value = usr_vals[0]
                  print("v" + str(value))
  
              attr_list.append(value)
  
  
              df = pd.DataFrame(user_ts)
              df.columns = ["day", "day_session_id",
                            "s02", "s03",
                            "s04", "s05",
                            "s06", "s07"]
              reg_model = LinearRegression(normalize=True)
              user_x = df[["day_session_id", "s04", "s05", "s06"]].to_numpy()
              user_s03 = df[["s03"]].to_numpy().ravel()
              reg_model.fit(user_x, user_s03)
              intercepts_list.append(reg_model.intercept_)
              coeff_list.append(reg_model.coef_)
          # y = mx + c, where m is the average slope of the neighbors and c is the average intercept obtained.
  
          # convert coeff's to numpy for manipulations
          numpy_attr_list = np.array(attr_list)
          print(numpy_attr_list)
          avg_np_attr_list = numpy_attr_list[:, 4:].mean(axis=0)
  
          print(avg_np_attr_list)
  
          numpy_coeff_list = np.array(coeff_list)
  
          print(numpy_coeff_list)
          print(numpy_coeff_list.mean(axis=0))
  
          # Day_index, s02, s04, s05, s06 ,s07 - Use only the fit independent features to estimate the dependent
          y = np.multiply(numpy_coeff_list[:, 0].mean(), point) + \
              np.multiply(numpy_coeff_list[:, 1].mean(), avg_np_attr_list[0]) + \
              np.multiply(numpy_coeff_list[:, 2].mean(), avg_np_attr_list[1]) + \
              np.multiply(numpy_coeff_list[:, 3].mean(), avg_np_attr_list[2]) + \
              np.asarray(intercepts_list).mean()
          preds.append(y)
      print(preds)
      return preds
  
  
  # Create test label as ground truth at prediction point.
  def create_y_labels(test_data, prediction_at, method="mean"):
      y_test = list()
      for i in range(0, len(test_data)):
          test_ts_test1 = tsg_data.get_usr_mday_ts_predict(int(test_data.iloc[i]["user_id"]))
          # print(len(test_ts_test1))
          if len(test_ts_test1) >= prediction_at:
              y_test.append(test_ts_test1[prediction_at - 1][2])
          elif len(test_ts_test1) < prediction_at:
              y_test.append(test_ts_test1[len(test_ts_test1) - 1][2])
      return y_test
  
  
  # Create reference points for multiple reference predictions
  def get_pred_ref_points(user_id, ndays, method="mean"):
      # Using the default tsg which is mean observations of the user
      test_user_ts = tsg_data.get_usr_mday_ts_predict(user_id)
  
      user_ts_idx = test_user_ts[:, 1]
      # ["date", "time_idx", "s02", "s03", "s04", "s05", "s06", "s07]
      user_distress = test_user_ts[:, 3]
  
      # Near evaluation. Change this for farther evaluations
      # Near -> 0.20, 0.10
      # Far -> 1 - (Near)
  
      # Near points are of the sequence of observation because we are sure all stay until here.
      #prediction_at = 10
  
      # Far prediction point is the last N% of the test user time series
      # It is tested for 0.75, 0.8, 0.9
      prediction_at = round(len(user_ts_idx) * 0.80)
      y_labels = user_distress[prediction_at:prediction_at + ndays].tolist()
      prediction_at_list = user_ts_idx[prediction_at:prediction_at + ndays].tolist()
  
      return y_labels, prediction_at_list
  
  
  def do_test(test_data, out_writer, csv_out_writer,
              ndays, near_idxs, encoded_data, fold_count="final",
              method="mean", dist_nn=None, wt_dist_flag=False):
      for i in range(0, len(test_data)):
          user_id = int(test_data.iloc[i]["user_id"])
          print("User- Id ", user_id)
          y_labels, prediction_at_list = get_pred_ref_points(user_id, ndays, method=method)
  
          # y_labels = create_y_labels(X_test, preds, method="mean")
          # Weighting by inverse of neighbor
          if wt_dist_flag:
              test_user_nn = near_idxs[i]
              test_user_dist = dist_nn[i]
              pred_weighted_average = compute_weighted_avg(test_user_nn, encoded_data, prediction_at_list,
                                                       method=method, dist_nn=test_user_dist, wt_flag=wt_dist_flag)
  
              pred_lr = compute(test_user_nn, encoded_data, prediction_at_list,
                                method=method, dist_nn=test_user_dist, wt_dist=wt_dist_flag)
          else:
              test_user_nn = near_idxs[i]
              pred_weighted_average = compute_weighted_avg(test_user_nn, encoded_data, prediction_at_list,
                                                           method=method, dist_nn=None, wt_flag=False)
              pred_lr = compute(test_user_nn, encoded_data, prediction_at_list,
                                method=method, dist_nn=None, wt_dist=False)
  
  
          # calculate
          if not fold_count == "final":
              print("Evaluating for the fold-" + str(fold_count) + " for the forecast reference points - " +
                    str(prediction_at_list))
              out_writer.write("Evaluating for the forecast reference points -- " +
                               str(prediction_at_list) + "for the method evaluation -- " + str(method) + "\n")
          else:
              print("Evaluating for forecast reference points - " +
                    str(prediction_at_list))
              out_writer.write("Evaluating over the forecast reference points -- " +
                               str(prediction_at_list) + "for the method evaluation -- " + str(method) + "\n")
  
          print("Computing RMSE for weighted average based predictions on the User -- " + str(user_id))
          print("---------------------------------------------------------------")
          out_writer.write("---------------------------------------------------------------\n")
  
          print("RMSE -- ", np.sqrt(mean_squared_error(y_labels, pred_weighted_average)))
          out_writer.write("RMSE -- " + str(np.sqrt(mean_squared_error(y_labels, pred_weighted_average))) + "\n")
  
  
          # Writing to csv file
          if not fold_count == "final":
              csv_out_writer.write("".join(str(user_id) + "," +
                                            str(np.sqrt(mean_squared_error(y_labels, pred_weighted_average))) + "," +
                                            "weighted_average" + ","
                                           + str(y_labels[0]) + "," + str(y_labels[1]) + "," + str(y_labels[2])
                                           + "," + str(pred_weighted_average[0]) + "," + str(pred_weighted_average[1])
                                           + "," + str(pred_weighted_average[2]) + "\n"))
          else:
              csv_out_writer.write("".join(str(user_id) + "," +
                                            str(np.sqrt(mean_squared_error(y_labels, pred_weighted_average))) + "," +
                                            "weighted_average" + ","
                                           + str(y_labels[0]) + "," + str(y_labels[1]) + "," + str(y_labels[2])
                                           + "," + str(pred_weighted_average[0]) + "," + str(pred_weighted_average[1])
                                           + "," + str(pred_weighted_average[2]) + "\n"))
  
          print("-----------------------------------------------------------------------------")
          out_writer.write("---------------------------------------------------------------\n")
          print("Computing RMSE for {} {} based predictions for the user -- {}"
                .format(str("weighted_distance" + str(wt_dist_flag)), str("linear_regression"), str(user_id)))
          out_writer.write("Computing RMSE for {} {} based predictions for the user -- {} \n"
                           .format(str("weighted_distance" + str(wt_dist_flag)), str("linear_regression"), str(user_id)))
          print("RMSE -- ", np.sqrt(mean_squared_error(y_labels, pred_lr)))
          out_writer.write("RMSE -- " + str(np.sqrt(mean_squared_error(y_labels, pred_lr))) + "\n")
          print("---------------------------------------------------------------")
          out_writer.write("---------------------------------------------------------------\n")
  
          # Write to csv file
          if not fold_count == "final":
              csv_out_writer.write("".join(str(user_id) + "," +
                                           str(np.sqrt(mean_squared_error(y_labels, pred_lr))) + "," +
                                           str("lr") + ","
                                           + str(y_labels[0]) + "," + str(y_labels[1]) + "," + str(y_labels[2])
                                           + "," + str(pred_lr[0]) + "," + str(pred_lr[1]) + "," + str(
                  pred_lr[2]) + "\n"))
          else:
              csv_out_writer.write("".join(str(user_id) + "," +
                                           str(np.sqrt(mean_squared_error(y_labels, pred_lr))) + "," +
                                           str("lr") + ","
                                           + str(y_labels[0]) + "," + str(y_labels[1]) + "," + str(y_labels[2])
                                           + "," + str(pred_lr[0]) + "," + str(pred_lr[1]) + "," + str(
                  pred_lr[2]) + "\n"))
  
  
  # Change method and execute to get the predictions appropriately, these are configurations
  eval_method = "mean"
  # Default day readings for all test users must be at mean and prediction are between min - mean - max
  
  tsg_data = TimeSeriesGroupProcessing(method=eval_method)
  # For all combinations evaluation it must be set to True
  quest_cmb_all = False
  # Same random state needs to be maintained to get consistent test data over all combinations and repeatable results
  random_state = 1220
  # It is the setting to get the ahead prediction for tinnitus distress, 3 here means for 3 days
  # min it is a day and max of about 60days between points which is not an usual scenario
  ndays = 3
  
  # Build the default NN with all the combination.
  if not quest_cmb_all:
      for key, val in properties.quest_comb.items():
          # Build NN for each category
          print("Building NN for the question combination -- " + str(key))
  
          cat_idx, num_idx, combined_df = smf.initial_processing(key, val, append_synthethic=False)
          # Build and get the knn NN for prediction over test instances.
          # Save the data objs
  
          encoded_data = save_data_objs(combined_df, key)
  
          out_writer = open("".join("output/output_" + str(key) + "_" + str(eval_method) + "_heom_norm.txt"), "w+")
          csv_out_writer = open("".join("output/output_" + str(key) + "_" + str(eval_method) + "_heom_norm.csv"), "w+")
  
          csv_out_writer.write("".join("user_id,rmse,algorithm,"
                                       "ref_p1,ref_p2,ref_p3,pred_p1,pred_p2,pred_p3\n"))
  
          #Create a test set
          X, test = train_test_split(encoded_data,
                                     test_size=0.20,
                                     random_state=random_state)
  
          def filter_train_ids(x):
              # print(x)
              if x["user_id"] in train_user_ids:
                  return x
  
          def filter_test_ids(x):
              # print(x)
              if x["user_id"] in test_user_ids:
                  return x
  
          train_user_ids = X["user_id"].to_list()
  
          X_train_data_ui = combined_df.apply(filter_train_ids, axis=1, result_type="broadcast").dropna()
          X_train_data_ui["user_id"] = X_train_data_ui["user_id"].apply(int)
          # Save the non encoded train data for visualization purposes
          utility.save_model("".join(key + "/" + key + "_train_stat_q_data"), X_train_data_ui)
  
          # filter and get the data to show to the UI for the test data.
          test_user_ids = test["user_id"].to_list()
  
          X_test_data_ui = combined_df.apply(filter_test_ids, axis=1, result_type="broadcast").dropna()
  
          X_test_data_ui["user_id"] = X_test_data_ui["user_id"].apply(int)
  
          # Save the data_ui object as json
          #test_data = {}
          #test_data["users"] = X_test_data_ui.to_dict("r")
          #utility.save_data("".join("test_data_ui_" + key), test_data)
  
          from HEOM import HEOM
          # Can be done at prediction too.
          from sklearn.metrics.pairwise import cosine_distances
          from sklearn.linear_model import LinearRegression
          from scipy.spatial.distance import pdist, squareform
          from scipy.stats import zscore
  
          heom = HEOM(X.to_numpy(), cat_idx, num_idx)
          sim_matrix = pdist(X.to_numpy()[:, 1:], heom.heom_distance)
          mean_heom_distance = sim_matrix.mean()
  
          knn = NearestNeighbors(n_neighbors=5, metric=heom.heom_distance, radius=mean_heom_distance)
          knn.fit(X.iloc[:, 1:])
          dist, test_idx = knn.kneighbors(test.to_numpy()[:, 1:], n_neighbors=5)
  
          # Execute without any varying for saving the KNN as pickle to be used by UI
          do_test(test, out_writer, csv_out_writer, ndays, test_idx, X,
                  fold_count="final", method=eval_method, dist_nn=None, wt_dist_flag=False)
  
          utility.save_model("".join(key + "/" + "knn_static"), knn)
          utility.save_model("".join(key + "/" + "train_sim_data.pckl"), X)
  
          out_writer.close()
          csv_out_writer.close()
  
  
  # All feature combinations
  
  cat_idx, num_idx, combined_df = initial_processing()
  
  
  # Build KNN for each category
  print("Building KNN for the question combination -- " + str("overall"))
  
  # Save the data objs
  encoded_data = save_data_objs(combined_df, "overall")
  
  
  X, test = train_test_split(encoded_data,
                                     test_size=0.20,
                                     random_state=random_state)
  
  
  def filter_train_ids(x):
      # print(x)
      if x["user_id"] in train_user_ids:
          return x
  
  
  def filter_test_ids(x):
      # print(x)
      if x["user_id"] in test_user_ids:
          return x
  
  
  train_user_ids = X["user_id"].to_list()
  
  X_train_data_ui = combined_df.apply(filter_train_ids, axis=1, result_type="broadcast").dropna()
  X_train_data_ui["user_id"] = X_train_data_ui["user_id"].apply(int)
  
  # Save in overall.
  utility.save_model("".join("overall" + "/" + "overall" + "_train_stat_q_data"), X_train_data_ui)
  
  # filter and get the data to show to the UI for the test data.
  test_user_ids = test["user_id"].to_list()
  
  X_test_data_ui = combined_df.apply(filter_test_ids, axis=1, result_type="broadcast").dropna()
  
  X_test_data_ui["user_id"] = X_test_data_ui["user_id"].apply(int)
  
  # Save the data_ui object as json
  test_data = {}
  test_data["users"] = X_test_data_ui.to_dict("r")
  utility.save_data("test_data_ui_x_test", test_data)
  
  # Save the results to out_writer
  out_writer = open("output/overall_output_folds_" + str(eval_method) + ".txt", "w+")
  csv_out_writer = open("output/overall_output_folds_" + str(eval_method) + ".csv", "w+")
  
  # First get the time series for a given test patient and the reference point and iterate to evaluate
  csv_out_writer.write("user_id,rmse,algorithm,"
                       "ref_p1,ref_p2,ref_p3,pred_p1,pred_p2,pred_p3\n")
  
  
  # Split the data into train and test
  from sklearn.model_selection import train_test_split
  import utility
  from HEOM import HEOM
  #Can be done at prediction too.
  from sklearn.metrics.pairwise import cosine_distances
  from sklearn.linear_model import LinearRegression
  from scipy.spatial.distance import pdist, squareform
  from scipy.stats import zscore
  
  
  heom = HEOM(X.to_numpy()[:, 1:], cat_idx, num_idx)
  sim_matrix = pdist(X.to_numpy()[:, 1:], heom.heom_distance)
  mean_heom_distance = sim_matrix.mean()
  
  knn = NearestNeighbors(n_neighbors=5, metric=heom.heom_distance, radius=mean_heom_distance)
  knn.fit(X.to_numpy()[:, 1:])
  dist, idx_test = knn.kneighbors(test.to_numpy()[:, 1:], n_neighbors=5)
  
  # First get the time series for a given test patient and the reference point and iterate to evaluate
  
  do_test(test, out_writer, csv_out_writer, ndays, idx_test, X,
          fold_count="final", method=eval_method, dist_nn=None, wt_dist_flag=False)
  
  out_writer.close()
  csv_out_writer.close()
  
  # End save the nearest neighbor as data objects, so that can be used from the UI
  utility.save_model("".join("overall/" + "knn_static"), knn)
  utility.save_model("".join("overall" + "/" + "train_sim_data.pckl"), X)
  
  
  '''
   ML Modelling based on s02 - loudness.
   Note: This has to be run once the all feature execution is completed since we build upon a custom similarity matrix,
   it is essential that the same split of train test happen so that it can be verified from the application.
  '''
  
  # Create train and test containing same users in train and test as per static data. (Note: Run above code and then this
  # because same set of train test users are used)
  
  def splitData(dataset, test_user_ids):
      train_data = dataset[~dataset["user_id"].isin(test_user_ids)]
      test_data = dataset[dataset["user_id"].isin(test_user_ids)]
      return train_data, test_data
  
  
  # Save both train and test matrix
  def save_ts_objs(train, test, location_name):
      try:
          if not os.path.isdir(properties.model_location + location_name):
              os.makedirs(properties.model_location + location_name)
          utility.save_model("".join(location_name + "/" + location_name + "_train_data"), train)
          utility.save_model("".join(location_name + "/" + location_name + "_test_data"), test)
  
      except Exception:
          print(traceback.print_exc())
  
  
  X = ml_ts.process_data(grouping="day")
  
  # Calculate pairwise distance and create a dataframe for the same
  from scipy.spatial.distance import pdist, squareform
  # Cross validate here based on the same split of static data here.
  # Note: Only one combination will be present
  C = np.zeros((X.shape[0], X.shape[0]))
  for i in range(0, len(X)):
      for j in range(0, len(X)):
          dist = ml_ts.compute_dist(X[:, 1][i], X[:, 1][j])
          C[i][j] = dist
  
  C_df = pd.DataFrame(C)
  
  
  #C_df.to_csv("sim_ema.csv")
  
  # Threshold overall distance for making within radius
  threshold_distance = sum(C_df.mean())/len(C_df)
  
  
  user_ids = []
  for val in X:
      user_ids.append(val[0])
  
  C_df["user_id"] = user_ids
  
  
  train_data, test_data = splitData(C_df, test_user_ids)
  # Save the time series data objects as dynamic_ts into model folder
  save_ts_objs(train_data, test_data, "dynamic_ts")
  
  out_writer = open("".join("output/output_ema_" + str(eval_method) + "_.txt"), "w+")
  csv_out_writer = open("".join("output/output_ema_" + str(eval_method) + "_.csv"), "w+")
  
  csv_out_writer.write("user_id,rmse,algorithm,"
                       "ref_p1,ref_p2,ref_p3,pred_p1,pred_p2,pred_p3\n")
  
  # Test on the final test set. Note there is no varying K just to save the NN here.
  # It should be noted we use NearesetNeighbors and not KNearestNeighbors classifier.
  knn_ema = NearestNeighbors(n_neighbors=5, metric="precomputed", radius=threshold_distance)
  knn_ema.fit(train_data[train_data.index])
  ema_dist, ema_idx = knn_ema.kneighbors(test_data[train_data.index], n_neighbors=5)
  # First get the time series for a given test patient and the reference point and iterate to evaluate
  do_test(test_data, out_writer, csv_out_writer, ndays, ema_idx, encoded_data,
          fold_count="final", method=eval_method, dist_nn=None, wt_dist_flag=False)
  
  # Close the writers
  out_writer.close()
  csv_out_writer.close()
  
  # Save the similarity search index KNN
  utility.save_model("".join("dynamic_ts" + "/" + "dynamic_ts" + "_knn"), knn_ema)
  
apis: numpy.sum pandas.read_pickle numpy.multiply sklearn.neighbors.NearestNeighbors numpy.divide numpy.zeros numpy.subtract pandas.DataFrame sklearn.linear_model.LinearRegression numpy.asarray numpy.array sklearn.model_selection.train_test_split