docstr: Fits the XGBoost model.
  
  :param train_data: encoded features for training dataset
  :param dev_data: encoded features for dev dataset
docstr_tok: ["Fits","the","XGBoost","model",".",":","param","train_data",":","encoded","features","for","training","dataset",":","param","dev_data",":","encoded","features","for","dev","dataset"]
code: def fit(self, train_data: EncodedDs, dev_data: EncodedDs) -> None:
      """
      Fits the XGBoost model.
  
      :param train_data: encoded features for training dataset
      :param dev_data: encoded features for dev dataset
      """
      started = time.time()
  
      log.info("Started fitting XGBoost model")
      self.fit_data_len = len(train_data)
      self.positive_domain = getattr(
          train_data.encoders.get(self.target, None),
          "positive_domain",
          False,
      )
      output_dtype = self.dtype_dict[self.target]
  
      if output_dtype not in self.cls_dtypes + self.num_dtypes:
          log.error(
              f"XGBoost mixer not supported for type: {output_dtype}"
          )
          raise Exception(
              f"XGBoost mixer not supported for type: {output_dtype}"
          )
      else:
          objective = (
              "reg:squarederror"
              if output_dtype in self.num_dtypes
              else "multi:softprob"
          )
          metric = (
              "rmse" if output_dtype in self.num_dtypes else "mlogloss"
          )
  
      self.params = {
          "objective": objective,
          "eval_metric": metric,
          "n_jobs": get_nr_procs(train_data.data_frame),
          "process_type": "default",  # normal training
          "verbosity": 0,
          "early_stopping_rounds": 5
          # 'device_type': self.device_str,  # TODO
      }
  
      # Prepare the data
      train_dataset, train_labels = self._to_dataset(
          train_data, output_dtype, mode="train"
      )
      dev_dataset, dev_labels = self._to_dataset(
          dev_data, output_dtype, mode="dev"
      )
  
      if output_dtype not in self.num_dtypes:
          self.all_classes = self.ordinal_encoder.categories_[0]
          self.params["num_class"] = self.all_classes.size
          model_class = xgb.XGBClassifier
      else:
          model_class = xgb.XGBRegressor
  
      # Determine time per iterations
      start = time.time()
      self.params["n_estimators"] = 1
  
      with xgb.config_context(verbosity=0):
          self.model = model_class(**self.params)
          self.model.fit(
              train_dataset,
              train_labels,
              eval_set=[(dev_dataset, dev_labels)],
          )
  
      end = time.time()
      seconds_for_one_iteration = max(0.1, end - start)
  
      self.stop_after = max(
          1, self.stop_after - (time.time() - started)
      )
  
      # Determine nr of iterations
      log.info(
          f"A single GBM iteration takes {seconds_for_one_iteration} seconds"
      )
      self.num_iterations = int(
          self.stop_after * 0.8 / seconds_for_one_iteration
      )
  
      # TODO: Turn on grid search if training doesn't take too long using it
      # kwargs = {}
      # if self.use_optuna and self.num_iterations >= 200:
      #     model_generator = None
      #     kwargs['time_budget'] = self.stop_after * 0.4
      #     self.num_iterations = int(self.num_iterations / 2)
      #     kwargs['optuna_seed'] = 0
      # else:
      #     model_generator = None
  
      # Train the models
      log.info(
          f"Training XGBoost with {self.num_iterations} iterations given {self.stop_after} seconds constraint"
      )  # noqa
      if self.num_iterations < 1:
          self.num_iterations = 1
      self.params["n_estimators"] = int(self.num_iterations)
  
      # TODO: reinstance these on each run! keep or use via eval_set in fit()?
      # self.params['callbacks'] = [(train_dataset, 'train'), (dev_dataset, 'eval')]
  
      with xgb.config_context(verbosity=0):
          self.model = model_class(**self.params)
          self.model.fit(
              train_dataset,
              train_labels,
              eval_set=[(dev_dataset, dev_labels)],
          )
  
      if self.fit_on_dev:
          self.partial_fit(dev_data, train_data)
  
imports: from typing import Dict, List, Optional, Union
  import time
  import torch
  import optuna
  import xgboost
  import numpy
  import pandas
  from sklearn.preprocessing import OrdinalEncoder
  from type_infer.dtype import dtype
  from lightwood.helpers.log import log
  from lightwood.helpers.parallelism import get_nr_procs
  from lightwood.mixer.base import BaseMixer
  from lightwood.encoder.base import BaseEncoder
  from lightwood.api.types import PredictionArguments
  from lightwood.data.encoded_ds import EncodedDs
label_desc: Use image node_id 3 for calling the XGBoostMixer obj's underlying member method code with example usage: obj.fit(train_data, dev_data) without return types