class_docstr: This is a multi-task dataset that is constructed from the datasets of
  individual tasks. It offers functionality to combine parts of individual
  datasets to construct the inputs necessary for the `MultiTaskGP` models.
  
  The datasets of individual tasks are allowed to represent different sets
  of features. When there are heterogeneous feature sets, calling
  `MultiTaskDataset.X` will result in an error.
docstr: Construct a `MultiTaskDataset` from a joint dataset that includes the
  data for all tasks with the task feature index.
  
  This will break down the joint dataset into individual datasets by the value
  of the task feature. Each resulting dataset will have its outcome name set
  based on `outcome_names_per_task`, with the missing values defaulting to
  `task_<task_feature>` (except for the target task, which will retain the
  original outcome name from the dataset).
  
  Args:
      dataset: The joint dataset.
      task_feature_index: The column index of the task feature in `dataset.X`.
      target_task_value: The value of the task feature for the target task
          in the dataset. The data for the target task is filtered according to
          `dataset.X[task_feature_index] == target_task_value`.
      outcome_names_per_task: Optional dictionary mapping task feature values
          to the outcome names for each task. If not provided, the auxiliary
          tasks will be named `task_<task_feature>` and the target task will
          retain the outcome name from the dataset.
  
  Returns:
      A `MultiTaskDataset` instance.
code: def from_joint_dataset(
      cls,
      dataset: SupervisedDataset,
      task_feature_index: int,
      target_task_value: int,
      outcome_names_per_task: Optional[Dict[int, str]] = None,
  ) -> MultiTaskDataset:
      r"""Construct a `MultiTaskDataset` from a joint dataset that includes the
      data for all tasks with the task feature index.
  
      This will break down the joint dataset into individual datasets by the value
      of the task feature. Each resulting dataset will have its outcome name set
      based on `outcome_names_per_task`, with the missing values defaulting to
      `task_<task_feature>` (except for the target task, which will retain the
      original outcome name from the dataset).
  
      Args:
          dataset: The joint dataset.
          task_feature_index: The column index of the task feature in `dataset.X`.
          target_task_value: The value of the task feature for the target task
              in the dataset. The data for the target task is filtered according to
              `dataset.X[task_feature_index] == target_task_value`.
          outcome_names_per_task: Optional dictionary mapping task feature values
              to the outcome names for each task. If not provided, the auxiliary
              tasks will be named `task_<task_feature>` and the target task will
              retain the outcome name from the dataset.
  
      Returns:
          A `MultiTaskDataset` instance.
      """
      if len(dataset.outcome_names) > 1:
          raise UnsupportedError(
              "Dataset containing more than one outcome is not supported. "
              f"Got {dataset.outcome_names=}."
          )
      outcome_names_per_task = outcome_names_per_task or {}
      # Split datasets by task feature.
      datasets = []
      all_task_features = dataset.X[:, task_feature_index]
      for task_value in all_task_features.unique().long().tolist():
          default_name = (
              dataset.outcome_names[0]
              if task_value == target_task_value
              else f"task_{task_value}"
          )
          outcome_name = outcome_names_per_task.get(
              task_value, default_name
          )
          filter_mask = all_task_features == task_value
          new_dataset = SupervisedDataset(
              X=dataset.X[filter_mask],
              Y=dataset.Y[filter_mask],
              Yvar=dataset.Yvar[filter_mask]
              if dataset.Yvar is not None
              else None,
              feature_names=dataset.feature_names,
              outcome_names=[outcome_name],
          )
          datasets.append(new_dataset)
      # Return the new
      return cls(
          datasets=datasets,
          target_outcome_name=outcome_names_per_task.get(
              target_task_value, dataset.outcome_names[0]
          ),
          task_feature_index=task_feature_index,
      )
  
imports: from __future__ import annotations
  import collections
  import warnings
  from typing import Any, Dict, List, Optional, Union
  import torch
  from botorch.exceptions.errors import InputDataError, UnsupportedError
  from botorch.utils.containers import BotorchContainer, SliceContainer
  from torch import long, ones, Tensor
label_desc: Use image node_id 2 for calling the MultiTaskDataset obj's underlying member method code with example usage: obj.from_joint_dataset(cls, dataset, task_feature_index, target_task_value, outcome_names_per_task) and returns: cls