docstr: Calls the mixer to emit forecasts.
  
  NOTE: in the future we may support predicting every single row efficiently. For now, this mixer
  replicates the neuralforecast library behavior and returns a forecast strictly for the next `tss.horizon`
  timesteps after the end of the input dataframe.
docstr_tok: ["Calls","the","mixer","to","emit","forecasts",".","NOTE",":","in","the","future","we","may","support","predicting","every","single","row","efficiently",".","For","now",",","this","mixer","replicates","the","neuralforecast","library","behavior","and","returns","a","forecast","strictly","for","the","next","`","tss.horizon","`","timesteps","after","the","end","of","the","input","dataframe","."]
code: def __call__(
      self,
      ds: Union[EncodedDs, ConcatedEncodedDs],
      args: PredictionArguments = PredictionArguments(),
  ) -> pd.DataFrame:
      """
      Calls the mixer to emit forecasts.
  
      NOTE: in the future we may support predicting every single row efficiently. For now, this mixer
      replicates the neuralforecast library behavior and returns a forecast strictly for the next `tss.horizon`
      timesteps after the end of the input dataframe.
      """  # noqa
      if args.predict_proba:
          log.warning(
              "This mixer does not output probability estimates"
          )
  
      # provided quantile must match one of the training levels, else we default to the largest one of these
      if (
          args.fixed_confidence is not None
          and int(args.fixed_confidence * 100) in self.conf_level
      ):
          level = int(args.fixed_confidence * 100)
      else:
          level = max(self.conf_level)
  
      target_cols = ["prediction", "lower", "upper"]
      pred_cols = [
          f"{self.model_class_str.upper()}-median",
          f"{self.model_class_str.upper()}-lo-{level}",
          f"{self.model_class_str.upper()}-hi-{level}",
      ]
  
      input_df, idxs = self._make_initial_df(deepcopy(ds.data_frame))
      length = (
          sum(ds.encoded_ds_lengths)
          if isinstance(ds, ConcatedEncodedDs)
          else len(ds)
      )
      ydf = pd.DataFrame(
          0, index=range(length), columns=target_cols, dtype=object
      )
  
      # fill with zeroed arrays
      zero_array = [0 for _ in range(self.horizon)]
      for target_col in target_cols:
          ydf[target_col] = [zero_array] * len(ydf)
  
      grouper = input_df.groupby("unique_id")
      group_ends = grouper.last()["index"].values
      fcst = self.model.predict(input_df).reset_index()
      fcst["ds"] = fcst.groupby("unique_id").cumcount()
      horizons = pd.pivot_table(
          fcst, values=pred_cols, index="unique_id", columns="ds"
      )
  
      temp_df = pd.DataFrame(
          0,  # zero-filled
          index=range(len(horizons)),
          columns=target_cols,
          dtype=object,
      )
  
      for pcol, tcol in zip(pred_cols, target_cols):
          temp_df[tcol] = horizons[pcol].values.tolist()
  
      for tcol in target_cols:
          ydf[tcol].iloc[group_ends] = temp_df[tcol]
  
      ydf["confidence"] = level / 100
      return ydf
  
file_path: lightwood/lightwood/mixer/nhits.py
imports: from typing import Dict, Union, Optional
  from copy import deepcopy
  import numpy
  import pandas
  from neuralforecast import NeuralForecast
  from neuralforecast.models.nhits import NHITS
  from neuralforecast.models.nbeats import NBEATS
  from neuralforecast.losses.pytorch import MQLoss
  from lightwood.helpers.log import log
  from lightwood.mixer.base import BaseMixer
  from lightwood.api.types import PredictionArguments
  from lightwood.data.encoded_ds import EncodedDs, ConcatedEncodedDs
label_desc: Use image node_id 4 for calling the NHitsMixer obj's underlying member method code with example usage: obj.__call__(ds, args) and returns: ydf