code: def complex_XGBoost_model(
      X_train,
      y_train,
      X_test,
      log_y=False,
      GPU_flag=False,
      scaler="",
      enc_method="label",
      n_splits=5,
      num_boost_round=1000,
      verbose=-1,
  ):
      """
      This model is called complex because it handle multi-label, mulit-class datasets which XGBoost ordinarily cant.
      Just send in X_train, y_train and what you want to predict, X_test
      It will automatically split X_train into multiple folds (10) and train and predict each time on X_test.
      It will then use average (or use mode) to combine the results and give you a y_test.
      It will automatically detect modeltype as "Regression" or 'Classification'
      It will also add MultiOutputClassifier and MultiOutputRegressor to multi_label problems.
      The underlying estimators in all cases is XGB. So you get the best of both worlds.
  
      Inputs:
      ------------
      X_train: pandas dataframe only: do not send in numpy arrays. This is the X_train of your dataset.
      y_train: pandas Series or DataFrame only: do not send in numpy arrays. This is the y_train of your dataset.
      X_test: pandas dataframe only: do not send in numpy arrays. This is the X_test of your dataset.
      log_y: default = False: If True, it means use the log of the target variable "y" to train and test.
      GPU_flag: if your machine has a GPU set this flag and it will use XGBoost GPU to speed up processing.
      scaler : default is StandardScaler(). But you can send in MinMaxScaler() as input to change it or any other scaler.
      enc_method: default is 'label' encoding. But you can choose 'glmm' as an alternative. But those are the only two.
      verbose: default = 0. Choosing 1 will give you lot more output.
  
      Outputs:
      ------------
      y_preds: Predicted values for your X_XGB_test dataframe.
          It has been averaged after repeatedly predicting on X_XGB_test. So likely to be better than one model.
      """
      X_XGB = copy.deepcopy(X_train)
      Y_XGB = copy.deepcopy(y_train)
      X_XGB_test = copy.deepcopy(X_test)
      ####################################
      start_time = time.time()
      top_num = 10
      if isinstance(Y_XGB, pd.Series):
          targets = [Y_XGB.name]
      else:
          targets = Y_XGB.columns.tolist()
      if len(targets) == 1:
          multi_label = False
          if isinstance(Y_XGB, pd.DataFrame):
              Y_XGB = pd.Series(
                  Y_XGB.values.ravel(),
                  name=targets[0],
                  index=Y_XGB.index,
              )
      else:
          multi_label = True
      modeltype, _ = analyze_problem_type(Y_XGB, targets)
      columns = X_XGB.columns
      ##### Now continue with scaler pre-processing ###########
      if isinstance(scaler, str):
          if not scaler == "":
              scaler = scaler.lower()
      if scaler == "standard":
          scaler = StandardScaler()
      elif scaler == "minmax":
          scaler = MinMaxScaler()
      else:
          scaler = StandardScaler()
      #########     G P U     P R O C E S S I N G      B E G I N S    ############
      ###### This is where we set the CPU and GPU parameters for XGBoost
      if GPU_flag:
          GPU_exists = check_if_GPU_exists()
      else:
          GPU_exists = False
      #####   Set the Scoring Parameters here based on each model and preferences of user ###
      cpu_params = {}
      param = {}
      cpu_params["tree_method"] = "hist"
      cpu_params["gpu_id"] = 0
      cpu_params["updater"] = "grow_colmaker"
      cpu_params["predictor"] = "cpu_predictor"
      if GPU_exists:
          param["tree_method"] = "gpu_hist"
          param["gpu_id"] = 0
          param["updater"] = "grow_gpu_hist"  #'prune'
          param["predictor"] = "gpu_predictor"
          print(
              "    Hyper Param Tuning XGBoost with GPU parameters. This will take time. Please be patient..."
          )
      else:
          param = copy.deepcopy(cpu_params)
          print(
              "    Hyper Param Tuning XGBoost with CPU parameters. This will take time. Please be patient..."
          )
      #################################################################################
      if modeltype == "Regression":
          if log_y:
              Y_XGB.loc[
                  Y_XGB == 0
              ] = 1e-15  ### just set something that is zero to a very small number
  
      #########  Now set the number of rows we need to tune hyper params ###
      scoreFunction = {
          "precision": "precision_weighted",
          "recall": "recall_weighted",
      }
      random_search_flag = True
  
      #### We need a small validation data set for hyper-param tuning #########################
      hyper_frac = 0.2
      #### now select a random sample from X_XGB ##
  
      if modeltype == "Regression":
          X_train, X_valid, Y_train, Y_valid = train_test_split(
              X_XGB, Y_XGB, test_size=hyper_frac, random_state=999
          )
      else:
          X_train, X_valid, Y_train, Y_valid = train_test_split(
              X_XGB,
              Y_XGB,
              test_size=hyper_frac,
              random_state=999,
              stratify=Y_XGB,
          )
  
      ######  This step is needed for making sure y is transformed to log_y ####################
      if modeltype == "Regression" and log_y:
          Y_train = np.log(Y_train)
          Y_valid = np.log(Y_valid)
  
      #### First convert test data into numeric using train data ###
      X_train, Y_train, X_valid, Y_valid, scaler = data_transform(
          X_train,
          Y_train,
          X_valid,
          Y_valid,
          modeltype,
          multi_label,
          scaler=scaler,
          enc_method=enc_method,
      )
  
      ######  Time to hyper-param tune model using randomizedsearchcv and partial train data #########
      num_boost_round = xgbm_model_fit(
          random_search_flag,
          X_train,
          Y_train,
          X_valid,
          Y_valid,
          modeltype,
          multi_label,
          log_y,
          num_boost_round=num_boost_round,
      )
  
      #### First convert test data into numeric using train data ###############################
      if not isinstance(X_XGB_test, str):
          x_train, y_train, x_test, _, _ = data_transform(
              X_XGB,
              Y_XGB,
              X_XGB_test,
              "",
              modeltype,
              multi_label,
              scaler=scaler,
              enc_method=enc_method,
          )
  
      ######  Time to train the hyper-tuned model on full train data ##########################
      random_search_flag = False
      model = xgbm_model_fit(
          random_search_flag,
          x_train,
          y_train,
          x_test,
          "",
          modeltype,
          multi_label,
          log_y,
          num_boost_round=num_boost_round,
      )
  
      #############  Time to get feature importances based on full train data   ################
  
      if multi_label:
          for i, target_name in enumerate(targets):
              each_model = model.estimators_[i]
              imp_feats = dict(
                  zip(x_train.columns, each_model.feature_importances_)
              )
              importances = (
                  pd.Series(imp_feats)
                  .sort_values(ascending=False)[:top_num]
                  .values
              )
              important_features = (
                  pd.Series(imp_feats)
                  .sort_values(ascending=False)[:top_num]
                  .index.tolist()
              )
              print(
                  "Top 10 features for {}: {}".format(
                      target_name, important_features
                  )
              )
      else:
          imp_feats = model.get_score(fmap="", importance_type="gain")
          importances = (
              pd.Series(imp_feats)
              .sort_values(ascending=False)[:top_num]
              .values
          )
          important_features = (
              pd.Series(imp_feats)
              .sort_values(ascending=False)[:top_num]
              .index.tolist()
          )
          print("Top 10 features:\n%s" % important_features[:top_num])
          #######  order this in the same order in which they were collected ######
          feature_importances = pd.DataFrame(
              importances,
              index=important_features,
              columns=["importance"],
          )
  
      ######  Time to consolidate the predictions on test data ################################
      if not multi_label and not isinstance(X_XGB_test, str):
          x_test = xgb.DMatrix(x_test)
      if isinstance(X_XGB_test, str):
          print(
              "No predictions since X_XGB_test is empty string. Returning..."
          )
          return {}
      if modeltype == "Regression":
          if not isinstance(X_XGB_test, str):
              if log_y:
                  pred_xgbs = np.exp(model.predict(x_test))
              else:
                  pred_xgbs = model.predict(x_test)
              #### if there is no test data just return empty strings ###
          else:
              pred_xgbs = []
      else:
          if multi_label:
              pred_xgbs = model.predict(x_test)
              pred_probas = model.predict_proba(x_test)
          else:
              pred_probas = model.predict(x_test)
              if modeltype == "Multi_Classification":
                  pred_xgbs = pred_probas.argmax(axis=1)
              else:
                  pred_xgbs = (pred_probas > 0.5).astype(int)
      ##### once the entire model is trained on full train data ##################
      print(
          "    Time taken for training XGBoost on entire train data (in minutes) = %0.1f"
          % ((time.time() - start_time) / 60)
      )
      if multi_label:
          for i, target_name in enumerate(targets):
              each_model = model.estimators_[i]
              xgb.plot_importance(
                  each_model,
                  importance_type="gain",
                  title="XGBoost model feature importances for %s"
                  % target_name,
              )
      else:
          xgb.plot_importance(
              model,
              importance_type="gain",
              title="XGBoost final model feature importances",
          )
      print("Returning the following:")
      print("    Model = %s" % model)
      print("    Scaler = %s" % scaler)
      if modeltype == "Regression":
          if not isinstance(X_XGB_test, str):
              print("    (3) sample predictions:%s" % pred_xgbs[:3])
          return (pred_xgbs, scaler, model)
      else:
          if not isinstance(X_XGB_test, str):
              print(
                  "    (3) sample predictions (may need to be transformed to original labels):%s"
                  % pred_xgbs[:3]
              )
              print(
                  "    (3) sample predicted probabilities:%s"
                  % pred_probas[:3]
              )
          return (pred_xgbs, scaler, model)
  
imports: import pandas
  import numpy
  from sklearn.model_selection import train_test_split
  from sklearn.model_selection import KFold
  from sklearn.model_selection import GridSearchCV
  from sklearn.multioutput import MultiOutputClassifier, MultiOutputRegressor
  from sklearn.multiclass import OneVsRestClassifier
  import xgboost
  from xgboost.sklearn import XGBClassifier
  from xgboost.sklearn import XGBRegressor
  from sklearn.model_selection import train_test_split
  from sklearn.multiclass import OneVsRestClassifier
  from sklearn.preprocessing import LabelEncoder
  import lightgbm
  from sklearn.model_selection import KFold, cross_val_score, StratifiedKFold
  import seaborn
  from sklearn.preprocessing import OneHotEncoder, LabelEncoder, label_binarize
  import csv
  import re
  from xgboost import XGBRegressor, XGBClassifier
  from sklearn.metrics import mean_squared_log_error, mean_squared_error, balanced_accuracy_score
  from scipy import stats
  from sklearn.model_selection import RandomizedSearchCV
  import scipy
  import time
  import copy
  from sklearn.preprocessing import StandardScaler, MinMaxScaler
  from collections import Counter, defaultdict
  import pdb
  import time
  from sklearn.preprocessing import StandardScaler, MinMaxScaler
  from sklearn.impute import SimpleImputer
  from sklearn.preprocessing import OneHotEncoder
  from sklearn.feature_extraction.text import CountVectorizer
  from sklearn.linear_model import LogisticRegression
  from sklearn.compose import make_column_transformer
  from sklearn.pipeline import make_pipeline
  from sklearn.preprocessing import LabelEncoder, LabelBinarizer
  from sklearn.base import BaseEstimator, TransformerMixin
  import pdb
  from sklearn.base import TransformerMixin
  from collections import defaultdict
  from sklearn.preprocessing import MaxAbsScaler
  from sklearn.preprocessing import FunctionTransformer
  from ..utils import My_LabelEncoder, My_LabelEncoder_Pipe
  from ..utils import left_subtract
  import xgboost
  from sklearn.utils.class_weight import compute_class_weight
  import copy
  from collections import Counter
  from collections import OrderedDict
  from collections import defaultdict
  from collections import OrderedDict
  from sklearn.impute import SimpleImputer
label_desc: Use image node_id 1 for calling a global function with example usage: complex_XGBoost_model(X_train, y_train, X_test, log_y, GPU_flag, scaler, enc_method, n_splits, num_boost_round, verbose) and returns: dict, pred_xgbs,  scaler,  model, pred_xgbs,  scaler,  model