docs: def copy(self: T, deep: bool = False, data: Mapping = None) -> T:
          """Returns a copy of this dataset.
  
          If `deep=True`, a deep copy is made of each of the component variables.
          Otherwise, a shallow copy of each of the component variable is made, so
          that the underlying memory region of the new dataset is the same as in
          the original dataset.
  
          Use `data` to create a new object with the same structure as
          original but entirely new data.
  
          Parameters
          ----------
          deep : bool, optional
              Whether each component variable is loaded into memory and copied onto
              the new object. Default is False.
          data : dict-like, optional
              Data to use in the new object. Each item in `data` must have same
              shape as corresponding data variable in original. When `data` is
              used, `deep` is ignored for the data variables and only used for
              coords.
  
          Returns
          -------
          object : Dataset
              New object with dimensions, attributes, coordinates, name, encoding,
              and optionally data copied from original.
  
          Examples
          --------
  
          Shallow copy versus deep copy
  
          >>> da = xr.DataArray(np.random.randn(2, 3))
          >>> ds = xr.Dataset({'foo': da, 'bar': ('x', [-1, 2])},
                              coords={'x': ['one', 'two']})
          >>> ds.copy()
          <xarray.Dataset>
          Dimensions:  (dim_0: 2, dim_1: 3, x: 2)
          Coordinates:
          * x        (x) <U3 'one' 'two'
          Dimensions without coordinates: dim_0, dim_1
          Data variables:
              foo      (dim_0, dim_1) float64 -0.8079 0.3897 -1.862 -0.6091 -1.051 -0.3003
              bar      (x) int64 -1 2
          >>> ds_0 = ds.copy(deep=False)
          >>> ds_0['foo'][0, 0] = 7
          >>> ds_0
          <xarray.Dataset>
          Dimensions:  (dim_0: 2, dim_1: 3, x: 2)
          Coordinates:
          * x        (x) <U3 'one' 'two'
          Dimensions without coordinates: dim_0, dim_1
          Data variables:
              foo      (dim_0, dim_1) float64 7.0 0.3897 -1.862 -0.6091 -1.051 -0.3003
              bar      (x) int64 -1 2
          >>> ds
          <xarray.Dataset>
          Dimensions:  (dim_0: 2, dim_1: 3, x: 2)
          Coordinates:
          * x        (x) <U3 'one' 'two'
          Dimensions without coordinates: dim_0, dim_1
          Data variables:
              foo      (dim_0, dim_1) float64 7.0 0.3897 -1.862 -0.6091 -1.051 -0.3003
              bar      (x) int64 -1 2
  
          Changing the data using the ``data`` argument maintains the
          structure of the original object, but with the new data. Original
          object is unaffected.
  
          >>> ds.copy(data={'foo': np.arange(6).reshape(2, 3), 'bar': ['a', 'b']})
          <xarray.Dataset>
          Dimensions:  (dim_0: 2, dim_1: 3, x: 2)
          Coordinates:
          * x        (x) <U3 'one' 'two'
          Dimensions without coordinates: dim_0, dim_1
          Data variables:
              foo      (dim_0, dim_1) int64 0 1 2 3 4 5
              bar      (x) <U1 'a' 'b'
          >>> ds
          <xarray.Dataset>
          Dimensions:  (dim_0: 2, dim_1: 3, x: 2)
          Coordinates:
          * x        (x) <U3 'one' 'two'
          Dimensions without coordinates: dim_0, dim_1
          Data variables:
              foo      (dim_0, dim_1) float64 7.0 0.3897 -1.862 -0.6091 -1.051 -0.3003
              bar      (x) int64 -1 2
  
          See Also
          --------
          pandas.DataFrame.copy
          """  # noqa
          if data is None:
              variables = OrderedDict((k, v.copy(deep=deep))
                                      for k, v in self._variables.items())
          elif not utils.is_dict_like(data):
              raise ValueError('Data must be dict-like')
          else:
              var_keys = set(self.data_vars.keys())
              data_keys = set(data.keys())
              keys_not_in_vars = data_keys - var_keys
              if keys_not_in_vars:
                  raise ValueError(
                      'Data must only contain variables in original '
                      'dataset. Extra variables: {}'
                      .format(keys_not_in_vars))
              keys_missing_from_data = var_keys - data_keys
              if keys_missing_from_data:
                  raise ValueError(
                      'Data must contain all variables in original '
                      'dataset. Data is missing {}'
                      .format(keys_missing_from_data))
              variables = OrderedDict((k, v.copy(deep=deep, data=data.get(k)))
                                      for k, v in self._variables.items())
  
          attrs = copy.deepcopy(self._attrs) if deep else copy.copy(self._attrs)
  
          return self._replace(variables, attrs=attrs)