code: def normalize_labels_shape(
      y: np.ndarray,
      mode: Optional[str] = None,
      n_tasks: Optional[int] = None,
      n_classes: Optional[int] = None,
  ) -> np.ndarray:
      """A utility function to correct the shape of the labels.
  
      Parameters
      ----------
      y: np.ndarray
          `y` is an array of shape `(N,)` or `(N, n_tasks)` or `(N, n_tasks, 1)`.
      mode: str, default None
          If `mode` is "classification" or "regression", attempts to apply
          data transformations.
      n_tasks: int, default None
          The number of tasks this class is expected to handle.
      n_classes: int, default None
          If specified use this as the number of classes. Else will try to
          impute it as `n_classes = max(y) + 1` for arrays and as
          `n_classes=2` for the case of scalars. Note this parameter only
          has value if `mode=="classification"`
  
      Returns
      -------
      y_out: np.ndarray
          If `mode=="classification"`, `y_out` is an array of shape `(N,
          n_tasks, n_classes)`. If `mode=="regression"`, `y_out` is an array
          of shape `(N, n_tasks)`.
      """
      if n_tasks is None:
          raise ValueError("n_tasks must be specified")
      if mode not in ["classification", "regression"]:
          raise ValueError(
              "mode must be either classification or regression."
          )
      if mode == "classification" and n_classes is None:
          raise ValueError("n_classes must be specified")
      if not isinstance(y, np.ndarray):
          raise ValueError("y must be a np.ndarray")
      # Handle n_classes/n_task shape ambiguity
      if mode == "classification" and len(y.shape) == 2:
          if (
              n_classes == y.shape[1]
              and n_tasks != 1
              and n_classes != n_tasks
          ):
              raise ValueError(
                  "Shape of input doesn't match expected n_tasks=1"
              )
          elif n_classes == y.shape[1] and n_tasks == 1:
              # Add in task dimension
              y = np.expand_dims(y, 1)
      if len(y.shape) == 1 and n_tasks != 1:
          raise ValueError(
              "n_tasks must equal 1 for a 1D set of labels."
          )
      if (
          len(y.shape) == 2 or len(y.shape) == 3
      ) and n_tasks != y.shape[1]:
          raise ValueError(
              "Shape of input doesn't match expected n_tasks=%d"
              % n_tasks
          )
      if len(y.shape) >= 4:
          raise ValueError(
              "Labels y must be a float scalar or a ndarray of shape `(N,)` or "
              "`(N, n_tasks)` or `(N, n_tasks, 1)` for regression problems and "
              "of shape `(N,)` or `(N, n_tasks)` or `(N, n_tasks, 1)` for classification problems"
          )
      if len(y.shape) == 1:
          # Insert a task dimension (we know n_tasks=1 from above0
          y_out = np.expand_dims(y, 1)
      elif len(y.shape) == 2:
          y_out = y
      elif len(y.shape) == 3:
          # If 3D and last dimension isn't 1, assume this is one-hot encoded and return as-is.
          if y.shape[-1] != 1:
              return y
          y_out = np.squeeze(y, axis=-1)
      # Handle classification. We need to convert labels into one-hot representation.
      if mode == "classification":
          all_y_task = []
          for task in range(n_tasks):
              y_task = y_out[:, task]
              # check whether n_classes is int or not
              assert isinstance(n_classes, int)
              y_hot = to_one_hot(y_task, n_classes=n_classes)
              y_hot = np.expand_dims(y_hot, 1)
              all_y_task.append(y_hot)
          y_out = np.concatenate(all_y_task, axis=1)
      return y_out
  
file_path: deepchem/deepchem/metrics/metric.py
imports: import logging
  from typing import Any, Callable, Optional
  import numpy
  from numpy.typing import ArrayLike
label_desc: Use image node_id 3 for calling a global function with example usage: normalize_labels_shape(y, mode, n_tasks, n_classes) and returns: y_out, y