docstr: Transform MNIST dataset by applying rotation to images.
  Attribute (rotation angle) is independent of label Y.
  
  :param images: original MNIST images
  :param labels: original MNIST labels
  :param angle: Value of rotation angle used for transforming the image
  :returns: TensorDataset containing transformed images, labels, and attributes (angle)
docstr_tok: ["Transform","MNIST","dataset","by","applying","rotation","to","images",".","Attribute","(","rotation","angle",")","is","independent","of","label","Y.",":","param","images",":","original","MNIST","images",":","param","labels",":","original","MNIST","labels",":","param","angle",":","Value","of","rotation","angle","used","for","transforming","the","image",":","returns",":","TensorDataset","containing","transformed","images",",","labels",",","and","attributes","(","angle",")"]
code: def rotate_dataset(self, images, labels, env_id, angle):
      """
      Transform MNIST dataset by applying rotation to images.
      Attribute (rotation angle) is independent of label Y.
  
      :param images: original MNIST images
      :param labels: original MNIST labels
      :param angle: Value of rotation angle used for transforming the image
      :returns: TensorDataset containing transformed images, labels, and attributes (angle)
      """
      rotation = transforms.Compose(
          [
              transforms.ToPILImage(),
              transforms.Lambda(
                  lambda x: rotate(
                      x,
                      int(angle),
                      fill=(0,),
                      interpolation=torchvision.transforms.InterpolationMode.BILINEAR,
                  )
              ),
              transforms.ToTensor(),
          ]
      )
  
      # Subsample 2x for computational convenience
      images = images.reshape((-1, 28, 28))[:, ::2, ::2]
      # Assign a binary label based on the digit
      labels = (labels < 5).float()
      # Flip label with probability 0.25
      labels = self.torch_xor_(
          labels, self.torch_bernoulli_(0.25, len(labels))
      )
  
      x = torch.zeros(len(images), 1, 14, 14)
      for i in range(len(images)):
          x[i] = rotation(images[i].float().div_(255.0))
  
      y = labels.view(-1).long()
      a = torch.full((y.shape[0],), env_id, dtype=torch.float32)
      a = torch.unsqueeze(a, 1)
  
      return TensorDataset(x, y, a)
  
imports: import torch
  import torchvision
  from PIL import Image
  from torch.utils.data import TensorDataset
  from torchvision import transforms
  from torchvision.datasets import MNIST
  from torchvision.transforms.functional import rotate
  from dowhy.causal_prediction.datasets.base_dataset import MultipleDomainDataset
label_desc: Use image node_id 2 for calling the MNISTIndAttribute obj's underlying member method code with example usage: obj.rotate_dataset(images, labels, env_id, angle) and returns: TensorDataset