text: import pickle
  import sys
  import warnings
  from copy import copy, deepcopy
  from io import StringIO
  from textwrap import dedent
  
  import numpy as np
  import pandas as pd
  import pytest
  from pandas.core.indexes.datetimes import DatetimeIndex
  
  import xarray as xr
  from xarray import (
      DataArray,
      Dataset,
      IndexVariable,
      MergeError,
      Variable,
      align,
      backends,
      broadcast,
      open_dataset,
      set_options,
  )
  from xarray.coding.cftimeindex import CFTimeIndex
  from xarray.core import dtypes, indexing, utils
  from xarray.core.common import duck_array_ops, full_like
  from xarray.core.npcompat import IS_NEP18_ACTIVE
  from xarray.core.pycompat import integer_types
  from xarray.core.utils import is_scalar
  
  from . import (
      InaccessibleArray,
      LooseVersion,
      UnexpectedDataAccess,
      assert_allclose,
      assert_array_equal,
      assert_equal,
      assert_identical,
      has_cftime,
      has_dask,
      raises_regex,
      requires_bottleneck,
      requires_cftime,
      requires_dask,
      requires_numbagg,
      requires_scipy,
      requires_sparse,
      source_ndarray,
  )
  
  try:
      import dask.array as da
  except ImportError:
      pass
  
  
  def create_test_data(seed=None):
      rs = np.random.RandomState(seed)
      _vars = {
          "var1": ["dim1", "dim2"],
          "var2": ["dim1", "dim2"],
          "var3": ["dim3", "dim1"],
      }
      _dims = {"dim1": 8, "dim2": 9, "dim3": 10}
  
      obj = Dataset()
      obj["time"] = ("time", pd.date_range("2000-01-01", periods=20))
      obj["dim2"] = ("dim2", 0.5 * np.arange(_dims["dim2"]))
      obj["dim3"] = ("dim3", list("abcdefghij"))
      for v, dims in sorted(_vars.items()):
          data = rs.normal(size=tuple(_dims[d] for d in dims))
          obj[v] = (dims, data, {"foo": "variable"})
      obj.coords["numbers"] = (
          "dim3",
          np.array([0, 1, 2, 0, 0, 1, 1, 2, 2, 3], dtype="int64"),
      )
      obj.encoding = {"foo": "bar"}
      assert all(obj.data.flags.writeable for obj in obj.variables.values())
      return obj
  
  
  def create_append_test_data(seed=None):
      rs = np.random.RandomState(seed)
  
      lat = [2, 1, 0]
      lon = [0, 1, 2]
      nt1 = 3
      nt2 = 2
      time1 = pd.date_range("2000-01-01", periods=nt1)
      time2 = pd.date_range("2000-02-01", periods=nt2)
      string_var = np.array(["ae", "bc", "df"], dtype=object)
      string_var_to_append = np.array(["asdf", "asdfg"], dtype=object)
      unicode_var = ["áó", "áó", "áó"]
      datetime_var = np.array(
          ["2019-01-01", "2019-01-02", "2019-01-03"], dtype="datetime64[s]"
      )
      datetime_var_to_append = np.array(
          ["2019-01-04", "2019-01-05"], dtype="datetime64[s]"
      )
      bool_var = np.array([True, False, True], dtype=np.bool)
      bool_var_to_append = np.array([False, True], dtype=np.bool)
  
      ds = xr.Dataset(
          data_vars={
              "da": xr.DataArray(
                  rs.rand(3, 3, nt1),
                  coords=[lat, lon, time1],
                  dims=["lat", "lon", "time"],
              ),
              "string_var": xr.DataArray(string_var, coords=[time1], dims=["time"]),
              "unicode_var": xr.DataArray(
                  unicode_var, coords=[time1], dims=["time"]
              ).astype(np.unicode_),
              "datetime_var": xr.DataArray(datetime_var, coords=[time1], dims=["time"]),
              "bool_var": xr.DataArray(bool_var, coords=[time1], dims=["time"]),
          }
      )
  
      ds_to_append = xr.Dataset(
          data_vars={
              "da": xr.DataArray(
                  rs.rand(3, 3, nt2),
                  coords=[lat, lon, time2],
                  dims=["lat", "lon", "time"],
              ),
              "string_var": xr.DataArray(
                  string_var_to_append, coords=[time2], dims=["time"]
              ),
              "unicode_var": xr.DataArray(
                  unicode_var[:nt2], coords=[time2], dims=["time"]
              ).astype(np.unicode_),
              "datetime_var": xr.DataArray(
                  datetime_var_to_append, coords=[time2], dims=["time"]
              ),
              "bool_var": xr.DataArray(bool_var_to_append, coords=[time2], dims=["time"]),
          }
      )
  
      ds_with_new_var = xr.Dataset(
          data_vars={
              "new_var": xr.DataArray(
                  rs.rand(3, 3, nt1 + nt2),
                  coords=[lat, lon, time1.append(time2)],
                  dims=["lat", "lon", "time"],
              )
          }
      )
  
      assert all(objp.data.flags.writeable for objp in ds.variables.values())
      assert all(objp.data.flags.writeable for objp in ds_to_append.variables.values())
      return ds, ds_to_append, ds_with_new_var
  
  
  def create_test_multiindex():
      mindex = pd.MultiIndex.from_product(
          [["a", "b"], [1, 2]], names=("level_1", "level_2")
      )
      return Dataset({}, {"x": mindex})
  
  
  def create_test_stacked_array():
      x = DataArray(pd.Index(np.r_[:10], name="x"))
      y = DataArray(pd.Index(np.r_[:20], name="y"))
      a = x * y
      b = x * y * y
      return a, b
  
  
  class InaccessibleVariableDataStore(backends.InMemoryDataStore):
      def __init__(self):
          super().__init__()
          self._indexvars = set()
  
      def store(self, variables, *args, **kwargs):
          super().store(variables, *args, **kwargs)
          for k, v in variables.items():
              if isinstance(v, IndexVariable):
                  self._indexvars.add(k)
  
      def get_variables(self):
          def lazy_inaccessible(k, v):
              if k in self._indexvars:
                  return v
              data = indexing.LazilyOuterIndexedArray(InaccessibleArray(v.values))
              return Variable(v.dims, data, v.attrs)
  
          return {k: lazy_inaccessible(k, v) for k, v in self._variables.items()}
  
  
  class TestDataset:
      def test_repr(self):
          data = create_test_data(seed=123)
          data.attrs["foo"] = "bar"
          # need to insert str dtype at runtime to handle different endianness
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:  (dim1: 8, dim2: 9, dim3: 10, time: 20)
              Coordinates:
                * time     (time) datetime64[ns] 2000-01-01 2000-01-02 ... 2000-01-20
                * dim2     (dim2) float64 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
                * dim3     (dim3) %s 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j'
                  numbers  (dim3) int64 0 1 2 0 0 1 1 2 2 3
              Dimensions without coordinates: dim1
              Data variables:
                  var1     (dim1, dim2) float64 -1.086 0.9973 0.283 ... 0.1995 0.4684 -0.8312
                  var2     (dim1, dim2) float64 1.162 -1.097 -2.123 ... 0.1302 1.267 0.3328
                  var3     (dim3, dim1) float64 0.5565 -0.2121 0.4563 ... -0.2452 -0.3616
              Attributes:
                  foo:      bar"""
              % data["dim3"].dtype
          )
          actual = "\n".join(x.rstrip() for x in repr(data).split("\n"))
          print(actual)
          assert expected == actual
  
          with set_options(display_width=100):
              max_len = max(map(len, repr(data).split("\n")))
              assert 90 < max_len < 100
  
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:  ()
              Data variables:
                  *empty*"""
          )
          actual = "\n".join(x.rstrip() for x in repr(Dataset()).split("\n"))
          print(actual)
          assert expected == actual
  
          # verify that ... doesn't appear for scalar coordinates
          data = Dataset({"foo": ("x", np.ones(10))}).mean()
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:  ()
              Data variables:
                  foo      float64 1.0"""
          )
          actual = "\n".join(x.rstrip() for x in repr(data).split("\n"))
          print(actual)
          assert expected == actual
  
          # verify long attributes are truncated
          data = Dataset(attrs={"foo": "bar" * 1000})
          assert len(repr(data)) < 1000
  
      def test_repr_multiindex(self):
          data = create_test_multiindex()
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:  (x: 4)
              Coordinates:
                * x        (x) MultiIndex
                - level_1  (x) object 'a' 'a' 'b' 'b'
                - level_2  (x) int64 1 2 1 2
              Data variables:
                  *empty*"""
          )
          actual = "\n".join(x.rstrip() for x in repr(data).split("\n"))
          print(actual)
          assert expected == actual
  
          # verify that long level names are not truncated
          mindex = pd.MultiIndex.from_product(
              [["a", "b"], [1, 2]], names=("a_quite_long_level_name", "level_2")
          )
          data = Dataset({}, {"x": mindex})
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:                  (x: 4)
              Coordinates:
                * x                        (x) MultiIndex
                - a_quite_long_level_name  (x) object 'a' 'a' 'b' 'b'
                - level_2                  (x) int64 1 2 1 2
              Data variables:
                  *empty*"""
          )
          actual = "\n".join(x.rstrip() for x in repr(data).split("\n"))
          print(actual)
          assert expected == actual
  
      def test_repr_period_index(self):
          data = create_test_data(seed=456)
          data.coords["time"] = pd.period_range("2000-01-01", periods=20, freq="B")
  
          # check that creating the repr doesn't raise an error #GH645
          repr(data)
  
      def test_unicode_data(self):
          # regression test for GH834
          data = Dataset({"foø": ["ba®"]}, attrs={"å": "∑"})
          repr(data)  # should not raise
  
          byteorder = "<" if sys.byteorder == "little" else ">"
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:  (foø: 1)
              Coordinates:
                * foø      (foø) %cU3 %r
              Data variables:
                  *empty*
              Attributes:
                  å:        ∑"""
              % (byteorder, "ba®")
          )
          actual = str(data)
          assert expected == actual
  
      @pytest.mark.skipif(not IS_NEP18_ACTIVE, reason="requires __array_function__")
      def test_repr_nep18(self):
          class Array:
              def __init__(self):
                  self.shape = (2,)
                  self.dtype = np.dtype(np.float64)
  
              def __array_function__(self, *args, **kwargs):
                  pass
  
              def __repr__(self):
                  return "Custom\nArray"
  
          dataset = Dataset({"foo": ("x", Array())})
          expected = dedent(
              """\
              <xarray.Dataset>
              Dimensions:  (x: 2)
              Dimensions without coordinates: x
              Data variables:
                  foo      (x) float64 Custom Array"""
          )
          assert expected == repr(dataset)
  
      def test_info(self):
          ds = create_test_data(seed=123)
          ds = ds.drop_vars("dim3")  # string type prints differently in PY2 vs PY3
          ds.attrs["unicode_attr"] = "ba®"
          ds.attrs["string_attr"] = "bar"
  
          buf = StringIO()
          ds.info(buf=buf)
  
          expected = dedent(
              """\
          xarray.Dataset {
          dimensions:
          \tdim1 = 8 ;
          \tdim2 = 9 ;
          \tdim3 = 10 ;
          \ttime = 20 ;
  
          variables:
          \tdatetime64[ns] time(time) ;
          \tfloat64 dim2(dim2) ;
          \tfloat64 var1(dim1, dim2) ;
          \t\tvar1:foo = variable ;
          \tfloat64 var2(dim1, dim2) ;
          \t\tvar2:foo = variable ;
          \tfloat64 var3(dim3, dim1) ;
          \t\tvar3:foo = variable ;
          \tint64 numbers(dim3) ;
  
          // global attributes:
          \t:unicode_attr = ba® ;
          \t:string_attr = bar ;
          }"""
          )
          actual = buf.getvalue()
          assert expected == actual
          buf.close()
  
      def test_constructor(self):
          x1 = ("x", 2 * np.arange(100))
          x2 = ("x", np.arange(1000))
          z = (["x", "y"], np.arange(1000).reshape(100, 10))
  
          with raises_regex(ValueError, "conflicting sizes"):
              Dataset({"a": x1, "b": x2})
          with raises_regex(ValueError, "disallows such variables"):
              Dataset({"a": x1, "x": z})
          with raises_regex(TypeError, "tuple of form"):
              Dataset({"x": (1, 2, 3, 4, 5, 6, 7)})
          with raises_regex(ValueError, "already exists as a scalar"):
              Dataset({"x": 0, "y": ("x", [1, 2, 3])})
  
          # verify handling of DataArrays
          expected = Dataset({"x": x1, "z": z})
          actual = Dataset({"z": expected["z"]})
          assert_identical(expected, actual)
  
      def test_constructor_invalid_dims(self):
          # regression for GH1120
          with pytest.raises(MergeError):
              Dataset(
                  data_vars=dict(v=("y", [1, 2, 3, 4])),
                  coords=dict(y=DataArray([0.1, 0.2, 0.3, 0.4], dims="x")),
              )
  
      def test_constructor_1d(self):
          expected = Dataset({"x": (["x"], 5.0 + np.arange(5))})
          actual = Dataset({"x": 5.0 + np.arange(5)})
          assert_identical(expected, actual)
  
          actual = Dataset({"x": [5, 6, 7, 8, 9]})
          assert_identical(expected, actual)
  
      def test_constructor_0d(self):
          expected = Dataset({"x": ([], 1)})
          for arg in [1, np.array(1), expected["x"]]:
              actual = Dataset({"x": arg})
              assert_identical(expected, actual)
  
          class Arbitrary:
              pass
  
          d = pd.Timestamp("2000-01-01T12")
          args = [
              True,
              None,
              3.4,
              np.nan,
              "hello",
              b"raw",
              np.datetime64("2000-01-01"),
              d,
              d.to_pydatetime(),
              Arbitrary(),
          ]
          for arg in args:
              print(arg)
              expected = Dataset({"x": ([], arg)})
              actual = Dataset({"x": arg})
              assert_identical(expected, actual)
  
      def test_constructor_deprecated(self):
          with raises_regex(ValueError, "DataArray dimensions"):
              DataArray([1, 2, 3], coords={"x": [0, 1, 2]})
  
      def test_constructor_auto_align(self):
          a = DataArray([1, 2], [("x", [0, 1])])
          b = DataArray([3, 4], [("x", [1, 2])])
  
          # verify align uses outer join
          expected = Dataset(
              {"a": ("x", [1, 2, np.nan]), "b": ("x", [np.nan, 3, 4])}, {"x": [0, 1, 2]}
          )
          actual = Dataset({"a": a, "b": b})
          assert_identical(expected, actual)
  
          # regression test for GH346
          assert isinstance(actual.variables["x"], IndexVariable)
  
          # variable with different dimensions
          c = ("y", [3, 4])
          expected2 = expected.merge({"c": c})
          actual = Dataset({"a": a, "b": b, "c": c})
          assert_identical(expected2, actual)
  
          # variable that is only aligned against the aligned variables
          d = ("x", [3, 2, 1])
          expected3 = expected.merge({"d": d})
          actual = Dataset({"a": a, "b": b, "d": d})
          assert_identical(expected3, actual)
  
          e = ("x", [0, 0])
          with raises_regex(ValueError, "conflicting sizes"):
              Dataset({"a": a, "b": b, "e": e})
  
      def test_constructor_pandas_sequence(self):
  
          ds = self.make_example_math_dataset()
          pandas_objs = {
              var_name: ds[var_name].to_pandas() for var_name in ["foo", "bar"]
          }
          ds_based_on_pandas = Dataset(pandas_objs, ds.coords, attrs=ds.attrs)
          del ds_based_on_pandas["x"]
          assert_equal(ds, ds_based_on_pandas)
  
          # reindex pandas obj, check align works
          rearranged_index = reversed(pandas_objs["foo"].index)
          pandas_objs["foo"] = pandas_objs["foo"].reindex(rearranged_index)
          ds_based_on_pandas = Dataset(pandas_objs, ds.coords, attrs=ds.attrs)
          del ds_based_on_pandas["x"]
          assert_equal(ds, ds_based_on_pandas)
  
      def test_constructor_pandas_single(self):
  
          das = [
              DataArray(np.random.rand(4), dims=["a"]),  # series
              DataArray(np.random.rand(4, 3), dims=["a", "b"]),  # df
          ]
  
          if LooseVersion(pd.__version__) < "0.25.0":
              das.append(DataArray(np.random.rand(4, 3, 2), dims=["a", "b", "c"]))
  
          with warnings.catch_warnings():
              warnings.filterwarnings("ignore", r"\W*Panel is deprecated")
              for a in das:
                  pandas_obj = a.to_pandas()
                  ds_based_on_pandas = Dataset(pandas_obj)
                  for dim in ds_based_on_pandas.data_vars:
                      assert_array_equal(ds_based_on_pandas[dim], pandas_obj[dim])
  
      def test_constructor_compat(self):
          data = {"x": DataArray(0, coords={"y": 1}), "y": ("z", [1, 1, 1])}
          expected = Dataset({"x": 0}, {"y": ("z", [1, 1, 1])})
          actual = Dataset(data)
          assert_identical(expected, actual)
  
          data = {"y": ("z", [1, 1, 1]), "x": DataArray(0, coords={"y": 1})}
          actual = Dataset(data)
          assert_identical(expected, actual)
  
          original = Dataset(
              {"a": (("x", "y"), np.ones((2, 3)))},
              {"c": (("x", "y"), np.zeros((2, 3))), "x": [0, 1]},
          )
          expected = Dataset(
              {"a": ("x", np.ones(2)), "b": ("y", np.ones(3))},
              {"c": (("x", "y"), np.zeros((2, 3))), "x": [0, 1]},
          )
  
          actual = Dataset(
              {"a": original["a"][:, 0], "b": original["a"][0].drop_vars("x")}
          )
          assert_identical(expected, actual)
  
          data = {"x": DataArray(0, coords={"y": 3}), "y": ("z", [1, 1, 1])}
          with pytest.raises(MergeError):
              Dataset(data)
  
          data = {"x": DataArray(0, coords={"y": 1}), "y": [1, 1]}
          actual = Dataset(data)
          expected = Dataset({"x": 0}, {"y": [1, 1]})
          assert_identical(expected, actual)
  
      def test_constructor_with_coords(self):
          with raises_regex(ValueError, "found in both data_vars and"):
              Dataset({"a": ("x", [1])}, {"a": ("x", [1])})
  
          ds = Dataset({}, {"a": ("x", [1])})
          assert not ds.data_vars
          assert list(ds.coords.keys()) == ["a"]
  
          mindex = pd.MultiIndex.from_product(
              [["a", "b"], [1, 2]], names=("level_1", "level_2")
          )
          with raises_regex(ValueError, "conflicting MultiIndex"):
              Dataset({}, {"x": mindex, "y": mindex})
              Dataset({}, {"x": mindex, "level_1": range(4)})
  
      def test_properties(self):
          ds = create_test_data()
          assert ds.dims == {"dim1": 8, "dim2": 9, "dim3": 10, "time": 20}
          assert list(ds.dims) == sorted(ds.dims)
          assert ds.sizes == ds.dims
  
          # These exact types aren't public API, but this makes sure we don't
          # change them inadvertently:
          assert isinstance(ds.dims, utils.Frozen)
          assert isinstance(ds.dims.mapping, utils.SortedKeysDict)
          assert type(ds.dims.mapping.mapping) is dict
  
          assert list(ds) == list(ds.data_vars)
          assert list(ds.keys()) == list(ds.data_vars)
          assert "aasldfjalskdfj" not in ds.variables
          assert "dim1" in repr(ds.variables)
          assert len(ds) == 3
          assert bool(ds)
  
          assert list(ds.data_vars) == ["var1", "var2", "var3"]
          assert list(ds.data_vars.keys()) == ["var1", "var2", "var3"]
          assert "var1" in ds.data_vars
          assert "dim1" not in ds.data_vars
          assert "numbers" not in ds.data_vars
          assert len(ds.data_vars) == 3
  
          assert set(ds.indexes) == {"dim2", "dim3", "time"}
          assert len(ds.indexes) == 3
          assert "dim2" in repr(ds.indexes)
  
          assert list(ds.coords) == ["time", "dim2", "dim3", "numbers"]
          assert "dim2" in ds.coords
          assert "numbers" in ds.coords
          assert "var1" not in ds.coords
          assert "dim1" not in ds.coords
          assert len(ds.coords) == 4
  
          assert Dataset({"x": np.int64(1), "y": np.float32([1, 2])}).nbytes == 16
  
      def test_asarray(self):
          ds = Dataset({"x": 0})
          with raises_regex(TypeError, "cannot directly convert"):
              np.asarray(ds)
  
      def test_get_index(self):
          ds = Dataset({"foo": (("x", "y"), np.zeros((2, 3)))}, coords={"x": ["a", "b"]})
          assert ds.get_index("x").equals(pd.Index(["a", "b"]))
          assert ds.get_index("y").equals(pd.Index([0, 1, 2]))
          with pytest.raises(KeyError):
              ds.get_index("z")
  
      def test_attr_access(self):
          ds = Dataset(
              {"tmin": ("x", [42], {"units": "Celcius"})}, attrs={"title": "My test data"}
          )
          assert_identical(ds.tmin, ds["tmin"])
          assert_identical(ds.tmin.x, ds.x)
  
          assert ds.title == ds.attrs["title"]
          assert ds.tmin.units == ds["tmin"].attrs["units"]
  
          assert {"tmin", "title"} <= set(dir(ds))
          assert "units" in set(dir(ds.tmin))
  
          # should defer to variable of same name
          ds.attrs["tmin"] = -999
          assert ds.attrs["tmin"] == -999
          assert_identical(ds.tmin, ds["tmin"])
  
      def test_variable(self):
          a = Dataset()
          d = np.random.random((10, 3))
          a["foo"] = (("time", "x"), d)
          assert "foo" in a.variables
          assert "foo" in a
          a["bar"] = (("time", "x"), d)
          # order of creation is preserved
          assert list(a.variables) == ["foo", "bar"]
          assert_array_equal(a["foo"].values, d)
          # try to add variable with dim (10,3) with data that's (3,10)
          with pytest.raises(ValueError):
              a["qux"] = (("time", "x"), d.T)
  
      def test_modify_inplace(self):
          a = Dataset()
          vec = np.random.random((10,))
          attributes = {"foo": "bar"}
          a["x"] = ("x", vec, attributes)
          assert "x" in a.coords
          assert isinstance(a.coords["x"].to_index(), pd.Index)
          assert_identical(a.coords["x"].variable, a.variables["x"])
          b = Dataset()
          b["x"] = ("x", vec, attributes)
          assert_identical(a["x"], b["x"])
          assert a.dims == b.dims
          # this should work
          a["x"] = ("x", vec[:5])
          a["z"] = ("x", np.arange(5))
          with pytest.raises(ValueError):
              # now it shouldn't, since there is a conflicting length
              a["x"] = ("x", vec[:4])
          arr = np.random.random((10, 1))
          scal = np.array(0)
          with pytest.raises(ValueError):
              a["y"] = ("y", arr)
          with pytest.raises(ValueError):
              a["y"] = ("y", scal)
          assert "y" not in a.dims
  
      def test_coords_properties(self):
          # use int64 for repr consistency on windows
          data = Dataset(
              {
                  "x": ("x", np.array([-1, -2], "int64")),
                  "y": ("y", np.array([0, 1, 2], "int64")),
                  "foo": (["x", "y"], np.random.randn(2, 3)),
              },
              {"a": ("x", np.array([4, 5], "int64")), "b": np.int64(-10)},
          )
  
          assert 4 == len(data.coords)
  
          assert ["x", "y", "a", "b"] == list(data.coords)
  
          assert_identical(data.coords["x"].variable, data["x"].variable)
          assert_identical(data.coords["y"].variable, data["y"].variable)
  
          assert "x" in data.coords
          assert "a" in data.coords
          assert 0 not in data.coords
          assert "foo" not in data.coords
  
          with pytest.raises(KeyError):
              data.coords["foo"]
          with pytest.raises(KeyError):
              data.coords[0]
  
          expected = dedent(
              """\
          Coordinates:
            * x        (x) int64 -1 -2
            * y        (y) int64 0 1 2
              a        (x) int64 4 5
              b        int64 -10"""
          )
          actual = repr(data.coords)
          assert expected == actual
  
          assert {"x": 2, "y": 3} == data.coords.dims
  
      def test_coords_modify(self):
          data = Dataset(
              {
                  "x": ("x", [-1, -2]),
                  "y": ("y", [0, 1, 2]),
                  "foo": (["x", "y"], np.random.randn(2, 3)),
              },
              {"a": ("x", [4, 5]), "b": -10},
          )
  
          actual = data.copy(deep=True)
          actual.coords["x"] = ("x", ["a", "b"])
          assert_array_equal(actual["x"], ["a", "b"])
  
          actual = data.copy(deep=True)
          actual.coords["z"] = ("z", ["a", "b"])
          assert_array_equal(actual["z"], ["a", "b"])
  
          actual = data.copy(deep=True)
          with raises_regex(ValueError, "conflicting sizes"):
              actual.coords["x"] = ("x", [-1])
          assert_identical(actual, data)  # should not be modified
  
          actual = data.copy()
          del actual.coords["b"]
          expected = data.reset_coords("b", drop=True)
          assert_identical(expected, actual)
  
          with pytest.raises(KeyError):
              del data.coords["not_found"]
  
          with pytest.raises(KeyError):
              del data.coords["foo"]
  
          actual = data.copy(deep=True)
          actual.coords.update({"c": 11})
          expected = data.merge({"c": 11}).set_coords("c")
          assert_identical(expected, actual)
  
      def test_update_index(self):
          actual = Dataset(coords={"x": [1, 2, 3]})
          actual["x"] = ["a", "b", "c"]
          assert actual.indexes["x"].equals(pd.Index(["a", "b", "c"]))
  
      def test_coords_setitem_with_new_dimension(self):
          actual = Dataset()
          actual.coords["foo"] = ("x", [1, 2, 3])
          expected = Dataset(coords={"foo": ("x", [1, 2, 3])})
          assert_identical(expected, actual)
  
      def test_coords_setitem_multiindex(self):
          data = create_test_multiindex()
          with raises_regex(ValueError, "conflicting MultiIndex"):
              data.coords["level_1"] = range(4)
  
      def test_coords_set(self):
          one_coord = Dataset({"x": ("x", [0]), "yy": ("x", [1]), "zzz": ("x", [2])})
          two_coords = Dataset({"zzz": ("x", [2])}, {"x": ("x", [0]), "yy": ("x", [1])})
          all_coords = Dataset(
              coords={"x": ("x", [0]), "yy": ("x", [1]), "zzz": ("x", [2])}
          )
  
          actual = one_coord.set_coords("x")
          assert_identical(one_coord, actual)
          actual = one_coord.set_coords(["x"])
          assert_identical(one_coord, actual)
  
          actual = one_coord.set_coords("yy")
          assert_identical(two_coords, actual)
  
          actual = one_coord.set_coords(["yy", "zzz"])
          assert_identical(all_coords, actual)
  
          actual = one_coord.reset_coords()
          assert_identical(one_coord, actual)
          actual = two_coords.reset_coords()
          assert_identical(one_coord, actual)
          actual = all_coords.reset_coords()
          assert_identical(one_coord, actual)
  
          actual = all_coords.reset_coords(["yy", "zzz"])
          assert_identical(one_coord, actual)
          actual = all_coords.reset_coords("zzz")
          assert_identical(two_coords, actual)
  
          with raises_regex(ValueError, "cannot remove index"):
              one_coord.reset_coords("x")
  
          actual = all_coords.reset_coords("zzz", drop=True)
          expected = all_coords.drop_vars("zzz")
          assert_identical(expected, actual)
          expected = two_coords.drop_vars("zzz")
          assert_identical(expected, actual)
  
      def test_coords_to_dataset(self):
          orig = Dataset({"foo": ("y", [-1, 0, 1])}, {"x": 10, "y": [2, 3, 4]})
          expected = Dataset(coords={"x": 10, "y": [2, 3, 4]})
          actual = orig.coords.to_dataset()
          assert_identical(expected, actual)
  
      def test_coords_merge(self):
          orig_coords = Dataset(coords={"a": ("x", [1, 2]), "x": [0, 1]}).coords
          other_coords = Dataset(coords={"b": ("x", ["a", "b"]), "x": [0, 1]}).coords
          expected = Dataset(
              coords={"a": ("x", [1, 2]), "b": ("x", ["a", "b"]), "x": [0, 1]}
          )
          actual = orig_coords.merge(other_coords)
          assert_identical(expected, actual)
          actual = other_coords.merge(orig_coords)
          assert_identical(expected, actual)
  
          other_coords = Dataset(coords={"x": ("x", ["a"])}).coords
          with pytest.raises(MergeError):
              orig_coords.merge(other_coords)
          other_coords = Dataset(coords={"x": ("x", ["a", "b"])}).coords
          with pytest.raises(MergeError):
              orig_coords.merge(other_coords)
          other_coords = Dataset(coords={"x": ("x", ["a", "b", "c"])}).coords
          with pytest.raises(MergeError):
              orig_coords.merge(other_coords)
  
          other_coords = Dataset(coords={"a": ("x", [8, 9])}).coords
          expected = Dataset(coords={"x": range(2)})
          actual = orig_coords.merge(other_coords)
          assert_identical(expected, actual)
          actual = other_coords.merge(orig_coords)
          assert_identical(expected, actual)
  
          other_coords = Dataset(coords={"x": np.nan}).coords
          actual = orig_coords.merge(other_coords)
          assert_identical(orig_coords.to_dataset(), actual)
          actual = other_coords.merge(orig_coords)
          assert_identical(orig_coords.to_dataset(), actual)
  
      def test_coords_merge_mismatched_shape(self):
          orig_coords = Dataset(coords={"a": ("x", [1, 1])}).coords
          other_coords = Dataset(coords={"a": 1}).coords
          expected = orig_coords.to_dataset()
          actual = orig_coords.merge(other_coords)
          assert_identical(expected, actual)
  
          other_coords = Dataset(coords={"a": ("y", [1])}).coords
          expected = Dataset(coords={"a": (["x", "y"], [[1], [1]])})
          actual = orig_coords.merge(other_coords)
          assert_identical(expected, actual)
  
          actual = other_coords.merge(orig_coords)
          assert_identical(expected.transpose(), actual)
  
          orig_coords = Dataset(coords={"a": ("x", [np.nan])}).coords
          other_coords = Dataset(coords={"a": np.nan}).coords
          expected = orig_coords.to_dataset()
          actual = orig_coords.merge(other_coords)
          assert_identical(expected, actual)
  
      def test_data_vars_properties(self):
          ds = Dataset()
          ds["foo"] = (("x",), [1.0])
          ds["bar"] = 2.0
  
          assert set(ds.data_vars) == {"foo", "bar"}
          assert "foo" in ds.data_vars
          assert "x" not in ds.data_vars
          assert_identical(ds["foo"], ds.data_vars["foo"])
  
          expected = dedent(
              """\
          Data variables:
              foo      (x) float64 1.0
              bar      float64 2.0"""
          )
          actual = repr(ds.data_vars)
          assert expected == actual
  
      def test_equals_and_identical(self):
          data = create_test_data(seed=42)
          assert data.equals(data)
          assert data.identical(data)
  
          data2 = create_test_data(seed=42)
          data2.attrs["foobar"] = "baz"
          assert data.equals(data2)
          assert not data.identical(data2)
  
          del data2["time"]
          assert not data.equals(data2)
  
          data = create_test_data(seed=42).rename({"var1": None})
          assert data.equals(data)
          assert data.identical(data)
  
          data2 = data.reset_coords()
          assert not data2.equals(data)
          assert not data2.identical(data)
  
      def test_equals_failures(self):
          data = create_test_data()
          assert not data.equals("foo")
          assert not data.identical(123)
          assert not data.broadcast_equals({1: 2})
  
      def test_broadcast_equals(self):
          data1 = Dataset(coords={"x": 0})
          data2 = Dataset(coords={"x": [0]})
          assert data1.broadcast_equals(data2)
          assert not data1.equals(data2)
          assert not data1.identical(data2)
  
      def test_attrs(self):
          data = create_test_data(seed=42)
          data.attrs = {"foobar": "baz"}
          assert data.attrs["foobar"], "baz"
          assert isinstance(data.attrs, dict)
  
      @requires_dask
      def test_chunk(self):
          data = create_test_data()
          for v in data.variables.values():
              assert isinstance(v.data, np.ndarray)
          assert data.chunks == {}
  
          reblocked = data.chunk()
          for k, v in reblocked.variables.items():
              if k in reblocked.dims:
                  assert isinstance(v.data, np.ndarray)
              else:
                  assert isinstance(v.data, da.Array)
  
          expected_chunks = {"dim1": (8,), "dim2": (9,), "dim3": (10,)}
          assert reblocked.chunks == expected_chunks
  
          def get_dask_names(ds):
              return {k: v.data.name for k, v in ds.items()}
  
          orig_dask_names = get_dask_names(reblocked)
  
          reblocked = data.chunk({"time": 5, "dim1": 5, "dim2": 5, "dim3": 5})
          # time is not a dim in any of the data_vars, so it
          # doesn't get chunked
          expected_chunks = {"dim1": (5, 3), "dim2": (5, 4), "dim3": (5, 5)}
          assert reblocked.chunks == expected_chunks
  
          # make sure dask names change when rechunking by different amounts
          # regression test for GH3350
          new_dask_names = get_dask_names(reblocked)
          for k, v in new_dask_names.items():
              assert v != orig_dask_names[k]
  
          reblocked = data.chunk(expected_chunks)
          assert reblocked.chunks == expected_chunks
  
          # reblock on already blocked data
          orig_dask_names = get_dask_names(reblocked)
          reblocked = reblocked.chunk(expected_chunks)
          new_dask_names = get_dask_names(reblocked)
          assert reblocked.chunks == expected_chunks
          assert_identical(reblocked, data)
          # recuhnking with same chunk sizes should not change names
          for k, v in new_dask_names.items():
              assert v == orig_dask_names[k]
  
          with raises_regex(ValueError, "some chunks"):
              data.chunk({"foo": 10})
  
      @requires_dask
      def test_dask_is_lazy(self):
          store = InaccessibleVariableDataStore()
          create_test_data().dump_to_store(store)
          ds = open_dataset(store).chunk()
  
          with pytest.raises(UnexpectedDataAccess):
              ds.load()
          with pytest.raises(UnexpectedDataAccess):
              ds["var1"].values
  
          # these should not raise UnexpectedDataAccess:
          ds.var1.data
          ds.isel(time=10)
          ds.isel(time=slice(10), dim1=[0]).isel(dim1=0, dim2=-1)
          ds.transpose()
          ds.mean()
          ds.fillna(0)
          ds.rename({"dim1": "foobar"})
          ds.set_coords("var1")
          ds.drop_vars("var1")
  
      def test_isel(self):
          data = create_test_data()
          slicers = {"dim1": slice(None, None, 2), "dim2": slice(0, 2)}
          ret = data.isel(**slicers)
  
          # Verify that only the specified dimension was altered
          assert list(data.dims) == list(ret.dims)
          for d in data.dims:
              if d in slicers:
                  assert ret.dims[d] == np.arange(data.dims[d])[slicers[d]].size
              else:
                  assert data.dims[d] == ret.dims[d]
          # Verify that the data is what we expect
          for v in data.variables:
              assert data[v].dims == ret[v].dims
              assert data[v].attrs == ret[v].attrs
              slice_list = [slice(None)] * data[v].values.ndim
              for d, s in slicers.items():
                  if d in data[v].dims:
                      inds = np.nonzero(np.array(data[v].dims) == d)[0]
                      for ind in inds:
                          slice_list[ind] = s
              expected = data[v].values[tuple(slice_list)]
              actual = ret[v].values
              np.testing.assert_array_equal(expected, actual)
  
          with pytest.raises(ValueError):
              data.isel(not_a_dim=slice(0, 2))
  
          ret = data.isel(dim1=0)
          assert {"time": 20, "dim2": 9, "dim3": 10} == ret.dims
          assert set(data.data_vars) == set(ret.data_vars)
          assert set(data.coords) == set(ret.coords)
          assert set(data.indexes) == set(ret.indexes)
  
          ret = data.isel(time=slice(2), dim1=0, dim2=slice(5))
          assert {"time": 2, "dim2": 5, "dim3": 10} == ret.dims
          assert set(data.data_vars) == set(ret.data_vars)
          assert set(data.coords) == set(ret.coords)
          assert set(data.indexes) == set(ret.indexes)
  
          ret = data.isel(time=0, dim1=0, dim2=slice(5))
          assert {"dim2": 5, "dim3": 10} == ret.dims
          assert set(data.data_vars) == set(ret.data_vars)
          assert set(data.coords) == set(ret.coords)
          assert set(data.indexes) == set(list(ret.indexes) + ["time"])
  
      def test_isel_fancy(self):
          # isel with fancy indexing.
          data = create_test_data()
  
          pdim1 = [1, 2, 3]
          pdim2 = [4, 5, 1]
          pdim3 = [1, 2, 3]
          actual = data.isel(
              dim1=(("test_coord",), pdim1),
              dim2=(("test_coord",), pdim2),
              dim3=(("test_coord",), pdim3),
          )
          assert "test_coord" in actual.dims
          assert actual.coords["test_coord"].shape == (len(pdim1),)
  
          # Should work with DataArray
          actual = data.isel(
              dim1=DataArray(pdim1, dims="test_coord"),
              dim2=(("test_coord",), pdim2),
              dim3=(("test_coord",), pdim3),
          )
          assert "test_coord" in actual.dims
          assert actual.coords["test_coord"].shape == (len(pdim1),)
          expected = data.isel(
              dim1=(("test_coord",), pdim1),
              dim2=(("test_coord",), pdim2),
              dim3=(("test_coord",), pdim3),
          )
          assert_identical(actual, expected)
  
          # DataArray with coordinate
          idx1 = DataArray(pdim1, dims=["a"], coords={"a": np.random.randn(3)})
          idx2 = DataArray(pdim2, dims=["b"], coords={"b": np.random.randn(3)})
          idx3 = DataArray(pdim3, dims=["c"], coords={"c": np.random.randn(3)})
          # Should work with DataArray
          actual = data.isel(dim1=idx1, dim2=idx2, dim3=idx3)
          assert "a" in actual.dims
          assert "b" in actual.dims
          assert "c" in actual.dims
          assert "time" in actual.coords
          assert "dim2" in actual.coords
          assert "dim3" in actual.coords
          expected = data.isel(
              dim1=(("a",), pdim1), dim2=(("b",), pdim2), dim3=(("c",), pdim3)
          )
          expected = expected.assign_coords(a=idx1["a"], b=idx2["b"], c=idx3["c"])
          assert_identical(actual, expected)
  
          idx1 = DataArray(pdim1, dims=["a"], coords={"a": np.random.randn(3)})
          idx2 = DataArray(pdim2, dims=["a"])
          idx3 = DataArray(pdim3, dims=["a"])
          # Should work with DataArray
          actual = data.isel(dim1=idx1, dim2=idx2, dim3=idx3)
          assert "a" in actual.dims
          assert "time" in actual.coords
          assert "dim2" in actual.coords
          assert "dim3" in actual.coords
          expected = data.isel(
              dim1=(("a",), pdim1), dim2=(("a",), pdim2), dim3=(("a",), pdim3)
          )
          expected = expected.assign_coords(a=idx1["a"])
          assert_identical(actual, expected)
  
          actual = data.isel(dim1=(("points",), pdim1), dim2=(("points",), pdim2))
          assert "points" in actual.dims
          assert "dim3" in actual.dims
          assert "dim3" not in actual.data_vars
          np.testing.assert_array_equal(data["dim2"][pdim2], actual["dim2"])
  
          # test that the order of the indexers doesn't matter
          assert_identical(
              data.isel(dim1=(("points",), pdim1), dim2=(("points",), pdim2)),
              data.isel(dim2=(("points",), pdim2), dim1=(("points",), pdim1)),
          )
          # make sure we're raising errors in the right places
          with raises_regex(IndexError, "Dimensions of indexers mismatch"):
              data.isel(dim1=(("points",), [1, 2]), dim2=(("points",), [1, 2, 3]))
          with raises_regex(TypeError, "cannot use a Dataset"):
              data.isel(dim1=Dataset({"points": [1, 2]}))
  
          # test to be sure we keep around variables that were not indexed
          ds = Dataset({"x": [1, 2, 3, 4], "y": 0})
          actual = ds.isel(x=(("points",), [0, 1, 2]))
          assert_identical(ds["y"], actual["y"])
  
          # tests using index or DataArray as indexers
          stations = Dataset()
          stations["station"] = (("station",), ["A", "B", "C"])
          stations["dim1s"] = (("station",), [1, 2, 3])
          stations["dim2s"] = (("station",), [4, 5, 1])
  
          actual = data.isel(dim1=stations["dim1s"], dim2=stations["dim2s"])
          assert "station" in actual.coords
          assert "station" in actual.dims
          assert_identical(actual["station"].drop_vars(["dim2"]), stations["station"])
  
          with raises_regex(ValueError, "conflicting values for "):
              data.isel(
                  dim1=DataArray(
                      [0, 1, 2], dims="station", coords={"station": [0, 1, 2]}
                  ),
                  dim2=DataArray(
                      [0, 1, 2], dims="station", coords={"station": [0, 1, 3]}
                  ),
              )
  
          # multi-dimensional selection
          stations = Dataset()
          stations["a"] = (("a",), ["A", "B", "C"])
          stations["b"] = (("b",), [0, 1])
          stations["dim1s"] = (("a", "b"), [[1, 2], [2, 3], [3, 4]])
          stations["dim2s"] = (("a",), [4, 5, 1])
          actual = data.isel(dim1=stations["dim1s"], dim2=stations["dim2s"])
          assert "a" in actual.coords
          assert "a" in actual.dims
          assert "b" in actual.coords
          assert "b" in actual.dims
          assert "dim2" in actual.coords
          assert "a" in actual["dim2"].dims
  
          assert_identical(actual["a"].drop_vars(["dim2"]), stations["a"])
          assert_identical(actual["b"], stations["b"])
          expected_var1 = data["var1"].variable[
              stations["dim1s"].variable, stations["dim2s"].variable
          ]
          expected_var2 = data["var2"].variable[
              stations["dim1s"].variable, stations["dim2s"].variable
          ]
          expected_var3 = data["var3"].variable[slice(None), stations["dim1s"].variable]
          assert_equal(actual["a"].drop_vars("dim2"), stations["a"])
          assert_array_equal(actual["var1"], expected_var1)
          assert_array_equal(actual["var2"], expected_var2)
          assert_array_equal(actual["var3"], expected_var3)
  
      def test_isel_dataarray(self):
          """ Test for indexing by DataArray """
          data = create_test_data()
          # indexing with DataArray with same-name coordinates.
          indexing_da = DataArray(
              np.arange(1, 4), dims=["dim1"], coords={"dim1": np.random.randn(3)}
          )
          actual = data.isel(dim1=indexing_da)
          assert_identical(indexing_da["dim1"], actual["dim1"])
          assert_identical(data["dim2"], actual["dim2"])
  
          # Conflict in the dimension coordinate
          indexing_da = DataArray(
              np.arange(1, 4), dims=["dim2"], coords={"dim2": np.random.randn(3)}
          )
          with raises_regex(IndexError, "dimension coordinate 'dim2'"):
              actual = data.isel(dim2=indexing_da)
          # Also the case for DataArray
          with raises_regex(IndexError, "dimension coordinate 'dim2'"):
              actual = data["var2"].isel(dim2=indexing_da)
          with raises_regex(IndexError, "dimension coordinate 'dim2'"):
              data["dim2"].isel(dim2=indexing_da)
  
          # same name coordinate which does not conflict
          indexing_da = DataArray(
              np.arange(1, 4), dims=["dim2"], coords={"dim2": data["dim2"].values[1:4]}
          )
          actual = data.isel(dim2=indexing_da)
          assert_identical(actual["dim2"], indexing_da["dim2"])
  
          # Silently drop conflicted (non-dimensional) coordinate of indexer
          indexing_da = DataArray(
              np.arange(1, 4),
              dims=["dim2"],
              coords={
                  "dim2": data["dim2"].values[1:4],
                  "numbers": ("dim2", np.arange(2, 5)),
              },
          )
          actual = data.isel(dim2=indexing_da)
          assert_identical(actual["numbers"], data["numbers"])
  
          # boolean data array with coordinate with the same name
          indexing_da = DataArray(
              np.arange(1, 10), dims=["dim2"], coords={"dim2": data["dim2"].values}
          )
          indexing_da = indexing_da < 3
          actual = data.isel(dim2=indexing_da)
          assert_identical(actual["dim2"], data["dim2"][:2])
  
          # boolean data array with non-dimensioncoordinate
          indexing_da = DataArray(
              np.arange(1, 10),
              dims=["dim2"],
              coords={
                  "dim2": data["dim2"].values,
                  "non_dim": (("dim2",), np.random.randn(9)),
                  "non_dim2": 0,
              },
          )
          indexing_da = indexing_da < 3
          actual = data.isel(dim2=indexing_da)
          assert_identical(
              actual["dim2"].drop_vars("non_dim").drop_vars("non_dim2"), data["dim2"][:2]
          )
          assert_identical(actual["non_dim"], indexing_da["non_dim"][:2])
          assert_identical(actual["non_dim2"], indexing_da["non_dim2"])
  
          # non-dimension coordinate will be also attached
          indexing_da = DataArray(
              np.arange(1, 4),
              dims=["dim2"],
              coords={"non_dim": (("dim2",), np.random.randn(3))},
          )
          actual = data.isel(dim2=indexing_da)
          assert "non_dim" in actual
          assert "non_dim" in actual.coords
  
          # Index by a scalar DataArray
          indexing_da = DataArray(3, dims=[], coords={"station": 2})
          actual = data.isel(dim2=indexing_da)
          assert "station" in actual
          actual = data.isel(dim2=indexing_da["station"])
          assert "station" in actual
  
          # indexer generated from coordinates
          indexing_ds = Dataset({}, coords={"dim2": [0, 1, 2]})
          with raises_regex(IndexError, "dimension coordinate 'dim2'"):
              actual = data.isel(dim2=indexing_ds["dim2"])
  
      def test_sel(self):
          data = create_test_data()
          int_slicers = {"dim1": slice(None, None, 2), "dim2": slice(2), "dim3": slice(3)}
          loc_slicers = {
              "dim1": slice(None, None, 2),
              "dim2": slice(0, 0.5),
              "dim3": slice("a", "c"),
          }
          assert_equal(data.isel(**int_slicers), data.sel(**loc_slicers))
          data["time"] = ("time", pd.date_range("2000-01-01", periods=20))
          assert_equal(data.isel(time=0), data.sel(time="2000-01-01"))
          assert_equal(
              data.isel(time=slice(10)), data.sel(time=slice("2000-01-01", "2000-01-10"))
          )
          assert_equal(data, data.sel(time=slice("1999", "2005")))
          times = pd.date_range("2000-01-01", periods=3)
          assert_equal(data.isel(time=slice(3)), data.sel(time=times))
          assert_equal(
              data.isel(time=slice(3)), data.sel(time=(data["time.dayofyear"] <= 3))
          )
  
          td = pd.to_timedelta(np.arange(3), unit="days")
          data = Dataset({"x": ("td", np.arange(3)), "td": td})
          assert_equal(data, data.sel(td=td))
          assert_equal(data, data.sel(td=slice("3 days")))
          assert_equal(data.isel(td=0), data.sel(td=pd.Timedelta("0 days")))
          assert_equal(data.isel(td=0), data.sel(td=pd.Timedelta("0h")))
          assert_equal(data.isel(td=slice(1, 3)), data.sel(td=slice("1 days", "2 days")))
  
      def test_sel_dataarray(self):
          data = create_test_data()
  
          ind = DataArray([0.0, 0.5, 1.0], dims=["dim2"])
          actual = data.sel(dim2=ind)
          assert_equal(actual, data.isel(dim2=[0, 1, 2]))
  
          # with different dimension
          ind = DataArray([0.0, 0.5, 1.0], dims=["new_dim"])
          actual = data.sel(dim2=ind)
          expected = data.isel(dim2=Variable("new_dim", [0, 1, 2]))
          assert "new_dim" in actual.dims
          assert_equal(actual, expected)
  
          # Multi-dimensional
          ind = DataArray([[0.0], [0.5], [1.0]], dims=["new_dim", "new_dim2"])
          actual = data.sel(dim2=ind)
          expected = data.isel(dim2=Variable(("new_dim", "new_dim2"), [[0], [1], [2]]))
          assert "new_dim" in actual.dims
          assert "new_dim2" in actual.dims
          assert_equal(actual, expected)
  
          # with coordinate
          ind = DataArray(
              [0.0, 0.5, 1.0], dims=["new_dim"], coords={"new_dim": ["a", "b", "c"]}
          )
          actual = data.sel(dim2=ind)
          expected = data.isel(dim2=[0, 1, 2]).rename({"dim2": "new_dim"})
          assert "new_dim" in actual.dims
          assert "new_dim" in actual.coords
          assert_equal(
              actual.drop_vars("new_dim").drop_vars("dim2"), expected.drop_vars("new_dim")
          )
          assert_equal(actual["new_dim"].drop_vars("dim2"), ind["new_dim"])
  
          # with conflicted coordinate (silently ignored)
          ind = DataArray(
              [0.0, 0.5, 1.0], dims=["dim2"], coords={"dim2": ["a", "b", "c"]}
          )
          actual = data.sel(dim2=ind)
          expected = data.isel(dim2=[0, 1, 2])
          assert_equal(actual, expected)
  
          # with conflicted coordinate (silently ignored)
          ind = DataArray(
              [0.0, 0.5, 1.0],
              dims=["new_dim"],
              coords={"new_dim": ["a", "b", "c"], "dim2": 3},
          )
          actual = data.sel(dim2=ind)
          assert_equal(
              actual["new_dim"].drop_vars("dim2"), ind["new_dim"].drop_vars("dim2")
          )
          expected = data.isel(dim2=[0, 1, 2])
          expected["dim2"] = (("new_dim"), expected["dim2"].values)
          assert_equal(actual["dim2"].drop_vars("new_dim"), expected["dim2"])
          assert actual["var1"].dims == ("dim1", "new_dim")
  
          # with non-dimensional coordinate
          ind = DataArray(
              [0.0, 0.5, 1.0],
              dims=["dim2"],
              coords={
                  "dim2": ["a", "b", "c"],
                  "numbers": ("dim2", [0, 1, 2]),
                  "new_dim": ("dim2", [1.1, 1.2, 1.3]),
              },
          )
          actual = data.sel(dim2=ind)
          expected = data.isel(dim2=[0, 1, 2])
          assert_equal(actual.drop_vars("new_dim"), expected)
          assert np.allclose(actual["new_dim"].values, ind["new_dim"].values)
  
      def test_sel_dataarray_mindex(self):
          midx = pd.MultiIndex.from_product([list("abc"), [0, 1]], names=("one", "two"))
          mds = xr.Dataset(
              {"var": (("x", "y"), np.random.rand(6, 3))},
              coords={"x": midx, "y": range(3)},
          )
  
          actual_isel = mds.isel(x=xr.DataArray(np.arange(3), dims="x"))
          actual_sel = mds.sel(x=DataArray(mds.indexes["x"][:3], dims="x"))
          assert actual_isel["x"].dims == ("x",)
          assert actual_sel["x"].dims == ("x",)
          assert_identical(actual_isel, actual_sel)
  
          actual_isel = mds.isel(x=xr.DataArray(np.arange(3), dims="z"))
          actual_sel = mds.sel(x=Variable("z", mds.indexes["x"][:3]))
          assert actual_isel["x"].dims == ("z",)
          assert actual_sel["x"].dims == ("z",)
          assert_identical(actual_isel, actual_sel)
  
          # with coordinate
          actual_isel = mds.isel(
              x=xr.DataArray(np.arange(3), dims="z", coords={"z": [0, 1, 2]})
          )
          actual_sel = mds.sel(
              x=xr.DataArray(mds.indexes["x"][:3], dims="z", coords={"z": [0, 1, 2]})
          )
          assert actual_isel["x"].dims == ("z",)
          assert actual_sel["x"].dims == ("z",)
          assert_identical(actual_isel, actual_sel)
  
          # Vectorized indexing with level-variables raises an error
          with raises_regex(ValueError, "Vectorized selection is "):
              mds.sel(one=["a", "b"])
  
          with raises_regex(
              ValueError,
              "Vectorized selection is " "not available along MultiIndex variable:" " x",
          ):
              mds.sel(
                  x=xr.DataArray(
                      [np.array(midx[:2]), np.array(midx[-2:])], dims=["a", "b"]
                  )
              )
  
      def test_sel_categorical(self):
          ind = pd.Series(["foo", "bar"], dtype="category")
          df = pd.DataFrame({"ind": ind, "values": [1, 2]})
          ds = df.set_index("ind").to_xarray()
          actual = ds.sel(ind="bar")
          expected = ds.isel(ind=1)
          assert_identical(expected, actual)
  
      def test_sel_categorical_error(self):
          ind = pd.Series(["foo", "bar"], dtype="category")
          df = pd.DataFrame({"ind": ind, "values": [1, 2]})
          ds = df.set_index("ind").to_xarray()
          with pytest.raises(ValueError):
              ds.sel(ind="bar", method="nearest")
          with pytest.raises(ValueError):
              ds.sel(ind="bar", tolerance="nearest")
  
      def test_categorical_index(self):
          cat = pd.CategoricalIndex(
              ["foo", "bar", "foo"],
              categories=["foo", "bar", "baz", "qux", "quux", "corge"],
          )
          ds = xr.Dataset(
              {"var": ("cat", np.arange(3))},
              coords={"cat": ("cat", cat), "c": ("cat", [0, 1, 1])},
          )
          # test slice
          actual = ds.sel(cat="foo")
          expected = ds.isel(cat=[0, 2])
          assert_identical(expected, actual)
          # make sure the conversion to the array works
          actual = ds.sel(cat="foo")["cat"].values
          assert (actual == np.array(["foo", "foo"])).all()
  
          ds = ds.set_index(index=["cat", "c"])
          actual = ds.unstack("index")
          assert actual["var"].shape == (2, 2)
  
      def test_categorical_reindex(self):
          cat = pd.CategoricalIndex(
              ["foo", "bar", "baz"],
              categories=["foo", "bar", "baz", "qux", "quux", "corge"],
          )
          ds = xr.Dataset(
              {"var": ("cat", np.arange(3))},
              coords={"cat": ("cat", cat), "c": ("cat", [0, 1, 2])},
          )
          actual = ds.reindex(cat=["foo"])["cat"].values
          assert (actual == np.array(["foo"])).all()
  
      def test_sel_drop(self):
          data = Dataset({"foo": ("x", [1, 2, 3])}, {"x": [0, 1, 2]})
          expected = Dataset({"foo": 1})
          selected = data.sel(x=0, drop=True)
          assert_identical(expected, selected)
  
          expected = Dataset({"foo": 1}, {"x": 0})
          selected = data.sel(x=0, drop=False)
          assert_identical(expected, selected)
  
          data = Dataset({"foo": ("x", [1, 2, 3])})
          expected = Dataset({"foo": 1})
          selected = data.sel(x=0, drop=True)
          assert_identical(expected, selected)
  
      def test_isel_drop(self):
          data = Dataset({"foo": ("x", [1, 2, 3])}, {"x": [0, 1, 2]})
          expected = Dataset({"foo": 1})
          selected = data.isel(x=0, drop=True)
          assert_identical(expected, selected)
  
          expected = Dataset({"foo": 1}, {"x": 0})
          selected = data.isel(x=0, drop=False)
          assert_identical(expected, selected)
  
      def test_head(self):
          data = create_test_data()
  
          expected = data.isel(time=slice(5), dim2=slice(6))
          actual = data.head(time=5, dim2=6)
          assert_equal(expected, actual)
  
          expected = data.isel(time=slice(0))
          actual = data.head(time=0)
          assert_equal(expected, actual)
  
          expected = data.isel({dim: slice(6) for dim in data.dims})
          actual = data.head(6)
          assert_equal(expected, actual)
  
          expected = data.isel({dim: slice(5) for dim in data.dims})
          actual = data.head()
          assert_equal(expected, actual)
  
          with raises_regex(TypeError, "either dict-like or a single int"):
              data.head([3])
          with raises_regex(TypeError, "expected integer type"):
              data.head(dim2=3.1)
          with raises_regex(ValueError, "expected positive int"):
              data.head(time=-3)
  
      def test_tail(self):
          data = create_test_data()
  
          expected = data.isel(time=slice(-5, None), dim2=slice(-6, None))
          actual = data.tail(time=5, dim2=6)
          assert_equal(expected, actual)
  
          expected = data.isel(dim1=slice(0))
          actual = data.tail(dim1=0)
          assert_equal(expected, actual)
  
          expected = data.isel({dim: slice(-6, None) for dim in data.dims})
          actual = data.tail(6)
          assert_equal(expected, actual)
  
          expected = data.isel({dim: slice(-5, None) for dim in data.dims})
          actual = data.tail()
          assert_equal(expected, actual)
  
          with raises_regex(TypeError, "either dict-like or a single int"):
              data.tail([3])
          with raises_regex(TypeError, "expected integer type"):
              data.tail(dim2=3.1)
          with raises_regex(ValueError, "expected positive int"):
              data.tail(time=-3)
  
      def test_thin(self):
          data = create_test_data()
  
          expected = data.isel(time=slice(None, None, 5), dim2=slice(None, None, 6))
          actual = data.thin(time=5, dim2=6)
          assert_equal(expected, actual)
  
          expected = data.isel({dim: slice(None, None, 6) for dim in data.dims})
          actual = data.thin(6)
          assert_equal(expected, actual)
  
          with raises_regex(TypeError, "either dict-like or a single int"):
              data.thin([3])
          with raises_regex(TypeError, "expected integer type"):
              data.thin(dim2=3.1)
          with raises_regex(ValueError, "cannot be zero"):
              data.thin(time=0)
          with raises_regex(ValueError, "expected positive int"):
              data.thin(time=-3)
  
      @pytest.mark.filterwarnings("ignore::DeprecationWarning")
      def test_sel_fancy(self):
          data = create_test_data()
  
          # add in a range() index
          data["dim1"] = data.dim1
  
          pdim1 = [1, 2, 3]
          pdim2 = [4, 5, 1]
          pdim3 = [1, 2, 3]
          expected = data.isel(
              dim1=Variable(("test_coord",), pdim1),
              dim2=Variable(("test_coord",), pdim2),
              dim3=Variable(("test_coord"), pdim3),
          )
          actual = data.sel(
              dim1=Variable(("test_coord",), data.dim1[pdim1]),
              dim2=Variable(("test_coord",), data.dim2[pdim2]),
              dim3=Variable(("test_coord",), data.dim3[pdim3]),
          )
          assert_identical(expected, actual)
  
          # DataArray Indexer
          idx_t = DataArray(
              data["time"][[3, 2, 1]].values, dims=["a"], coords={"a": ["a", "b", "c"]}
          )
          idx_2 = DataArray(
              data["dim2"][[3, 2, 1]].values, dims=["a"], coords={"a": ["a", "b", "c"]}
          )
          idx_3 = DataArray(
              data["dim3"][[3, 2, 1]].values, dims=["a"], coords={"a": ["a", "b", "c"]}
          )
          actual = data.sel(time=idx_t, dim2=idx_2, dim3=idx_3)
          expected = data.isel(
              time=Variable(("a",), [3, 2, 1]),
              dim2=Variable(("a",), [3, 2, 1]),
              dim3=Variable(("a",), [3, 2, 1]),
          )
          expected = expected.assign_coords(a=idx_t["a"])
          assert_identical(expected, actual)
  
          idx_t = DataArray(
              data["time"][[3, 2, 1]].values, dims=["a"], coords={"a": ["a", "b", "c"]}
          )
          idx_2 = DataArray(
              data["dim2"][[2, 1, 3]].values, dims=["b"], coords={"b": [0, 1, 2]}
          )
          idx_3 = DataArray(
              data["dim3"][[1, 2, 1]].values, dims=["c"], coords={"c": [0.0, 1.1, 2.2]}
          )
          actual = data.sel(time=idx_t, dim2=idx_2, dim3=idx_3)
          expected = data.isel(
              time=Variable(("a",), [3, 2, 1]),
              dim2=Variable(("b",), [2, 1, 3]),
              dim3=Variable(("c",), [1, 2, 1]),
          )
          expected = expected.assign_coords(a=idx_t["a"], b=idx_2["b"], c=idx_3["c"])
          assert_identical(expected, actual)
  
          # test from sel_points
          data = Dataset({"foo": (("x", "y"), np.arange(9).reshape(3, 3))})
          data.coords.update({"x": [0, 1, 2], "y": [0, 1, 2]})
  
          expected = Dataset(
              {"foo": ("points", [0, 4, 8])},
              coords={
                  "x": Variable(("points",), [0, 1, 2]),
                  "y": Variable(("points",), [0, 1, 2]),
              },
          )
          actual = data.sel(
              x=Variable(("points",), [0, 1, 2]), y=Variable(("points",), [0, 1, 2])
          )
          assert_identical(expected, actual)
  
          expected.coords.update({"x": ("points", [0, 1, 2]), "y": ("points", [0, 1, 2])})
          actual = data.sel(
              x=Variable(("points",), [0.1, 1.1, 2.5]),
              y=Variable(("points",), [0, 1.2, 2.0]),
              method="pad",
          )
          assert_identical(expected, actual)
  
          idx_x = DataArray([0, 1, 2], dims=["a"], coords={"a": ["a", "b", "c"]})
          idx_y = DataArray([0, 2, 1], dims=["b"], coords={"b": [0, 3, 6]})
          expected_ary = data["foo"][[0, 1, 2], [0, 2, 1]]
          actual = data.sel(x=idx_x, y=idx_y)
          assert_array_equal(expected_ary, actual["foo"])
          assert_identical(actual["a"].drop_vars("x"), idx_x["a"])
          assert_identical(actual["b"].drop_vars("y"), idx_y["b"])
  
          with pytest.raises(KeyError):
              data.sel(x=[2.5], y=[2.0], method="pad", tolerance=1e-3)
  
      def test_sel_method(self):
          data = create_test_data()
  
          expected = data.sel(dim2=1)
          actual = data.sel(dim2=0.95, method="nearest")
          assert_identical(expected, actual)
  
          actual = data.sel(dim2=0.95, method="nearest", tolerance=1)
          assert_identical(expected, actual)
  
          with pytest.raises(KeyError):
              actual = data.sel(dim2=np.pi, method="nearest", tolerance=0)
  
          expected = data.sel(dim2=[1.5])
          actual = data.sel(dim2=[1.45], method="backfill")
          assert_identical(expected, actual)
  
          with raises_regex(NotImplementedError, "slice objects"):
              data.sel(dim2=slice(1, 3), method="ffill")
  
          with raises_regex(TypeError, "``method``"):
              # this should not pass silently
              data.sel(method=data)
  
          # cannot pass method if there is no associated coordinate
          with raises_regex(ValueError, "cannot supply"):
              data.sel(dim1=0, method="nearest")
  
      def test_loc(self):
          data = create_test_data()
          expected = data.sel(dim3="a")
          actual = data.loc[dict(dim3="a")]
          assert_identical(expected, actual)
          with raises_regex(TypeError, "can only lookup dict"):
              data.loc["a"]
          with pytest.raises(TypeError):
              data.loc[dict(dim3="a")] = 0
  
      def test_selection_multiindex(self):
          mindex = pd.MultiIndex.from_product(
              [["a", "b"], [1, 2], [-1, -2]], names=("one", "two", "three")
          )
          mdata = Dataset(data_vars={"var": ("x", range(8))}, coords={"x": mindex})
  
          def test_sel(lab_indexer, pos_indexer, replaced_idx=False, renamed_dim=None):
              ds = mdata.sel(x=lab_indexer)
              expected_ds = mdata.isel(x=pos_indexer)
              if not replaced_idx:
                  assert_identical(ds, expected_ds)
              else:
                  if renamed_dim:
                      assert ds["var"].dims[0] == renamed_dim
                      ds = ds.rename({renamed_dim: "x"})
                  assert_identical(ds["var"].variable, expected_ds["var"].variable)
                  assert not ds["x"].equals(expected_ds["x"])
  
          test_sel(("a", 1, -1), 0)
          test_sel(("b", 2, -2), -1)
          test_sel(("a", 1), [0, 1], replaced_idx=True, renamed_dim="three")
          test_sel(("a",), range(4), replaced_idx=True)
          test_sel("a", range(4), replaced_idx=True)
          test_sel([("a", 1, -1), ("b", 2, -2)], [0, 7])
          test_sel(slice("a", "b"), range(8))
          test_sel(slice(("a", 1), ("b", 1)), range(6))
          test_sel({"one": "a", "two": 1, "three": -1}, 0)
          test_sel({"one": "a", "two": 1}, [0, 1], replaced_idx=True, renamed_dim="three")
          test_sel({"one": "a"}, range(4), replaced_idx=True)
  
          assert_identical(mdata.loc[{"x": {"one": "a"}}], mdata.sel(x={"one": "a"}))
          assert_identical(mdata.loc[{"x": "a"}], mdata.sel(x="a"))
          assert_identical(mdata.loc[{"x": ("a", 1)}], mdata.sel(x=("a", 1)))
          assert_identical(mdata.loc[{"x": ("a", 1, -1)}], mdata.sel(x=("a", 1, -1)))
  
          assert_identical(mdata.sel(x={"one": "a", "two": 1}), mdata.sel(one="a", two=1))
  
      def test_broadcast_like(self):
          original1 = DataArray(
              np.random.randn(5), [("x", range(5))], name="a"
          ).to_dataset()
  
          original2 = DataArray(np.random.randn(6), [("y", range(6))], name="b")
  
          expected1, expected2 = broadcast(original1, original2)
  
          assert_identical(
              original1.broadcast_like(original2), expected1.transpose("y", "x")
          )
  
          assert_identical(original2.broadcast_like(original1), expected2)
  
      def test_reindex_like(self):
          data = create_test_data()
          data["letters"] = ("dim3", 10 * ["a"])
  
          expected = data.isel(dim1=slice(10), time=slice(13))
          actual = data.reindex_like(expected)
          assert_identical(actual, expected)
  
          expected = data.copy(deep=True)
          expected["dim3"] = ("dim3", list("cdefghijkl"))
          expected["var3"][:-2] = expected["var3"][2:].values
          expected["var3"][-2:] = np.nan
          expected["letters"] = expected["letters"].astype(object)
          expected["letters"][-2:] = np.nan
          expected["numbers"] = expected["numbers"].astype(float)
          expected["numbers"][:-2] = expected["numbers"][2:].values
          expected["numbers"][-2:] = np.nan
          actual = data.reindex_like(expected)
          assert_identical(actual, expected)
  
      def test_reindex(self):
          data = create_test_data()
          assert_identical(data, data.reindex())
  
          expected = data.assign_coords(dim1=data["dim1"])
          actual = data.reindex(dim1=data["dim1"])
          assert_identical(actual, expected)
  
          actual = data.reindex(dim1=data["dim1"].values)
          assert_identical(actual, expected)
  
          actual = data.reindex(dim1=data["dim1"].to_index())
          assert_identical(actual, expected)
  
          with raises_regex(ValueError, "cannot reindex or align along dimension"):
              data.reindex(dim1=data["dim1"][:5])
  
          expected = data.isel(dim2=slice(5))
          actual = data.reindex(dim2=data["dim2"][:5])
          assert_identical(actual, expected)
  
          # test dict-like argument
          actual = data.reindex({"dim2": data["dim2"]})
          expected = data
          assert_identical(actual, expected)
          with raises_regex(ValueError, "cannot specify both"):
              data.reindex({"x": 0}, x=0)
          with raises_regex(ValueError, "dictionary"):
              data.reindex("foo")
  
          # invalid dimension
          with raises_regex(ValueError, "invalid reindex dim"):
              data.reindex(invalid=0)
  
          # out of order
          expected = data.sel(dim2=data["dim2"][:5:-1])
          actual = data.reindex(dim2=data["dim2"][:5:-1])
          assert_identical(actual, expected)
  
          # regression test for #279
          expected = Dataset({"x": ("time", np.random.randn(5))}, {"time": range(5)})
          time2 = DataArray(np.arange(5), dims="time2")
          with pytest.raises(ValueError):
              actual = expected.reindex(time=time2)
  
          # another regression test
          ds = Dataset(
              {"foo": (["x", "y"], np.zeros((3, 4)))}, {"x": range(3), "y": range(4)}
          )
          expected = Dataset(
              {"foo": (["x", "y"], np.zeros((3, 2)))}, {"x": [0, 1, 3], "y": [0, 1]}
          )
          expected["foo"][-1] = np.nan
          actual = ds.reindex(x=[0, 1, 3], y=[0, 1])
          assert_identical(expected, actual)
  
      def test_reindex_warning(self):
          data = create_test_data()
  
          with pytest.raises(ValueError):
              # DataArray with different dimension raises Future warning
              ind = xr.DataArray([0.0, 1.0], dims=["new_dim"], name="ind")
              data.reindex(dim2=ind)
  
          # Should not warn
          ind = xr.DataArray([0.0, 1.0], dims=["dim2"], name="ind")
          with pytest.warns(None) as ws:
              data.reindex(dim2=ind)
              assert len(ws) == 0
  
      def test_reindex_variables_copied(self):
          data = create_test_data()
          reindexed_data = data.reindex(copy=False)
          for k in data.variables:
              assert reindexed_data.variables[k] is not data.variables[k]
  
      def test_reindex_method(self):
          ds = Dataset({"x": ("y", [10, 20]), "y": [0, 1]})
          y = [-0.5, 0.5, 1.5]
          actual = ds.reindex(y=y, method="backfill")
          expected = Dataset({"x": ("y", [10, 20, np.nan]), "y": y})
          assert_identical(expected, actual)
  
          actual = ds.reindex(y=y, method="backfill", tolerance=0.1)
          expected = Dataset({"x": ("y", 3 * [np.nan]), "y": y})
          assert_identical(expected, actual)
  
          actual = ds.reindex(y=y, method="pad")
          expected = Dataset({"x": ("y", [np.nan, 10, 20]), "y": y})
          assert_identical(expected, actual)
  
          alt = Dataset({"y": y})
          actual = ds.reindex_like(alt, method="pad")
          assert_identical(expected, actual)
  
      @pytest.mark.parametrize("fill_value", [dtypes.NA, 2, 2.0])
      def test_reindex_fill_value(self, fill_value):
          ds = Dataset({"x": ("y", [10, 20]), "y": [0, 1]})
          y = [0, 1, 2]
          actual = ds.reindex(y=y, fill_value=fill_value)
          if fill_value == dtypes.NA:
              # if we supply the default, we expect the missing value for a
              # float array
              fill_value = np.nan
          expected = Dataset({"x": ("y", [10, 20, fill_value]), "y": y})
          assert_identical(expected, actual)
  
      @pytest.mark.parametrize("fill_value", [dtypes.NA, 2, 2.0])
      def test_reindex_like_fill_value(self, fill_value):
          ds = Dataset({"x": ("y", [10, 20]), "y": [0, 1]})
          y = [0, 1, 2]
          alt = Dataset({"y": y})
          actual = ds.reindex_like(alt, fill_value=fill_value)
          if fill_value == dtypes.NA:
              # if we supply the default, we expect the missing value for a
              # float array
              fill_value = np.nan
          expected = Dataset({"x": ("y", [10, 20, fill_value]), "y": y})
          assert_identical(expected, actual)
  
      @pytest.mark.parametrize("fill_value", [dtypes.NA, 2, 2.0])
      def test_align_fill_value(self, fill_value):
          x = Dataset({"foo": DataArray([1, 2], dims=["x"], coords={"x": [1, 2]})})
          y = Dataset({"bar": DataArray([1, 2], dims=["x"], coords={"x": [1, 3]})})
          x2, y2 = align(x, y, join="outer", fill_value=fill_value)
          if fill_value == dtypes.NA:
              # if we supply the default, we expect the missing value for a
              # float array
              fill_value = np.nan
  
          expected_x2 = Dataset(
              {"foo": DataArray([1, 2, fill_value], dims=["x"], coords={"x": [1, 2, 3]})}
          )
          expected_y2 = Dataset(
              {"bar": DataArray([1, fill_value, 2], dims=["x"], coords={"x": [1, 2, 3]})}
          )
          assert_identical(expected_x2, x2)
          assert_identical(expected_y2, y2)
  
      def test_align(self):
          left = create_test_data()
          right = left.copy(deep=True)
          right["dim3"] = ("dim3", list("cdefghijkl"))
          right["var3"][:-2] = right["var3"][2:].values
          right["var3"][-2:] = np.random.randn(*right["var3"][-2:].shape)
          right["numbers"][:-2] = right["numbers"][2:].values
          right["numbers"][-2:] = -10
  
          intersection = list("cdefghij")
          union = list("abcdefghijkl")
  
          left2, right2 = align(left, right, join="inner")
          assert_array_equal(left2["dim3"], intersection)
          assert_identical(left2, right2)
  
          left2, right2 = align(left, right, join="outer")
  
          assert_array_equal(left2["dim3"], union)
          assert_equal(left2["dim3"].variable, right2["dim3"].variable)
  
          assert_identical(left2.sel(dim3=intersection), right2.sel(dim3=intersection))
          assert np.isnan(left2["var3"][-2:]).all()
          assert np.isnan(right2["var3"][:2]).all()
  
          left2, right2 = align(left, right, join="left")
          assert_equal(left2["dim3"].variable, right2["dim3"].variable)
          assert_equal(left2["dim3"].variable, left["dim3"].variable)
  
          assert_identical(left2.sel(dim3=intersection), right2.sel(dim3=intersection))
          assert np.isnan(right2["var3"][:2]).all()
  
          left2, right2 = align(left, right, join="right")
          assert_equal(left2["dim3"].variable, right2["dim3"].variable)
          assert_equal(left2["dim3"].variable, right["dim3"].variable)
  
          assert_identical(left2.sel(dim3=intersection), right2.sel(dim3=intersection))
  
          assert np.isnan(left2["var3"][-2:]).all()
  
          with raises_regex(ValueError, "invalid value for join"):
              align(left, right, join="foobar")
          with pytest.raises(TypeError):
              align(left, right, foo="bar")
  
      def test_align_exact(self):
          left = xr.Dataset(coords={"x": [0, 1]})
          right = xr.Dataset(coords={"x": [1, 2]})
  
          left1, left2 = xr.align(left, left, join="exact")
          assert_identical(left1, left)
          assert_identical(left2, left)
  
          with raises_regex(ValueError, "indexes .* not equal"):
              xr.align(left, right, join="exact")
  
      def test_align_override(self):
          left = xr.Dataset(coords={"x": [0, 1, 2]})
          right = xr.Dataset(coords={"x": [0.1, 1.1, 2.1], "y": [1, 2, 3]})
          expected_right = xr.Dataset(coords={"x": [0, 1, 2], "y": [1, 2, 3]})
  
          new_left, new_right = xr.align(left, right, join="override")
          assert_identical(left, new_left)
          assert_identical(new_right, expected_right)
  
          new_left, new_right = xr.align(left, right, exclude="x", join="override")
          assert_identical(left, new_left)
          assert_identical(right, new_right)
  
          new_left, new_right = xr.align(
              left.isel(x=0, drop=True), right, exclude="x", join="override"
          )
          assert_identical(left.isel(x=0, drop=True), new_left)
          assert_identical(right, new_right)
  
          with raises_regex(ValueError, "Indexes along dimension 'x' don't have"):
              xr.align(left.isel(x=0).expand_dims("x"), right, join="override")
  
      def test_align_exclude(self):
          x = Dataset(
              {
                  "foo": DataArray(
                      [[1, 2], [3, 4]], dims=["x", "y"], coords={"x": [1, 2], "y": [3, 4]}
                  )
              }
          )
          y = Dataset(
              {
                  "bar": DataArray(
                      [[1, 2], [3, 4]], dims=["x", "y"], coords={"x": [1, 3], "y": [5, 6]}
                  )
              }
          )
          x2, y2 = align(x, y, exclude=["y"], join="outer")
  
          expected_x2 = Dataset(
              {
                  "foo": DataArray(
                      [[1, 2], [3, 4], [np.nan, np.nan]],
                      dims=["x", "y"],
                      coords={"x": [1, 2, 3], "y": [3, 4]},
                  )
              }
          )
          expected_y2 = Dataset(
              {
                  "bar": DataArray(
                      [[1, 2], [np.nan, np.nan], [3, 4]],
                      dims=["x", "y"],
                      coords={"x": [1, 2, 3], "y": [5, 6]},
                  )
              }
          )
          assert_identical(expected_x2, x2)
          assert_identical(expected_y2, y2)
  
      def test_align_nocopy(self):
          x = Dataset({"foo": DataArray([1, 2, 3], coords=[("x", [1, 2, 3])])})
          y = Dataset({"foo": DataArray([1, 2], coords=[("x", [1, 2])])})
          expected_x2 = x
          expected_y2 = Dataset(
              {"foo": DataArray([1, 2, np.nan], coords=[("x", [1, 2, 3])])}
          )
  
          x2, y2 = align(x, y, copy=False, join="outer")
          assert_identical(expected_x2, x2)
          assert_identical(expected_y2, y2)
          assert source_ndarray(x["foo"].data) is source_ndarray(x2["foo"].data)
  
          x2, y2 = align(x, y, copy=True, join="outer")
          assert source_ndarray(x["foo"].data) is not source_ndarray(x2["foo"].data)
          assert_identical(expected_x2, x2)
          assert_identical(expected_y2, y2)
  
      def test_align_indexes(self):
          x = Dataset({"foo": DataArray([1, 2, 3], dims="x", coords=[("x", [1, 2, 3])])})
          (x2,) = align(x, indexes={"x": [2, 3, 1]})
          expected_x2 = Dataset(
              {"foo": DataArray([2, 3, 1], dims="x", coords={"x": [2, 3, 1]})}
          )
  
          assert_identical(expected_x2, x2)
  
      def test_align_non_unique(self):
          x = Dataset({"foo": ("x", [3, 4, 5]), "x": [0, 0, 1]})
          x1, x2 = align(x, x)
          assert x1.identical(x) and x2.identical(x)
  
          y = Dataset({"bar": ("x", [6, 7]), "x": [0, 1]})
          with raises_regex(ValueError, "cannot reindex or align"):
              align(x, y)
  
      def test_broadcast(self):
          ds = Dataset(
              {"foo": 0, "bar": ("x", [1]), "baz": ("y", [2, 3])}, {"c": ("x", [4])}
          )
          expected = Dataset(
              {
                  "foo": (("x", "y"), [[0, 0]]),
                  "bar": (("x", "y"), [[1, 1]]),
                  "baz": (("x", "y"), [[2, 3]]),
              },
              {"c": ("x", [4])},
          )
          (actual,) = broadcast(ds)
          assert_identical(expected, actual)
  
          ds_x = Dataset({"foo": ("x", [1])})
          ds_y = Dataset({"bar": ("y", [2, 3])})
          expected_x = Dataset({"foo": (("x", "y"), [[1, 1]])})
          expected_y = Dataset({"bar": (("x", "y"), [[2, 3]])})
          actual_x, actual_y = broadcast(ds_x, ds_y)
          assert_identical(expected_x, actual_x)
          assert_identical(expected_y, actual_y)
  
          array_y = ds_y["bar"]
          expected_y = expected_y["bar"]
          actual_x, actual_y = broadcast(ds_x, array_y)
          assert_identical(expected_x, actual_x)
          assert_identical(expected_y, actual_y)
  
      def test_broadcast_nocopy(self):
          # Test that data is not copied if not needed
          x = Dataset({"foo": (("x", "y"), [[1, 1]])})
          y = Dataset({"bar": ("y", [2, 3])})
  
          (actual_x,) = broadcast(x)
          assert_identical(x, actual_x)
          assert source_ndarray(actual_x["foo"].data) is source_ndarray(x["foo"].data)
  
          actual_x, actual_y = broadcast(x, y)
          assert_identical(x, actual_x)
          assert source_ndarray(actual_x["foo"].data) is source_ndarray(x["foo"].data)
  
      def test_broadcast_exclude(self):
          x = Dataset(
              {
                  "foo": DataArray(
                      [[1, 2], [3, 4]], dims=["x", "y"], coords={"x": [1, 2], "y": [3, 4]}
                  ),
                  "bar": DataArray(5),
              }
          )
          y = Dataset(
              {
                  "foo": DataArray(
                      [[1, 2]], dims=["z", "y"], coords={"z": [1], "y": [5, 6]}
                  )
              }
          )
          x2, y2 = broadcast(x, y, exclude=["y"])
  
          expected_x2 = Dataset(
              {
                  "foo": DataArray(
                      [[[1, 2]], [[3, 4]]],
                      dims=["x", "z", "y"],
                      coords={"z": [1], "x": [1, 2], "y": [3, 4]},
                  ),
                  "bar": DataArray(
                      [[5], [5]], dims=["x", "z"], coords={"x": [1, 2], "z": [1]}
                  ),
              }
          )
          expected_y2 = Dataset(
              {
                  "foo": DataArray(
                      [[[1, 2]], [[1, 2]]],
                      dims=["x", "z", "y"],
                      coords={"z": [1], "x": [1, 2], "y": [5, 6]},
                  )
              }
          )
          assert_identical(expected_x2, x2)
          assert_identical(expected_y2, y2)
  
      def test_broadcast_misaligned(self):
          x = Dataset({"foo": DataArray([1, 2, 3], coords=[("x", [-1, -2, -3])])})
          y = Dataset(
              {
                  "bar": DataArray(
                      [[1, 2], [3, 4]],
                      dims=["y", "x"],
                      coords={"y": [1, 2], "x": [10, -3]},
                  )
              }
          )
          x2, y2 = broadcast(x, y)
          expected_x2 = Dataset(
              {
                  "foo": DataArray(
                      [[3, 3], [2, 2], [1, 1], [np.nan, np.nan]],
                      dims=["x", "y"],
                      coords={"y": [1, 2], "x": [-3, -2, -1, 10]},
                  )
              }
          )
          expected_y2 = Dataset(
              {
                  "bar": DataArray(
                      [[2, 4], [np.nan, np.nan], [np.nan, np.nan], [1, 3]],
                      dims=["x", "y"],
                      coords={"y": [1, 2], "x": [-3, -2, -1, 10]},
                  )
              }
          )
          assert_identical(expected_x2, x2)
          assert_identical(expected_y2, y2)
  
      def test_variable_indexing(self):
          data = create_test_data()
          v = data["var1"]
          d1 = data["dim1"]
          d2 = data["dim2"]
          assert_equal(v, v[d1.values])
          assert_equal(v, v[d1])
          assert_equal(v[:3], v[d1 < 3])
          assert_equal(v[:, 3:], v[:, d2 >= 1.5])
          assert_equal(v[:3, 3:], v[d1 < 3, d2 >= 1.5])
          assert_equal(v[:3, :2], v[range(3), range(2)])
          assert_equal(v[:3, :2], v.loc[d1[:3], d2[:2]])
  
      def test_drop_variables(self):
          data = create_test_data()
  
          assert_identical(data, data.drop_vars([]))
  
          expected = Dataset({k: data[k] for k in data.variables if k != "time"})
          actual = data.drop_vars("time")
          assert_identical(expected, actual)
          actual = data.drop_vars(["time"])
          assert_identical(expected, actual)
  
          with raises_regex(ValueError, "cannot be found"):
              data.drop_vars("not_found_here")
  
          actual = data.drop_vars("not_found_here", errors="ignore")
          assert_identical(data, actual)
  
          actual = data.drop_vars(["not_found_here"], errors="ignore")
          assert_identical(data, actual)
  
          actual = data.drop_vars(["time", "not_found_here"], errors="ignore")
          assert_identical(expected, actual)
  
          # deprecated approach with `drop` works (straight copy paste from above)
  
          with pytest.warns(PendingDeprecationWarning):
              actual = data.drop("not_found_here", errors="ignore")
          assert_identical(data, actual)
  
          with pytest.warns(PendingDeprecationWarning):
              actual = data.drop(["not_found_here"], errors="ignore")
          assert_identical(data, actual)
  
          with pytest.warns(PendingDeprecationWarning):
              actual = data.drop(["time", "not_found_here"], errors="ignore")
          assert_identical(expected, actual)
  
          with pytest.warns(PendingDeprecationWarning):
              actual = data.drop({"time", "not_found_here"}, errors="ignore")
          assert_identical(expected, actual)
  
      def test_drop_index_labels(self):
          data = Dataset({"A": (["x", "y"], np.random.randn(2, 3)), "x": ["a", "b"]})
  
          with pytest.warns(DeprecationWarning):
              actual = data.drop(["a"], dim="x")
          expected = data.isel(x=[1])
          assert_identical(expected, actual)
  
          with pytest.warns(DeprecationWarning):
              actual = data.drop(["a", "b"], dim="x")
          expected = data.isel(x=slice(0, 0))
          assert_identical(expected, actual)
  
          with pytest.raises(KeyError):
              # not contained in axis
              with pytest.warns(DeprecationWarning):
                  data.drop(["c"], dim="x")
  
          with pytest.warns(DeprecationWarning):
              actual = data.drop(["c"], dim="x", errors="ignore")
          assert_identical(data, actual)
  
          with pytest.raises(ValueError):
              with pytest.warns(DeprecationWarning):
                  data.drop(["c"], dim="x", errors="wrong_value")
  
          with pytest.warns(DeprecationWarning):
              actual = data.drop(["a", "b", "c"], "x", errors="ignore")
          expected = data.isel(x=slice(0, 0))
          assert_identical(expected, actual)
  
          # DataArrays as labels are a nasty corner case as they are not
          # Iterable[Hashable] - DataArray.__iter__ yields scalar DataArrays.
          actual = data.drop_sel(x=DataArray(["a", "b", "c"]), errors="ignore")
          expected = data.isel(x=slice(0, 0))
          assert_identical(expected, actual)
          with pytest.warns(DeprecationWarning):
              data.drop(DataArray(["a", "b", "c"]), dim="x", errors="ignore")
          assert_identical(expected, actual)
  
          with raises_regex(ValueError, "does not have coordinate labels"):
              data.drop_sel(y=1)
  
      def test_drop_labels_by_keyword(self):
          data = Dataset(
              {"A": (["x", "y"], np.random.randn(2, 6)), "x": ["a", "b"], "y": range(6)}
          )
          # Basic functionality.
          assert len(data.coords["x"]) == 2
  
          with pytest.warns(DeprecationWarning):
              ds1 = data.drop(["a"], dim="x")
          ds2 = data.drop_sel(x="a")
          ds3 = data.drop_sel(x=["a"])
          ds4 = data.drop_sel(x=["a", "b"])
          ds5 = data.drop_sel(x=["a", "b"], y=range(0, 6, 2))
  
          arr = DataArray(range(3), dims=["c"])
          with pytest.warns(FutureWarning):
              data.drop(arr.coords)
          with pytest.warns(FutureWarning):
              data.drop(arr.indexes)
  
          assert_array_equal(ds1.coords["x"], ["b"])
          assert_array_equal(ds2.coords["x"], ["b"])
          assert_array_equal(ds3.coords["x"], ["b"])
          assert ds4.coords["x"].size == 0
          assert ds5.coords["x"].size == 0
          assert_array_equal(ds5.coords["y"], [1, 3, 5])
  
          # Error handling if user tries both approaches.
          with pytest.raises(ValueError):
              data.drop(labels=["a"], x="a")
          with pytest.raises(ValueError):
              data.drop(labels=["a"], dim="x", x="a")
          warnings.filterwarnings("ignore", r"\W*drop")
          with pytest.raises(ValueError):
              data.drop(dim="x", x="a")
  
      def test_drop_dims(self):
          data = xr.Dataset(
              {
                  "A": (["x", "y"], np.random.randn(2, 3)),
                  "B": ("x", np.random.randn(2)),
                  "x": ["a", "b"],
                  "z": np.pi,
              }
          )
  
          actual = data.drop_dims("x")
          expected = data.drop_vars(["A", "B", "x"])
          assert_identical(expected, actual)
  
          actual = data.drop_dims("y")
          expected = data.drop_vars("A")
          assert_identical(expected, actual)
  
          actual = data.drop_dims(["x", "y"])
          expected = data.drop_vars(["A", "B", "x"])
          assert_identical(expected, actual)
  
          with pytest.raises((ValueError, KeyError)):
              data.drop_dims("z")  # not a dimension
  
          with pytest.raises((ValueError, KeyError)):
              data.drop_dims(None)
  
          actual = data.drop_dims("z", errors="ignore")
          assert_identical(data, actual)
  
          actual = data.drop_dims(None, errors="ignore")
          assert_identical(data, actual)
  
          with pytest.raises(ValueError):
              actual = data.drop_dims("z", errors="wrong_value")
  
          actual = data.drop_dims(["x", "y", "z"], errors="ignore")
          expected = data.drop_vars(["A", "B", "x"])
          assert_identical(expected, actual)
  
      def test_copy(self):
          data = create_test_data()
          data.attrs["Test"] = [1, 2, 3]
  
          for copied in [data.copy(deep=False), copy(data)]:
              assert_identical(data, copied)
              assert data.encoding == copied.encoding
              # Note: IndexVariable objects with string dtype are always
              # copied because of xarray.core.util.safe_cast_to_index.
              # Limiting the test to data variables.
              for k in data.data_vars:
                  v0 = data.variables[k]
                  v1 = copied.variables[k]
                  assert source_ndarray(v0.data) is source_ndarray(v1.data)
              copied["foo"] = ("z", np.arange(5))
              assert "foo" not in data
  
              copied.attrs["foo"] = "bar"
              assert "foo" not in data.attrs
              assert data.attrs["Test"] is copied.attrs["Test"]
  
          for copied in [data.copy(deep=True), deepcopy(data)]:
              assert_identical(data, copied)
              for k, v0 in data.variables.items():
                  v1 = copied.variables[k]
                  assert v0 is not v1
  
              assert data.attrs["Test"] is not copied.attrs["Test"]
  
      def test_copy_with_data(self):
          orig = create_test_data()
          new_data = {k: np.random.randn(*v.shape) for k, v in orig.data_vars.items()}
          actual = orig.copy(data=new_data)
  
          expected = orig.copy()
          for k, v in new_data.items():
              expected[k].data = v
          assert_identical(expected, actual)
  
      @pytest.mark.xfail(raises=AssertionError)
      @pytest.mark.parametrize(
          "deep, expected_orig",
          [
              [
                  True,
                  xr.DataArray(
                      xr.IndexVariable("a", np.array([1, 2])),
                      coords={"a": [1, 2]},
                      dims=["a"],
                  ),
              ],
              [
                  False,
                  xr.DataArray(
                      xr.IndexVariable("a", np.array([999, 2])),
                      coords={"a": [999, 2]},
                      dims=["a"],
                  ),
              ],
          ],
      )
      def test_copy_coords(self, deep, expected_orig):
          """The test fails for the shallow copy, and apparently only on Windows
          for some reason. In windows coords seem to be immutable unless it's one
          dataset deep copied from another."""
          ds = xr.DataArray(
              np.ones([2, 2, 2]),
              coords={"a": [1, 2], "b": ["x", "y"], "c": [0, 1]},
              dims=["a", "b", "c"],
              name="value",
          ).to_dataset()
          ds_cp = ds.copy(deep=deep)
          ds_cp.coords["a"].data[0] = 999
  
          expected_cp = xr.DataArray(
              xr.IndexVariable("a", np.array([999, 2])),
              coords={"a": [999, 2]},
              dims=["a"],
          )
          assert_identical(ds_cp.coords["a"], expected_cp)
  
          assert_identical(ds.coords["a"], expected_orig)
  
      def test_copy_with_data_errors(self):
          orig = create_test_data()
          new_var1 = np.arange(orig["var1"].size).reshape(orig["var1"].shape)
          with raises_regex(ValueError, "Data must be dict-like"):
              orig.copy(data=new_var1)
          with raises_regex(ValueError, "only contain variables in original"):
              orig.copy(data={"not_in_original": new_var1})
          with raises_regex(ValueError, "contain all variables in original"):
              orig.copy(data={"var1": new_var1})
  
      def test_rename(self):
          data = create_test_data()
          newnames = {"var1": "renamed_var1", "dim2": "renamed_dim2"}
          renamed = data.rename(newnames)
  
          variables = dict(data.variables)
          for k, v in newnames.items():
              variables[v] = variables.pop(k)
  
          for k, v in variables.items():
              dims = list(v.dims)
              for name, newname in newnames.items():
                  if name in dims:
                      dims[dims.index(name)] = newname
  
              assert_equal(
                  Variable(dims, v.values, v.attrs),
                  renamed[k].variable.to_base_variable(),
              )
              assert v.encoding == renamed[k].encoding
              assert type(v) is type(renamed.variables[k])  # noqa: E721
  
          assert "var1" not in renamed
          assert "dim2" not in renamed
  
          with raises_regex(ValueError, "cannot rename 'not_a_var'"):
              data.rename({"not_a_var": "nada"})
  
          with raises_regex(ValueError, "'var1' conflicts"):
              data.rename({"var2": "var1"})
  
          # verify that we can rename a variable without accessing the data
          var1 = data["var1"]
          data["var1"] = (var1.dims, InaccessibleArray(var1.values))
          renamed = data.rename(newnames)
          with pytest.raises(UnexpectedDataAccess):
              renamed["renamed_var1"].values
  
          renamed_kwargs = data.rename(**newnames)
          assert_identical(renamed, renamed_kwargs)
  
      def test_rename_old_name(self):
          # regtest for GH1477
          data = create_test_data()
  
          with raises_regex(ValueError, "'samecol' conflicts"):
              data.rename({"var1": "samecol", "var2": "samecol"})
  
          # This shouldn't cause any problems.
          data.rename({"var1": "var2", "var2": "var1"})
  
      def test_rename_same_name(self):
          data = create_test_data()
          newnames = {"var1": "var1", "dim2": "dim2"}
          renamed = data.rename(newnames)
          assert_identical(renamed, data)
  
      def test_rename_inplace(self):
          times = pd.date_range("2000-01-01", periods=3)
          data = Dataset({"z": ("x", [2, 3, 4]), "t": ("t", times)})
          with pytest.raises(TypeError):
              data.rename({"x": "y"}, inplace=True)
  
      def test_rename_dims(self):
          original = Dataset({"x": ("x", [0, 1, 2]), "y": ("x", [10, 11, 12]), "z": 42})
          expected = Dataset(
              {"x": ("x_new", [0, 1, 2]), "y": ("x_new", [10, 11, 12]), "z": 42}
          )
          expected = expected.set_coords("x")
          dims_dict = {"x": "x_new"}
          actual = original.rename_dims(dims_dict)
          assert_identical(expected, actual)
          actual_2 = original.rename_dims(**dims_dict)
          assert_identical(expected, actual_2)
  
          # Test to raise ValueError
          dims_dict_bad = {"x_bad": "x_new"}
          with pytest.raises(ValueError):
              original.rename_dims(dims_dict_bad)
  
          with pytest.raises(ValueError):
              original.rename_dims({"x": "z"})
  
      def test_rename_vars(self):
          original = Dataset({"x": ("x", [0, 1, 2]), "y": ("x", [10, 11, 12]), "z": 42})
          expected = Dataset(
              {"x_new": ("x", [0, 1, 2]), "y": ("x", [10, 11, 12]), "z": 42}
          )
          expected = expected.set_coords("x_new")
          name_dict = {"x": "x_new"}
          actual = original.rename_vars(name_dict)
          assert_identical(expected, actual)
          actual_2 = original.rename_vars(**name_dict)
          assert_identical(expected, actual_2)
  
          # Test to raise ValueError
          names_dict_bad = {"x_bad": "x_new"}
          with pytest.raises(ValueError):
              original.rename_vars(names_dict_bad)
  
      def test_rename_multiindex(self):
          mindex = pd.MultiIndex.from_tuples(
              [([1, 2]), ([3, 4])], names=["level0", "level1"]
          )
          data = Dataset({}, {"x": mindex})
          with raises_regex(ValueError, "conflicting MultiIndex"):
              data.rename({"x": "level0"})
  
      @requires_cftime
      def test_rename_does_not_change_CFTimeIndex_type(self):
          # make sure CFTimeIndex is not converted to DatetimeIndex #3522
  
          time = xr.cftime_range(start="2000", periods=6, freq="2MS", calendar="noleap")
          orig = Dataset(coords={"time": time})
  
          renamed = orig.rename(time="time_new")
          assert "time_new" in renamed.indexes
          assert isinstance(renamed.indexes["time_new"], CFTimeIndex)
          assert renamed.indexes["time_new"].name == "time_new"
  
          # check original has not changed
          assert "time" in orig.indexes
          assert isinstance(orig.indexes["time"], CFTimeIndex)
          assert orig.indexes["time"].name == "time"
  
          # note: rename_dims(time="time_new") drops "ds.indexes"
          renamed = orig.rename_dims()
          assert isinstance(renamed.indexes["time"], CFTimeIndex)
  
          renamed = orig.rename_vars()
          assert isinstance(renamed.indexes["time"], CFTimeIndex)
  
      def test_rename_does_not_change_DatetimeIndex_type(self):
          # make sure DatetimeIndex is conderved on rename
  
          time = pd.date_range(start="2000", periods=6, freq="2MS")
          orig = Dataset(coords={"time": time})
  
          renamed = orig.rename(time="time_new")
          assert "time_new" in renamed.indexes
          assert isinstance(renamed.indexes["time_new"], DatetimeIndex)
          assert renamed.indexes["time_new"].name == "time_new"
  
          # check original has not changed
          assert "time" in orig.indexes
          assert isinstance(orig.indexes["time"], DatetimeIndex)
          assert orig.indexes["time"].name == "time"
  
          # note: rename_dims(time="time_new") drops "ds.indexes"
          renamed = orig.rename_dims()
          assert isinstance(renamed.indexes["time"], DatetimeIndex)
  
          renamed = orig.rename_vars()
          assert isinstance(renamed.indexes["time"], DatetimeIndex)
  
      def test_swap_dims(self):
          original = Dataset({"x": [1, 2, 3], "y": ("x", list("abc")), "z": 42})
          expected = Dataset({"z": 42}, {"x": ("y", [1, 2, 3]), "y": list("abc")})
          actual = original.swap_dims({"x": "y"})
          assert_identical(expected, actual)
          assert isinstance(actual.variables["y"], IndexVariable)
          assert isinstance(actual.variables["x"], Variable)
          pd.testing.assert_index_equal(actual.indexes["y"], expected.indexes["y"])
  
          roundtripped = actual.swap_dims({"y": "x"})
          assert_identical(original.set_coords("y"), roundtripped)
  
          with raises_regex(ValueError, "cannot swap"):
              original.swap_dims({"y": "x"})
          with raises_regex(ValueError, "replacement dimension"):
              original.swap_dims({"x": "z"})
  
          expected = Dataset(
              {"y": ("u", list("abc")), "z": 42}, coords={"x": ("u", [1, 2, 3])}
          )
          actual = original.swap_dims({"x": "u"})
          assert_identical(expected, actual)
  
          # handle multiindex case
          idx = pd.MultiIndex.from_arrays([list("aab"), list("yzz")], names=["y1", "y2"])
          original = Dataset({"x": [1, 2, 3], "y": ("x", idx), "z": 42})
          expected = Dataset({"z": 42}, {"x": ("y", [1, 2, 3]), "y": idx})
          actual = original.swap_dims({"x": "y"})
          assert_identical(expected, actual)
          assert isinstance(actual.variables["y"], IndexVariable)
          assert isinstance(actual.variables["x"], Variable)
          pd.testing.assert_index_equal(actual.indexes["y"], expected.indexes["y"])
  
      def test_expand_dims_error(self):
          original = Dataset(
              {
                  "x": ("a", np.random.randn(3)),
                  "y": (["b", "a"], np.random.randn(4, 3)),
                  "z": ("a", np.random.randn(3)),
              },
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
              attrs={"key": "entry"},
          )
  
          with raises_regex(ValueError, "already exists"):
              original.expand_dims(dim=["x"])
  
          # Make sure it raises true error also for non-dimensional coordinates
          # which has dimension.
          original = original.set_coords("z")
          with raises_regex(ValueError, "already exists"):
              original.expand_dims(dim=["z"])
  
          original = Dataset(
              {
                  "x": ("a", np.random.randn(3)),
                  "y": (["b", "a"], np.random.randn(4, 3)),
                  "z": ("a", np.random.randn(3)),
              },
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
              attrs={"key": "entry"},
          )
          with raises_regex(TypeError, "value of new dimension"):
              original.expand_dims({"d": 3.2})
          with raises_regex(ValueError, "both keyword and positional"):
              original.expand_dims({"d": 4}, e=4)
  
      def test_expand_dims_int(self):
          original = Dataset(
              {"x": ("a", np.random.randn(3)), "y": (["b", "a"], np.random.randn(4, 3))},
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
              attrs={"key": "entry"},
          )
  
          actual = original.expand_dims(["z"], [1])
          expected = Dataset(
              {
                  "x": original["x"].expand_dims("z", 1),
                  "y": original["y"].expand_dims("z", 1),
              },
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
              attrs={"key": "entry"},
          )
          assert_identical(expected, actual)
          # make sure squeeze restores the original data set.
          roundtripped = actual.squeeze("z")
          assert_identical(original, roundtripped)
  
          # another test with a negative axis
          actual = original.expand_dims(["z"], [-1])
          expected = Dataset(
              {
                  "x": original["x"].expand_dims("z", -1),
                  "y": original["y"].expand_dims("z", -1),
              },
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
              attrs={"key": "entry"},
          )
          assert_identical(expected, actual)
          # make sure squeeze restores the original data set.
          roundtripped = actual.squeeze("z")
          assert_identical(original, roundtripped)
  
      def test_expand_dims_coords(self):
          original = Dataset({"x": ("a", np.array([1, 2, 3]))})
          expected = Dataset(
              {"x": (("b", "a"), np.array([[1, 2, 3], [1, 2, 3]]))}, coords={"b": [1, 2]}
          )
          actual = original.expand_dims(dict(b=[1, 2]))
          assert_identical(expected, actual)
          assert "b" not in original._coord_names
  
      def test_expand_dims_existing_scalar_coord(self):
          original = Dataset({"x": 1}, {"a": 2})
          expected = Dataset({"x": (("a",), [1])}, {"a": [2]})
          actual = original.expand_dims("a")
          assert_identical(expected, actual)
  
      def test_isel_expand_dims_roundtrip(self):
          original = Dataset({"x": (("a",), [1])}, {"a": [2]})
          actual = original.isel(a=0).expand_dims("a")
          assert_identical(actual, original)
  
      def test_expand_dims_mixed_int_and_coords(self):
          # Test expanding one dimension to have size > 1 that doesn't have
          # coordinates, and also expanding another dimension to have size > 1
          # that DOES have coordinates.
          original = Dataset(
              {"x": ("a", np.random.randn(3)), "y": (["b", "a"], np.random.randn(4, 3))},
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
          )
  
          actual = original.expand_dims({"d": 4, "e": ["l", "m", "n"]})
  
          expected = Dataset(
              {
                  "x": xr.DataArray(
                      original["x"].values * np.ones([4, 3, 3]),
                      coords=dict(d=range(4), e=["l", "m", "n"], a=np.linspace(0, 1, 3)),
                      dims=["d", "e", "a"],
                  ).drop_vars("d"),
                  "y": xr.DataArray(
                      original["y"].values * np.ones([4, 3, 4, 3]),
                      coords=dict(
                          d=range(4),
                          e=["l", "m", "n"],
                          b=np.linspace(0, 1, 4),
                          a=np.linspace(0, 1, 3),
                      ),
                      dims=["d", "e", "b", "a"],
                  ).drop_vars("d"),
              },
              coords={"c": np.linspace(0, 1, 5)},
          )
          assert_identical(actual, expected)
  
      def test_expand_dims_kwargs_python36plus(self):
          original = Dataset(
              {"x": ("a", np.random.randn(3)), "y": (["b", "a"], np.random.randn(4, 3))},
              coords={
                  "a": np.linspace(0, 1, 3),
                  "b": np.linspace(0, 1, 4),
                  "c": np.linspace(0, 1, 5),
              },
              attrs={"key": "entry"},
          )
          other_way = original.expand_dims(e=["l", "m", "n"])
          other_way_expected = Dataset(
              {
                  "x": xr.DataArray(
                      original["x"].values * np.ones([3, 3]),
                      coords=dict(e=["l", "m", "n"], a=np.linspace(0, 1, 3)),
                      dims=["e", "a"],
                  ),
                  "y": xr.DataArray(
                      original["y"].values * np.ones([3, 4, 3]),
                      coords=dict(
                          e=["l", "m", "n"],
                          b=np.linspace(0, 1, 4),
                          a=np.linspace(0, 1, 3),
                      ),
                      dims=["e", "b", "a"],
                  ),
              },
              coords={"c": np.linspace(0, 1, 5)},
              attrs={"key": "entry"},
          )
          assert_identical(other_way_expected, other_way)
  
      def test_set_index(self):
          expected = create_test_multiindex()
          mindex = expected["x"].to_index()
          indexes = [mindex.get_level_values(n) for n in mindex.names]
          coords = {idx.name: ("x", idx) for idx in indexes}
          ds = Dataset({}, coords=coords)
  
          obj = ds.set_index(x=mindex.names)
          assert_identical(obj, expected)
  
          with pytest.raises(TypeError):
              ds.set_index(x=mindex.names, inplace=True)
              assert_identical(ds, expected)
  
          # ensure set_index with no existing index and a single data var given
          # doesn't return multi-index
          ds = Dataset(data_vars={"x_var": ("x", [0, 1, 2])})
          expected = Dataset(coords={"x": [0, 1, 2]})
          assert_identical(ds.set_index(x="x_var"), expected)
  
          # Issue 3176: Ensure clear error message on key error.
          with pytest.raises(ValueError) as excinfo:
              ds.set_index(foo="bar")
          assert str(excinfo.value) == "bar is not the name of an existing variable."
  
      def test_reset_index(self):
          ds = create_test_multiindex()
          mindex = ds["x"].to_index()
          indexes = [mindex.get_level_values(n) for n in mindex.names]
          coords = {idx.name: ("x", idx) for idx in indexes}
          expected = Dataset({}, coords=coords)
  
          obj = ds.reset_index("x")
          assert_identical(obj, expected)
  
          with pytest.raises(TypeError):
              ds.reset_index("x", inplace=True)
  
      def test_reorder_levels(self):
          ds = create_test_multiindex()
          mindex = ds["x"].to_index()
          midx = mindex.reorder_levels(["level_2", "level_1"])
          expected = Dataset({}, coords={"x": midx})
  
          reindexed = ds.reorder_levels(x=["level_2", "level_1"])
          assert_identical(reindexed, expected)
  
          with pytest.raises(TypeError):
              ds.reorder_levels(x=["level_2", "level_1"], inplace=True)
  
          ds = Dataset({}, coords={"x": [1, 2]})
          with raises_regex(ValueError, "has no MultiIndex"):
              ds.reorder_levels(x=["level_1", "level_2"])
  
      def test_stack(self):
          ds = Dataset(
              {"a": ("x", [0, 1]), "b": (("x", "y"), [[0, 1], [2, 3]]), "y": ["a", "b"]}
          )
  
          exp_index = pd.MultiIndex.from_product([[0, 1], ["a", "b"]], names=["x", "y"])
          expected = Dataset(
              {"a": ("z", [0, 0, 1, 1]), "b": ("z", [0, 1, 2, 3]), "z": exp_index}
          )
          actual = ds.stack(z=["x", "y"])
          assert_identical(expected, actual)
  
          exp_index = pd.MultiIndex.from_product([["a", "b"], [0, 1]], names=["y", "x"])
          expected = Dataset(
              {"a": ("z", [0, 1, 0, 1]), "b": ("z", [0, 2, 1, 3]), "z": exp_index}
          )
          actual = ds.stack(z=["y", "x"])
          assert_identical(expected, actual)
  
      def test_unstack(self):
          index = pd.MultiIndex.from_product([[0, 1], ["a", "b"]], names=["x", "y"])
          ds = Dataset({"b": ("z", [0, 1, 2, 3]), "z": index})
          expected = Dataset(
              {"b": (("x", "y"), [[0, 1], [2, 3]]), "x": [0, 1], "y": ["a", "b"]}
          )
          for dim in ["z", ["z"], None]:
              actual = ds.unstack(dim)
              assert_identical(actual, expected)
  
      def test_unstack_errors(self):
          ds = Dataset({"x": [1, 2, 3]})
          with raises_regex(ValueError, "does not contain the dimensions"):
              ds.unstack("foo")
          with raises_regex(ValueError, "do not have a MultiIndex"):
              ds.unstack("x")
  
      def test_unstack_fill_value(self):
          ds = xr.Dataset(
              {"var": (("x",), np.arange(6))},
              coords={"x": [0, 1, 2] * 2, "y": (("x",), ["a"] * 3 + ["b"] * 3)},
          )
          # make ds incomplete
          ds = ds.isel(x=[0, 2, 3, 4]).set_index(index=["x", "y"])
          # test fill_value
          actual = ds.unstack("index", fill_value=-1)
          expected = ds.unstack("index").fillna(-1).astype(np.int)
          assert actual["var"].dtype == np.int
          assert_equal(actual, expected)
  
          actual = ds["var"].unstack("index", fill_value=-1)
          expected = ds["var"].unstack("index").fillna(-1).astype(np.int)
          assert actual.equals(expected)
  
      @requires_sparse
      def test_unstack_sparse(self):
          ds = xr.Dataset(
              {"var": (("x",), np.arange(6))},
              coords={"x": [0, 1, 2] * 2, "y": (("x",), ["a"] * 3 + ["b"] * 3)},
          )
          # make ds incomplete
          ds = ds.isel(x=[0, 2, 3, 4]).set_index(index=["x", "y"])
          # test fill_value
          actual = ds.unstack("index", sparse=True)
          expected = ds.unstack("index")
          assert actual["var"].variable._to_dense().equals(expected["var"].variable)
          assert actual["var"].data.density < 1.0
  
          actual = ds["var"].unstack("index", sparse=True)
          expected = ds["var"].unstack("index")
          assert actual.variable._to_dense().equals(expected.variable)
          assert actual.data.density < 1.0
  
      def test_stack_unstack_fast(self):
          ds = Dataset(
              {
                  "a": ("x", [0, 1]),
                  "b": (("x", "y"), [[0, 1], [2, 3]]),
                  "x": [0, 1],
                  "y": ["a", "b"],
              }
          )
          actual = ds.stack(z=["x", "y"]).unstack("z")
          assert actual.broadcast_equals(ds)
  
          actual = ds[["b"]].stack(z=["x", "y"]).unstack("z")
          assert actual.identical(ds[["b"]])
  
      def test_stack_unstack_slow(self):
          ds = Dataset(
              {
                  "a": ("x", [0, 1]),
                  "b": (("x", "y"), [[0, 1], [2, 3]]),
                  "x": [0, 1],
                  "y": ["a", "b"],
              }
          )
          stacked = ds.stack(z=["x", "y"])
          actual = stacked.isel(z=slice(None, None, -1)).unstack("z")
          assert actual.broadcast_equals(ds)
  
          stacked = ds[["b"]].stack(z=["x", "y"])
          actual = stacked.isel(z=slice(None, None, -1)).unstack("z")
          assert actual.identical(ds[["b"]])
  
      def test_to_stacked_array_invalid_sample_dims(self):
          data = xr.Dataset(
              data_vars={"a": (("x", "y"), [[0, 1, 2], [3, 4, 5]]), "b": ("x", [6, 7])},
              coords={"y": ["u", "v", "w"]},
          )
          with pytest.raises(ValueError):
              data.to_stacked_array("features", sample_dims=["y"])
  
      def test_to_stacked_array_name(self):
          name = "adf9d"
  
          # make a two dimensional dataset
          a, b = create_test_stacked_array()
          D = xr.Dataset({"a": a, "b": b})
          sample_dims = ["x"]
  
          y = D.to_stacked_array("features", sample_dims, name=name)
          assert y.name == name
  
      def test_to_stacked_array_dtype_dims(self):
          # make a two dimensional dataset
          a, b = create_test_stacked_array()
          D = xr.Dataset({"a": a, "b": b})
          sample_dims = ["x"]
          y = D.to_stacked_array("features", sample_dims)
          assert y.indexes["features"].levels[1].dtype == D.y.dtype
          assert y.dims == ("x", "features")
  
      def test_to_stacked_array_to_unstacked_dataset(self):
          # make a two dimensional dataset
          a, b = create_test_stacked_array()
          D = xr.Dataset({"a": a, "b": b})
          sample_dims = ["x"]
          y = D.to_stacked_array("features", sample_dims).transpose("x", "features")
  
          x = y.to_unstacked_dataset("features")
          assert_identical(D, x)
  
          # test on just one sample
          x0 = y[0].to_unstacked_dataset("features")
          d0 = D.isel(x=0)
          assert_identical(d0, x0)
  
      def test_to_stacked_array_to_unstacked_dataset_different_dimension(self):
          # test when variables have different dimensionality
          a, b = create_test_stacked_array()
          sample_dims = ["x"]
          D = xr.Dataset({"a": a, "b": b.isel(y=0)})
  
          y = D.to_stacked_array("features", sample_dims)
          x = y.to_unstacked_dataset("features")
          assert_identical(D, x)
  
      def test_update(self):
          data = create_test_data(seed=0)
          expected = data.copy()
          var2 = Variable("dim1", np.arange(8))
          actual = data.update({"var2": var2})
          expected["var2"] = var2
          assert_identical(expected, actual)
  
          actual = data.copy()
          actual_result = actual.update(data)
          assert actual_result is actual
          assert_identical(expected, actual)
  
          with pytest.raises(TypeError):
              actual = data.update(data, inplace=False)
  
          other = Dataset(attrs={"new": "attr"})
          actual = data.copy()
          actual.update(other)
          assert_identical(expected, actual)
  
      def test_update_overwrite_coords(self):
          data = Dataset({"a": ("x", [1, 2])}, {"b": 3})
          data.update(Dataset(coords={"b": 4}))
          expected = Dataset({"a": ("x", [1, 2])}, {"b": 4})
          assert_identical(data, expected)
  
          data = Dataset({"a": ("x", [1, 2])}, {"b": 3})
          data.update(Dataset({"c": 5}, coords={"b": 4}))
          expected = Dataset({"a": ("x", [1, 2]), "c": 5}, {"b": 4})
          assert_identical(data, expected)
  
          data = Dataset({"a": ("x", [1, 2])}, {"b": 3})
          data.update({"c": DataArray(5, coords={"b": 4})})
          expected = Dataset({"a": ("x", [1, 2]), "c": 5}, {"b": 3})
          assert_identical(data, expected)
  
      def test_update_auto_align(self):
          ds = Dataset({"x": ("t", [3, 4])}, {"t": [0, 1]})
  
          expected = Dataset({"x": ("t", [3, 4]), "y": ("t", [np.nan, 5])}, {"t": [0, 1]})
          actual = ds.copy()
          other = {"y": ("t", [5]), "t": [1]}
          with raises_regex(ValueError, "conflicting sizes"):
              actual.update(other)
          actual.update(Dataset(other))
          assert_identical(expected, actual)
  
          actual = ds.copy()
          other = Dataset({"y": ("t", [5]), "t": [100]})
          actual.update(other)
          expected = Dataset(
              {"x": ("t", [3, 4]), "y": ("t", [np.nan] * 2)}, {"t": [0, 1]}
          )
          assert_identical(expected, actual)
  
      def test_getitem(self):
          data = create_test_data()
          assert isinstance(data["var1"], DataArray)
          assert_equal(data["var1"].variable, data.variables["var1"])
          with pytest.raises(KeyError):
              data["notfound"]
          with pytest.raises(KeyError):
              data[["var1", "notfound"]]
  
          actual = data[["var1", "var2"]]
          expected = Dataset({"var1": data["var1"], "var2": data["var2"]})
          assert_equal(expected, actual)
  
          actual = data["numbers"]
          expected = DataArray(
              data["numbers"].variable,
              {"dim3": data["dim3"], "numbers": data["numbers"]},
              dims="dim3",
              name="numbers",
          )
          assert_identical(expected, actual)
  
          actual = data[dict(dim1=0)]
          expected = data.isel(dim1=0)
          assert_identical(expected, actual)
  
      def test_getitem_hashable(self):
          data = create_test_data()
          data[(3, 4)] = data["var1"] + 1
          expected = data["var1"] + 1
          expected.name = (3, 4)
          assert_identical(expected, data[(3, 4)])
          with raises_regex(KeyError, "('var1', 'var2')"):
              data[("var1", "var2")]
  
      def test_virtual_variables_default_coords(self):
          dataset = Dataset({"foo": ("x", range(10))})
          expected = DataArray(range(10), dims="x", name="x")
          actual = dataset["x"]
          assert_identical(expected, actual)
          assert isinstance(actual.variable, IndexVariable)
  
          actual = dataset[["x", "foo"]]
          expected = dataset.assign_coords(x=range(10))
          assert_identical(expected, actual)
  
      def test_virtual_variables_time(self):
          # access virtual variables
          data = create_test_data()
          expected = DataArray(
              1 + np.arange(20), coords=[data["time"]], dims="time", name="dayofyear"
          )
  
          assert_array_equal(
              data["time.month"].values, data.variables["time"].to_index().month
          )
          assert_array_equal(data["time.season"].values, "DJF")
          # test virtual variable math
          assert_array_equal(data["time.dayofyear"] + 1, 2 + np.arange(20))
          assert_array_equal(np.sin(data["time.dayofyear"]), np.sin(1 + np.arange(20)))
          # ensure they become coordinates
          expected = Dataset({}, {"dayofyear": data["time.dayofyear"]})
          actual = data[["time.dayofyear"]]
          assert_equal(expected, actual)
          # non-coordinate variables
          ds = Dataset({"t": ("x", pd.date_range("2000-01-01", periods=3))})
          assert (ds["t.year"] == 2000).all()
  
      def test_virtual_variable_same_name(self):
          # regression test for GH367
          times = pd.date_range("2000-01-01", freq="H", periods=5)
          data = Dataset({"time": times})
          actual = data["time.time"]
          expected = DataArray(times.time, [("time", times)], name="time")
          assert_identical(actual, expected)
  
      def test_virtual_variable_multiindex(self):
          # access multi-index levels as virtual variables
          data = create_test_multiindex()
          expected = DataArray(
              ["a", "a", "b", "b"],
              name="level_1",
              coords=[data["x"].to_index()],
              dims="x",
          )
          assert_identical(expected, data["level_1"])
  
          # combine multi-index level and datetime
          dr_index = pd.date_range("1/1/2011", periods=4, freq="H")
          mindex = pd.MultiIndex.from_arrays(
              [["a", "a", "b", "b"], dr_index], names=("level_str", "level_date")
          )
          data = Dataset({}, {"x": mindex})
          expected = DataArray(
              mindex.get_level_values("level_date").hour,
              name="hour",
              coords=[mindex],
              dims="x",
          )
          assert_identical(expected, data["level_date.hour"])
  
          # attribute style access
          assert_identical(data.level_str, data["level_str"])
  
      def test_time_season(self):
          ds = Dataset({"t": pd.date_range("2000-01-01", periods=12, freq="M")})
          seas = ["DJF"] * 2 + ["MAM"] * 3 + ["JJA"] * 3 + ["SON"] * 3 + ["DJF"]
          assert_array_equal(seas, ds["t.season"])
  
      def test_slice_virtual_variable(self):
          data = create_test_data()
          assert_equal(
              data["time.dayofyear"][:10].variable, Variable(["time"], 1 + np.arange(10))
          )
          assert_equal(data["time.dayofyear"][0].variable, Variable([], 1))
  
      def test_setitem(self):
          # assign a variable
          var = Variable(["dim1"], np.random.randn(8))
          data1 = create_test_data()
          data1["A"] = var
          data2 = data1.copy()
          data2["A"] = var
          assert_identical(data1, data2)
          # assign a dataset array
          dv = 2 * data2["A"]
          data1["B"] = dv.variable
          data2["B"] = dv
          assert_identical(data1, data2)
          # can't assign an ND array without dimensions
          with raises_regex(ValueError, "without explicit dimension names"):
              data2["C"] = var.values.reshape(2, 4)
          # but can assign a 1D array
          data1["C"] = var.values
          data2["C"] = ("C", var.values)
          assert_identical(data1, data2)
          # can assign a scalar
          data1["scalar"] = 0
          data2["scalar"] = ([], 0)
          assert_identical(data1, data2)
          # can't use the same dimension name as a scalar var
          with raises_regex(ValueError, "already exists as a scalar"):
              data1["newvar"] = ("scalar", [3, 4, 5])
          # can't resize a used dimension
          with raises_regex(ValueError, "arguments without labels"):
              data1["dim1"] = data1["dim1"][:5]
          # override an existing value
          data1["A"] = 3 * data2["A"]
          assert_equal(data1["A"], 3 * data2["A"])
  
          with pytest.raises(NotImplementedError):
              data1[{"x": 0}] = 0
  
      def test_setitem_pandas(self):
  
          ds = self.make_example_math_dataset()
          ds["x"] = np.arange(3)
          ds_copy = ds.copy()
          ds_copy["bar"] = ds["bar"].to_pandas()
  
          assert_equal(ds, ds_copy)
  
      def test_setitem_auto_align(self):
          ds = Dataset()
          ds["x"] = ("y", range(3))
          ds["y"] = 1 + np.arange(3)
          expected = Dataset({"x": ("y", range(3)), "y": 1 + np.arange(3)})
          assert_identical(ds, expected)
  
          ds["y"] = DataArray(range(3), dims="y")
          expected = Dataset({"x": ("y", range(3))}, {"y": range(3)})
          assert_identical(ds, expected)
  
          ds["x"] = DataArray([1, 2], coords=[("y", [0, 1])])
          expected = Dataset({"x": ("y", [1, 2, np.nan])}, {"y": range(3)})
          assert_identical(ds, expected)
  
          ds["x"] = 42
          expected = Dataset({"x": 42, "y": range(3)})
          assert_identical(ds, expected)
  
          ds["x"] = DataArray([4, 5, 6, 7], coords=[("y", [0, 1, 2, 3])])
          expected = Dataset({"x": ("y", [4, 5, 6])}, {"y": range(3)})
          assert_identical(ds, expected)
  
      def test_setitem_dimension_override(self):
          # regression test for GH-3377
          ds = xr.Dataset({"x": [0, 1, 2]})
          ds["x"] = ds["x"][:2]
          expected = Dataset({"x": [0, 1]})
          assert_identical(ds, expected)
  
          ds = xr.Dataset({"x": [0, 1, 2]})
          ds["x"] = np.array([0, 1])
          assert_identical(ds, expected)
  
          ds = xr.Dataset({"x": [0, 1, 2]})
          ds.coords["x"] = [0, 1]
          assert_identical(ds, expected)
  
      def test_setitem_with_coords(self):
          # Regression test for GH:2068
          ds = create_test_data()
  
          other = DataArray(
              np.arange(10), dims="dim3", coords={"numbers": ("dim3", np.arange(10))}
          )
          expected = ds.copy()
          expected["var3"] = other.drop_vars("numbers")
          actual = ds.copy()
          actual["var3"] = other
          assert_identical(expected, actual)
          assert "numbers" in other.coords  # should not change other
  
          # with alignment
          other = ds["var3"].isel(dim3=slice(1, -1))
          other["numbers"] = ("dim3", np.arange(8))
          actual = ds.copy()
          actual["var3"] = other
          assert "numbers" in other.coords  # should not change other
          expected = ds.copy()
          expected["var3"] = ds["var3"].isel(dim3=slice(1, -1))
          assert_identical(expected, actual)
  
          # with non-duplicate coords
          other = ds["var3"].isel(dim3=slice(1, -1))
          other["numbers"] = ("dim3", np.arange(8))
          other["position"] = ("dim3", np.arange(8))
          actual = ds.copy()
          actual["var3"] = other
          assert "position" in actual
          assert "position" in other.coords
  
          # assigning a coordinate-only dataarray
          actual = ds.copy()
          other = actual["numbers"]
          other[0] = 10
          actual["numbers"] = other
          assert actual["numbers"][0] == 10
  
          # GH: 2099
          ds = Dataset(
              {"var": ("x", [1, 2, 3])},
              coords={"x": [0, 1, 2], "z1": ("x", [1, 2, 3]), "z2": ("x", [1, 2, 3])},
          )
          ds["var"] = ds["var"] * 2
          assert np.allclose(ds["var"], [2, 4, 6])
  
      def test_setitem_align_new_indexes(self):
          ds = Dataset({"foo": ("x", [1, 2, 3])}, {"x": [0, 1, 2]})
          ds["bar"] = DataArray([2, 3, 4], [("x", [1, 2, 3])])
          expected = Dataset(
              {"foo": ("x", [1, 2, 3]), "bar": ("x", [np.nan, 2, 3])}, {"x": [0, 1, 2]}
          )
          assert_identical(ds, expected)
  
      def test_assign(self):
          ds = Dataset()
          actual = ds.assign(x=[0, 1, 2], y=2)
          expected = Dataset({"x": [0, 1, 2], "y": 2})
          assert_identical(actual, expected)
          assert list(actual.variables) == ["x", "y"]
          assert_identical(ds, Dataset())
  
          actual = actual.assign(y=lambda ds: ds.x ** 2)
          expected = Dataset({"y": ("x", [0, 1, 4]), "x": [0, 1, 2]})
          assert_identical(actual, expected)
  
          actual = actual.assign_coords(z=2)
          expected = Dataset({"y": ("x", [0, 1, 4])}, {"z": 2, "x": [0, 1, 2]})
          assert_identical(actual, expected)
  
          ds = Dataset({"a": ("x", range(3))}, {"b": ("x", ["A"] * 2 + ["B"])})
          actual = ds.groupby("b").assign(c=lambda ds: 2 * ds.a)
          expected = ds.merge({"c": ("x", [0, 2, 4])})
          assert_identical(actual, expected)
  
          actual = ds.groupby("b").assign(c=lambda ds: ds.a.sum())
          expected = ds.merge({"c": ("x", [1, 1, 2])})
          assert_identical(actual, expected)
  
          actual = ds.groupby("b").assign_coords(c=lambda ds: ds.a.sum())
          expected = expected.set_coords("c")
          assert_identical(actual, expected)
  
      def test_assign_coords(self):
          ds = Dataset()
  
          actual = ds.assign(x=[0, 1, 2], y=2)
          actual = actual.assign_coords(x=list("abc"))
          expected = Dataset({"x": list("abc"), "y": 2})
          assert_identical(actual, expected)
  
          actual = ds.assign(x=[0, 1, 2], y=[2, 3])
          actual = actual.assign_coords({"y": [2.0, 3.0]})
          expected = ds.assign(x=[0, 1, 2], y=[2.0, 3.0])
          assert_identical(actual, expected)
  
      def test_assign_attrs(self):
          expected = Dataset(attrs=dict(a=1, b=2))
          new = Dataset()
          actual = new.assign_attrs(a=1, b=2)
          assert_identical(actual, expected)
          assert new.attrs == {}
  
          expected.attrs["c"] = 3
          new_actual = actual.assign_attrs({"c": 3})
          assert_identical(new_actual, expected)
          assert actual.attrs == dict(a=1, b=2)
  
      def test_assign_multiindex_level(self):
          data = create_test_multiindex()
          with raises_regex(ValueError, "conflicting MultiIndex"):
              data.assign(level_1=range(4))
              data.assign_coords(level_1=range(4))
          # raise an Error when any level name is used as dimension GH:2299
          with pytest.raises(ValueError):
              data["y"] = ("level_1", [0, 1])
  
      def test_merge_multiindex_level(self):
          data = create_test_multiindex()
          other = Dataset({"z": ("level_1", [0, 1])})  # conflict dimension
          with pytest.raises(ValueError):
              data.merge(other)
          other = Dataset({"level_1": ("x", [0, 1])})  # conflict variable name
          with pytest.raises(ValueError):
              data.merge(other)
  
      def test_setitem_original_non_unique_index(self):
          # regression test for GH943
          original = Dataset({"data": ("x", np.arange(5))}, coords={"x": [0, 1, 2, 0, 1]})
          expected = Dataset({"data": ("x", np.arange(5))}, {"x": range(5)})
  
          actual = original.copy()
          actual["x"] = list(range(5))
          assert_identical(actual, expected)
  
          actual = original.copy()
          actual["x"] = ("x", list(range(5)))
          assert_identical(actual, expected)
  
          actual = original.copy()
          actual.coords["x"] = list(range(5))
          assert_identical(actual, expected)
  
      def test_setitem_both_non_unique_index(self):
          # regression test for GH956
          names = ["joaquin", "manolo", "joaquin"]
          values = np.random.randint(0, 256, (3, 4, 4))
          array = DataArray(
              values, dims=["name", "row", "column"], coords=[names, range(4), range(4)]
          )
          expected = Dataset({"first": array, "second": array})
          actual = array.rename("first").to_dataset()
          actual["second"] = array
          assert_identical(expected, actual)
  
      def test_setitem_multiindex_level(self):
          data = create_test_multiindex()
          with raises_regex(ValueError, "conflicting MultiIndex"):
              data["level_1"] = range(4)
  
      def test_delitem(self):
          data = create_test_data()
          all_items = set(data.variables)
          assert set(data.variables) == all_items
          del data["var1"]
          assert set(data.variables) == all_items - {"var1"}
          del data["numbers"]
          assert set(data.variables) == all_items - {"var1", "numbers"}
          assert "numbers" not in data.coords
  
          expected = Dataset()
          actual = Dataset({"y": ("x", [1, 2])})
          del actual["y"]
          assert_identical(expected, actual)
  
      def test_squeeze(self):
          data = Dataset({"foo": (["x", "y", "z"], [[[1], [2]]])})
          for args in [[], [["x"]], [["x", "z"]]]:
  
              def get_args(v):
                  return [set(args[0]) & set(v.dims)] if args else []
  
              expected = Dataset(
                  {k: v.squeeze(*get_args(v)) for k, v in data.variables.items()}
              )
              expected = expected.set_coords(data.coords)
              assert_identical(expected, data.squeeze(*args))
          # invalid squeeze
          with raises_regex(ValueError, "cannot select a dimension"):
              data.squeeze("y")
  
      def test_squeeze_drop(self):
          data = Dataset({"foo": ("x", [1])}, {"x": [0]})
          expected = Dataset({"foo": 1})
          selected = data.squeeze(drop=True)
          assert_identical(expected, selected)
  
          expected = Dataset({"foo": 1}, {"x": 0})
          selected = data.squeeze(drop=False)
          assert_identical(expected, selected)
  
          data = Dataset({"foo": (("x", "y"), [[1]])}, {"x": [0], "y": [0]})
          expected = Dataset({"foo": 1})
          selected = data.squeeze(drop=True)
          assert_identical(expected, selected)
  
          expected = Dataset({"foo": ("x", [1])}, {"x": [0]})
          selected = data.squeeze(dim="y", drop=True)
          assert_identical(expected, selected)
  
          data = Dataset({"foo": (("x",), [])}, {"x": []})
          selected = data.squeeze(drop=True)
          assert_identical(data, selected)
  
      def test_groupby(self):
          data = Dataset(
              {"z": (["x", "y"], np.random.randn(3, 5))},
              {"x": ("x", list("abc")), "c": ("x", [0, 1, 0]), "y": range(5)},
          )
          groupby = data.groupby("x")
          assert len(groupby) == 3
          expected_groups = {"a": 0, "b": 1, "c": 2}
          assert groupby.groups == expected_groups
          expected_items = [
              ("a", data.isel(x=0)),
              ("b", data.isel(x=1)),
              ("c", data.isel(x=2)),
          ]
          for actual, expected in zip(groupby, expected_items):
              assert actual[0] == expected[0]
              assert_equal(actual[1], expected[1])
  
          def identity(x):
              return x
  
          for k in ["x", "c", "y"]:
              actual = data.groupby(k, squeeze=False).map(identity)
              assert_equal(data, actual)
  
      def test_groupby_returns_new_type(self):
          data = Dataset({"z": (["x", "y"], np.random.randn(3, 5))})
  
          actual = data.groupby("x").map(lambda ds: ds["z"])
          expected = data["z"]
          assert_identical(expected, actual)
  
          actual = data["z"].groupby("x").map(lambda x: x.to_dataset())
          expected = data
          assert_identical(expected, actual)
  
      def test_groupby_iter(self):
          data = create_test_data()
          for n, (t, sub) in enumerate(list(data.groupby("dim1"))[:3]):
              assert data["dim1"][n] == t
              assert_equal(data["var1"][n], sub["var1"])
              assert_equal(data["var2"][n], sub["var2"])
              assert_equal(data["var3"][:, n], sub["var3"])
  
      def test_groupby_errors(self):
          data = create_test_data()
          with raises_regex(TypeError, "`group` must be"):
              data.groupby(np.arange(10))
          with raises_regex(ValueError, "length does not match"):
              data.groupby(data["dim1"][:3])
          with raises_regex(TypeError, "`group` must be"):
              data.groupby(data.coords["dim1"].to_index())
  
      def test_groupby_reduce(self):
          data = Dataset(
              {
                  "xy": (["x", "y"], np.random.randn(3, 4)),
                  "xonly": ("x", np.random.randn(3)),
                  "yonly": ("y", np.random.randn(4)),
                  "letters": ("y", ["a", "a", "b", "b"]),
              }
          )
  
          expected = data.mean("y")
          expected["yonly"] = expected["yonly"].variable.set_dims({"x": 3})
          actual = data.groupby("x").mean(...)
          assert_allclose(expected, actual)
  
          actual = data.groupby("x").mean("y")
          assert_allclose(expected, actual)
  
          letters = data["letters"]
          expected = Dataset(
              {
                  "xy": data["xy"].groupby(letters).mean(...),
                  "xonly": (data["xonly"].mean().variable.set_dims({"letters": 2})),
                  "yonly": data["yonly"].groupby(letters).mean(),
              }
          )
          actual = data.groupby("letters").mean(...)
          assert_allclose(expected, actual)
  
      def test_groupby_math(self):
          def reorder_dims(x):
              return x.transpose("dim1", "dim2", "dim3", "time")
  
          ds = create_test_data()
          ds["dim1"] = ds["dim1"]
          for squeeze in [True, False]:
              grouped = ds.groupby("dim1", squeeze=squeeze)
  
              expected = reorder_dims(ds + ds.coords["dim1"])
              actual = grouped + ds.coords["dim1"]
              assert_identical(expected, reorder_dims(actual))
  
              actual = ds.coords["dim1"] + grouped
              assert_identical(expected, reorder_dims(actual))
  
              ds2 = 2 * ds
              expected = reorder_dims(ds + ds2)
              actual = grouped + ds2
              assert_identical(expected, reorder_dims(actual))
  
              actual = ds2 + grouped
              assert_identical(expected, reorder_dims(actual))
  
          grouped = ds.groupby("numbers")
          zeros = DataArray([0, 0, 0, 0], [("numbers", range(4))])
          expected = (ds + Variable("dim3", np.zeros(10))).transpose(
              "dim3", "dim1", "dim2", "time"
          )
          actual = grouped + zeros
          assert_equal(expected, actual)
  
          actual = zeros + grouped
          assert_equal(expected, actual)
  
          with raises_regex(ValueError, "incompat.* grouped binary"):
              grouped + ds
          with raises_regex(ValueError, "incompat.* grouped binary"):
              ds + grouped
          with raises_regex(TypeError, "only support binary ops"):
              grouped + 1
          with raises_regex(TypeError, "only support binary ops"):
              grouped + grouped
          with raises_regex(TypeError, "in-place operations"):
              ds += grouped
  
          ds = Dataset(
              {
                  "x": ("time", np.arange(100)),
                  "time": pd.date_range("2000-01-01", periods=100),
              }
          )
          with raises_regex(ValueError, "incompat.* grouped binary"):
              ds + ds.groupby("time.month")
  
      def test_groupby_math_virtual(self):
          ds = Dataset(
              {"x": ("t", [1, 2, 3])}, {"t": pd.date_range("20100101", periods=3)}
          )
          grouped = ds.groupby("t.day")
          actual = grouped - grouped.mean(...)
          expected = Dataset({"x": ("t", [0, 0, 0])}, ds[["t", "t.day"]])
          assert_identical(actual, expected)
  
      def test_groupby_nan(self):
          # nan should be excluded from groupby
          ds = Dataset({"foo": ("x", [1, 2, 3, 4])}, {"bar": ("x", [1, 1, 2, np.nan])})
          actual = ds.groupby("bar").mean(...)
          expected = Dataset({"foo": ("bar", [1.5, 3]), "bar": [1, 2]})
          assert_identical(actual, expected)
  
      def test_groupby_order(self):
          # groupby should preserve variables order
          ds = Dataset()
          for vn in ["a", "b", "c"]:
              ds[vn] = DataArray(np.arange(10), dims=["t"])
          data_vars_ref = list(ds.data_vars.keys())
          ds = ds.groupby("t").mean(...)
          data_vars = list(ds.data_vars.keys())
          assert data_vars == data_vars_ref
          # coords are now at the end of the list, so the test below fails
          # all_vars = list(ds.variables.keys())
          # all_vars_ref = list(ds.variables.keys())
          # self.assertEqual(all_vars, all_vars_ref)
  
      def test_resample_and_first(self):
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
  
          actual = ds.resample(time="1D").first(keep_attrs=True)
          expected = ds.isel(time=[0, 4, 8])
          assert_identical(expected, actual)
  
          # upsampling
          expected_time = pd.date_range("2000-01-01", freq="3H", periods=19)
          expected = ds.reindex(time=expected_time)
          actual = ds.resample(time="3H")
          for how in ["mean", "sum", "first", "last"]:
              method = getattr(actual, how)
              result = method()
              assert_equal(expected, result)
          for method in [np.mean]:
              result = actual.reduce(method)
              assert_equal(expected, result)
  
      def test_resample_min_count(self):
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
          # inject nan
          ds["foo"] = xr.where(ds["foo"] > 2.0, np.nan, ds["foo"])
  
          actual = ds.resample(time="1D").sum(min_count=1)
          expected = xr.concat(
              [
                  ds.isel(time=slice(i * 4, (i + 1) * 4)).sum("time", min_count=1)
                  for i in range(3)
              ],
              dim=actual["time"],
          )
          assert_equal(expected, actual)
  
      def test_resample_by_mean_with_keep_attrs(self):
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
          ds.attrs["dsmeta"] = "dsdata"
  
          resampled_ds = ds.resample(time="1D").mean(keep_attrs=True)
          actual = resampled_ds["bar"].attrs
          expected = ds["bar"].attrs
          assert expected == actual
  
          actual = resampled_ds.attrs
          expected = ds.attrs
          assert expected == actual
  
      def test_resample_loffset(self):
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
          ds.attrs["dsmeta"] = "dsdata"
  
          actual = ds.resample(time="24H", loffset="-12H").mean("time").time
          expected = xr.DataArray(
              ds.bar.to_series().resample("24H", loffset="-12H").mean()
          ).time
          assert_identical(expected, actual)
  
      def test_resample_by_mean_discarding_attrs(self):
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
          ds.attrs["dsmeta"] = "dsdata"
  
          resampled_ds = ds.resample(time="1D").mean(keep_attrs=False)
  
          assert resampled_ds["bar"].attrs == {}
          assert resampled_ds.attrs == {}
  
      def test_resample_by_last_discarding_attrs(self):
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
          ds.attrs["dsmeta"] = "dsdata"
  
          resampled_ds = ds.resample(time="1D").last(keep_attrs=False)
  
          assert resampled_ds["bar"].attrs == {}
          assert resampled_ds.attrs == {}
  
      @requires_scipy
      def test_resample_drop_nondim_coords(self):
          xs = np.arange(6)
          ys = np.arange(3)
          times = pd.date_range("2000-01-01", freq="6H", periods=5)
          data = np.tile(np.arange(5), (6, 3, 1))
          xx, yy = np.meshgrid(xs * 5, ys * 2.5)
          tt = np.arange(len(times), dtype=int)
          array = DataArray(data, {"time": times, "x": xs, "y": ys}, ("x", "y", "time"))
          xcoord = DataArray(xx.T, {"x": xs, "y": ys}, ("x", "y"))
          ycoord = DataArray(yy.T, {"x": xs, "y": ys}, ("x", "y"))
          tcoord = DataArray(tt, {"time": times}, ("time",))
          ds = Dataset({"data": array, "xc": xcoord, "yc": ycoord, "tc": tcoord})
          ds = ds.set_coords(["xc", "yc", "tc"])
  
          # Re-sample
          actual = ds.resample(time="12H").mean("time")
          assert "tc" not in actual.coords
  
          # Up-sample - filling
          actual = ds.resample(time="1H").ffill()
          assert "tc" not in actual.coords
  
          # Up-sample - interpolation
          actual = ds.resample(time="1H").interpolate("linear")
          assert "tc" not in actual.coords
  
      def test_resample_old_api(self):
  
          times = pd.date_range("2000-01-01", freq="6H", periods=10)
          ds = Dataset(
              {
                  "foo": (["time", "x", "y"], np.random.randn(10, 5, 3)),
                  "bar": ("time", np.random.randn(10), {"meta": "data"}),
                  "time": times,
              }
          )
  
          with raises_regex(TypeError, r"resample\(\) no longer supports"):
              ds.resample("1D", "time")
  
          with raises_regex(TypeError, r"resample\(\) no longer supports"):
              ds.resample("1D", dim="time", how="mean")
  
          with raises_regex(TypeError, r"resample\(\) no longer supports"):
              ds.resample("1D", dim="time")
  
      def test_resample_ds_da_are_the_same(self):
          time = pd.date_range("2000-01-01", freq="6H", periods=365 * 4)
          ds = xr.Dataset(
              {
                  "foo": (("time", "x"), np.random.randn(365 * 4, 5)),
                  "time": time,
                  "x": np.arange(5),
              }
          )
          assert_identical(
              ds.resample(time="M").mean()["foo"], ds.foo.resample(time="M").mean()
          )
  
      def test_ds_resample_apply_func_args(self):
          def func(arg1, arg2, arg3=0.0):
              return arg1.mean("time") + arg2 + arg3
  
          times = pd.date_range("2000", freq="D", periods=3)
          ds = xr.Dataset({"foo": ("time", [1.0, 1.0, 1.0]), "time": times})
          expected = xr.Dataset({"foo": ("time", [3.0, 3.0, 3.0]), "time": times})
          actual = ds.resample(time="D").map(func, args=(1.0,), arg3=1.0)
          assert_identical(expected, actual)
  
      def test_to_array(self):
          ds = Dataset(
              {"a": 1, "b": ("x", [1, 2, 3])},
              coords={"c": 42},
              attrs={"Conventions": "None"},
          )
          data = [[1, 1, 1], [1, 2, 3]]
          coords = {"c": 42, "variable": ["a", "b"]}
          dims = ("variable", "x")
          expected = DataArray(data, coords, dims, attrs=ds.attrs)
          actual = ds.to_array()
          assert_identical(expected, actual)
  
          actual = ds.to_array("abc", name="foo")
          expected = expected.rename({"variable": "abc"}).rename("foo")
          assert_identical(expected, actual)
  
      def test_to_and_from_dataframe(self):
          x = np.random.randn(10)
          y = np.random.randn(10)
          t = list("abcdefghij")
          ds = Dataset({"a": ("t", x), "b": ("t", y), "t": ("t", t)})
          expected = pd.DataFrame(
              np.array([x, y]).T, columns=["a", "b"], index=pd.Index(t, name="t")
          )
          actual = ds.to_dataframe()
          # use the .equals method to check all DataFrame metadata
          assert expected.equals(actual), (expected, actual)
  
          # verify coords are included
          actual = ds.set_coords("b").to_dataframe()
          assert expected.equals(actual), (expected, actual)
  
          # check roundtrip
          assert_identical(ds, Dataset.from_dataframe(actual))
  
          # test a case with a MultiIndex
          w = np.random.randn(2, 3)
          ds = Dataset({"w": (("x", "y"), w)})
          ds["y"] = ("y", list("abc"))
          exp_index = pd.MultiIndex.from_arrays(
              [[0, 0, 0, 1, 1, 1], ["a", "b", "c", "a", "b", "c"]], names=["x", "y"]
          )
          expected = pd.DataFrame(w.reshape(-1), columns=["w"], index=exp_index)
          actual = ds.to_dataframe()
          assert expected.equals(actual)
  
          # check roundtrip
          assert_identical(ds.assign_coords(x=[0, 1]), Dataset.from_dataframe(actual))
  
          # check pathological cases
          df = pd.DataFrame([1])
          actual = Dataset.from_dataframe(df)
          expected = Dataset({0: ("index", [1])}, {"index": [0]})
          assert_identical(expected, actual)
  
          df = pd.DataFrame()
          actual = Dataset.from_dataframe(df)
          expected = Dataset(coords={"index": []})
          assert_identical(expected, actual)
  
          # GH697
          df = pd.DataFrame({"A": []})
          actual = Dataset.from_dataframe(df)
          expected = Dataset({"A": DataArray([], dims=("index",))}, {"index": []})
          assert_identical(expected, actual)
  
          # regression test for GH278
          # use int64 to ensure consistent results for the pandas .equals method
          # on windows (which requires the same dtype)
          ds = Dataset({"x": pd.Index(["bar"]), "a": ("y", np.array([1], "int64"))}).isel(
              x=0
          )
          # use .loc to ensure consistent results on Python 3
          actual = ds.to_dataframe().loc[:, ["a", "x"]]
          expected = pd.DataFrame(
              [[1, "bar"]], index=pd.Index([0], name="y"), columns=["a", "x"]
          )
          assert expected.equals(actual), (expected, actual)
  
          ds = Dataset({"x": np.array([0], "int64"), "y": np.array([1], "int64")})
          actual = ds.to_dataframe()
          idx = pd.MultiIndex.from_arrays([[0], [1]], names=["x", "y"])
          expected = pd.DataFrame([[]], index=idx)
          assert expected.equals(actual), (expected, actual)
  
      def test_from_dataframe_categorical(self):
          cat = pd.CategoricalDtype(
              categories=["foo", "bar", "baz", "qux", "quux", "corge"]
          )
          i1 = pd.Series(["foo", "bar", "foo"], dtype=cat)
          i2 = pd.Series(["bar", "bar", "baz"], dtype=cat)
  
          df = pd.DataFrame({"i1": i1, "i2": i2, "values": [1, 2, 3]})
          ds = df.set_index("i1").to_xarray()
          assert len(ds["i1"]) == 3
  
          ds = df.set_index(["i1", "i2"]).to_xarray()
          assert len(ds["i1"]) == 2
          assert len(ds["i2"]) == 2
  
      @requires_sparse
      def test_from_dataframe_sparse(self):
          import sparse
  
          df_base = pd.DataFrame(
              {"x": range(10), "y": list("abcdefghij"), "z": np.arange(0, 100, 10)}
          )
  
          ds_sparse = Dataset.from_dataframe(df_base.set_index("x"), sparse=True)
          ds_dense = Dataset.from_dataframe(df_base.set_index("x"), sparse=False)
          assert isinstance(ds_sparse["y"].data, sparse.COO)
          assert isinstance(ds_sparse["z"].data, sparse.COO)
          ds_sparse["y"].data = ds_sparse["y"].data.todense()
          ds_sparse["z"].data = ds_sparse["z"].data.todense()
          assert_identical(ds_dense, ds_sparse)
  
          ds_sparse = Dataset.from_dataframe(df_base.set_index(["x", "y"]), sparse=True)
          ds_dense = Dataset.from_dataframe(df_base.set_index(["x", "y"]), sparse=False)
          assert isinstance(ds_sparse["z"].data, sparse.COO)
          ds_sparse["z"].data = ds_sparse["z"].data.todense()
          assert_identical(ds_dense, ds_sparse)
  
      def test_to_and_from_empty_dataframe(self):
          # GH697
          expected = pd.DataFrame({"foo": []})
          ds = Dataset.from_dataframe(expected)
          assert len(ds["foo"]) == 0
          actual = ds.to_dataframe()
          assert len(actual) == 0
          assert expected.equals(actual)
  
      def test_from_dataframe_non_unique_columns(self):
          # regression test for GH449
          df = pd.DataFrame(np.zeros((2, 2)))
          df.columns = ["foo", "foo"]
          with raises_regex(ValueError, "non-unique columns"):
              Dataset.from_dataframe(df)
  
      def test_convert_dataframe_with_many_types_and_multiindex(self):
          # regression test for GH737
          df = pd.DataFrame(
              {
                  "a": list("abc"),
                  "b": list(range(1, 4)),
                  "c": np.arange(3, 6).astype("u1"),
                  "d": np.arange(4.0, 7.0, dtype="float64"),
                  "e": [True, False, True],
                  "f": pd.Categorical(list("abc")),
                  "g": pd.date_range("20130101", periods=3),
                  "h": pd.date_range("20130101", periods=3, tz="US/Eastern"),
              }
          )
          df.index = pd.MultiIndex.from_product([["a"], range(3)], names=["one", "two"])
          roundtripped = Dataset.from_dataframe(df).to_dataframe()
          # we can't do perfectly, but we should be at least as faithful as
          # np.asarray
          expected = df.apply(np.asarray)
          assert roundtripped.equals(expected)
  
      def test_to_and_from_dict(self):
          # <xarray.Dataset>
          # Dimensions:  (t: 10)
          # Coordinates:
          #   * t        (t) <U1 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j'
          # Data variables:
          #     a        (t) float64 0.6916 -1.056 -1.163 0.9792 -0.7865 ...
          #     b        (t) float64 1.32 0.1954 1.91 1.39 0.519 -0.2772 ...
          x = np.random.randn(10)
          y = np.random.randn(10)
          t = list("abcdefghij")
          ds = Dataset({"a": ("t", x), "b": ("t", y), "t": ("t", t)})
          expected = {
              "coords": {"t": {"dims": ("t",), "data": t, "attrs": {}}},
              "attrs": {},
              "dims": {"t": 10},
              "data_vars": {
                  "a": {"dims": ("t",), "data": x.tolist(), "attrs": {}},
                  "b": {"dims": ("t",), "data": y.tolist(), "attrs": {}},
              },
          }
  
          actual = ds.to_dict()
  
          # check that they are identical
          assert expected == actual
  
          # check roundtrip
          assert_identical(ds, Dataset.from_dict(actual))
  
          # check the data=False option
          expected_no_data = expected.copy()
          del expected_no_data["coords"]["t"]["data"]
          del expected_no_data["data_vars"]["a"]["data"]
          del expected_no_data["data_vars"]["b"]["data"]
          endiantype = "<U1" if sys.byteorder == "little" else ">U1"
          expected_no_data["coords"]["t"].update({"dtype": endiantype, "shape": (10,)})
          expected_no_data["data_vars"]["a"].update({"dtype": "float64", "shape": (10,)})
          expected_no_data["data_vars"]["b"].update({"dtype": "float64", "shape": (10,)})
          actual_no_data = ds.to_dict(data=False)
          assert expected_no_data == actual_no_data
  
          # verify coords are included roundtrip
          expected_ds = ds.set_coords("b")
          actual = Dataset.from_dict(expected_ds.to_dict())
  
          assert_identical(expected_ds, actual)
  
          # test some incomplete dicts:
          # this one has no attrs field, the dims are strings, and x, y are
          # np.arrays
  
          d = {
              "coords": {"t": {"dims": "t", "data": t}},
              "dims": "t",
              "data_vars": {"a": {"dims": "t", "data": x}, "b": {"dims": "t", "data": y}},
          }
          assert_identical(ds, Dataset.from_dict(d))
  
          # this is kind of a flattened version with no coords, or data_vars
          d = {
              "a": {"dims": "t", "data": x},
              "t": {"data": t, "dims": "t"},
              "b": {"dims": "t", "data": y},
          }
          assert_identical(ds, Dataset.from_dict(d))
  
          # this one is missing some necessary information
          d = {
              "a": {"data": x},
              "t": {"data": t, "dims": "t"},
              "b": {"dims": "t", "data": y},
          }
          with raises_regex(ValueError, "cannot convert dict " "without the key 'dims'"):
              Dataset.from_dict(d)
  
      def test_to_and_from_dict_with_time_dim(self):
          x = np.random.randn(10, 3)
          y = np.random.randn(10, 3)
          t = pd.date_range("20130101", periods=10)
          lat = [77.7, 83.2, 76]
          ds = Dataset(
              {
                  "a": (["t", "lat"], x),
                  "b": (["t", "lat"], y),
                  "t": ("t", t),
                  "lat": ("lat", lat),
              }
          )
          roundtripped = Dataset.from_dict(ds.to_dict())
          assert_identical(ds, roundtripped)
  
      def test_to_and_from_dict_with_nan_nat(self):
          x = np.random.randn(10, 3)
          y = np.random.randn(10, 3)
          y[2] = np.nan
          t = pd.Series(pd.date_range("20130101", periods=10))
          t[2] = np.nan
  
          lat = [77.7, 83.2, 76]
          ds = Dataset(
              {
                  "a": (["t", "lat"], x),
                  "b": (["t", "lat"], y),
                  "t": ("t", t),
                  "lat": ("lat", lat),
              }
          )
          roundtripped = Dataset.from_dict(ds.to_dict())
          assert_identical(ds, roundtripped)
  
      def test_to_dict_with_numpy_attrs(self):
          # this doesn't need to roundtrip
          x = np.random.randn(10)
          y = np.random.randn(10)
          t = list("abcdefghij")
          attrs = {
              "created": np.float64(1998),
              "coords": np.array([37, -110.1, 100]),
              "maintainer": "bar",
          }
          ds = Dataset({"a": ("t", x, attrs), "b": ("t", y, attrs), "t": ("t", t)})
          expected_attrs = {
              "created": attrs["created"].item(),
              "coords": attrs["coords"].tolist(),
              "maintainer": "bar",
          }
          actual = ds.to_dict()
  
          # check that they are identical
          assert expected_attrs == actual["data_vars"]["a"]["attrs"]
  
      def test_pickle(self):
          data = create_test_data()
          roundtripped = pickle.loads(pickle.dumps(data))
          assert_identical(data, roundtripped)
          # regression test for #167:
          assert data.dims == roundtripped.dims
  
      def test_lazy_load(self):
          store = InaccessibleVariableDataStore()
          create_test_data().dump_to_store(store)
  
          for decode_cf in [True, False]:
              ds = open_dataset(store, decode_cf=decode_cf)
              with pytest.raises(UnexpectedDataAccess):
                  ds.load()
              with pytest.raises(UnexpectedDataAccess):
                  ds["var1"].values
  
              # these should not raise UnexpectedDataAccess:
              ds.isel(time=10)
              ds.isel(time=slice(10), dim1=[0]).isel(dim1=0, dim2=-1)
  
      def test_dropna(self):
          x = np.random.randn(4, 4)
          x[::2, 0] = np.nan
          y = np.random.randn(4)
          y[-1] = np.nan
          ds = Dataset({"foo": (("a", "b"), x), "bar": (("b", y))})
  
          expected = ds.isel(a=slice(1, None, 2))
          actual = ds.dropna("a")
          assert_identical(actual, expected)
  
          expected = ds.isel(b=slice(1, 3))
          actual = ds.dropna("b")
          assert_identical(actual, expected)
  
          actual = ds.dropna("b", subset=["foo", "bar"])
          assert_identical(actual, expected)
  
          expected = ds.isel(b=slice(1, None))
          actual = ds.dropna("b", subset=["foo"])
          assert_identical(actual, expected)
  
          expected = ds.isel(b=slice(3))
          actual = ds.dropna("b", subset=["bar"])
          assert_identical(actual, expected)
  
          actual = ds.dropna("a", subset=[])
          assert_identical(actual, ds)
  
          actual = ds.dropna("a", subset=["bar"])
          assert_identical(actual, ds)
  
          actual = ds.dropna("a", how="all")
          assert_identical(actual, ds)
  
          actual = ds.dropna("b", how="all", subset=["bar"])
          expected = ds.isel(b=[0, 1, 2])
          assert_identical(actual, expected)
  
          actual = ds.dropna("b", thresh=1, subset=["bar"])
          assert_identical(actual, expected)
  
          actual = ds.dropna("b", thresh=2)
          assert_identical(actual, ds)
  
          actual = ds.dropna("b", thresh=4)
          expected = ds.isel(b=[1, 2, 3])
          assert_identical(actual, expected)
  
          actual = ds.dropna("a", thresh=3)
          expected = ds.isel(a=[1, 3])
          assert_identical(actual, ds)
  
          with raises_regex(ValueError, "a single dataset dimension"):
              ds.dropna("foo")
          with raises_regex(ValueError, "invalid how"):
              ds.dropna("a", how="somehow")
          with raises_regex(TypeError, "must specify how or thresh"):
              ds.dropna("a", how=None)
  
      def test_fillna(self):
          ds = Dataset({"a": ("x", [np.nan, 1, np.nan, 3])}, {"x": [0, 1, 2, 3]})
  
          # fill with -1
          actual = ds.fillna(-1)
          expected = Dataset({"a": ("x", [-1, 1, -1, 3])}, {"x": [0, 1, 2, 3]})
          assert_identical(expected, actual)
  
          actual = ds.fillna({"a": -1})
          assert_identical(expected, actual)
  
          other = Dataset({"a": -1})
          actual = ds.fillna(other)
          assert_identical(expected, actual)
  
          actual = ds.fillna({"a": other.a})
          assert_identical(expected, actual)
  
          # fill with range(4)
          b = DataArray(range(4), coords=[("x", range(4))])
          actual = ds.fillna(b)
          expected = b.rename("a").to_dataset()
          assert_identical(expected, actual)
  
          actual = ds.fillna(expected)
          assert_identical(expected, actual)
  
          actual = ds.fillna(range(4))
          assert_identical(expected, actual)
  
          actual = ds.fillna(b[:3])
          assert_identical(expected, actual)
  
          # okay to only include some data variables
          ds["b"] = np.nan
          actual = ds.fillna({"a": -1})
          expected = Dataset(
              {"a": ("x", [-1, 1, -1, 3]), "b": np.nan}, {"x": [0, 1, 2, 3]}
          )
          assert_identical(expected, actual)
  
          # but new data variables is not okay
          with raises_regex(ValueError, "must be contained"):
              ds.fillna({"x": 0})
  
          # empty argument should be OK
          result = ds.fillna({})
          assert_identical(ds, result)
  
          result = ds.fillna(Dataset(coords={"c": 42}))
          expected = ds.assign_coords(c=42)
          assert_identical(expected, result)
  
          # groupby
          expected = Dataset({"a": ("x", range(4))}, {"x": [0, 1, 2, 3]})
          for target in [ds, expected]:
              target.coords["b"] = ("x", [0, 0, 1, 1])
          actual = ds.groupby("b").fillna(DataArray([0, 2], dims="b"))
          assert_identical(expected, actual)
  
          actual = ds.groupby("b").fillna(Dataset({"a": ("b", [0, 2])}))
          assert_identical(expected, actual)
  
          # attrs with groupby
          ds.attrs["attr"] = "ds"
          ds.a.attrs["attr"] = "da"
          actual = ds.groupby("b").fillna(Dataset({"a": ("b", [0, 2])}))
          assert actual.attrs == ds.attrs
          assert actual.a.name == "a"
          assert actual.a.attrs == ds.a.attrs
  
          da = DataArray(range(5), name="a", attrs={"attr": "da"})
          actual = da.fillna(1)
          assert actual.name == "a"
          assert actual.attrs == da.attrs
  
          ds = Dataset({"a": da}, attrs={"attr": "ds"})
          actual = ds.fillna({"a": 1})
          assert actual.attrs == ds.attrs
          assert actual.a.name == "a"
          assert actual.a.attrs == ds.a.attrs
  
      def test_where(self):
          ds = Dataset({"a": ("x", range(5))})
          expected = Dataset({"a": ("x", [np.nan, np.nan, 2, 3, 4])})
          actual = ds.where(ds > 1)
          assert_identical(expected, actual)
  
          actual = ds.where(ds.a > 1)
          assert_identical(expected, actual)
  
          actual = ds.where(ds.a.values > 1)
          assert_identical(expected, actual)
  
          actual = ds.where(True)
          assert_identical(ds, actual)
  
          expected = ds.copy(deep=True)
          expected["a"].values = [np.nan] * 5
          actual = ds.where(False)
          assert_identical(expected, actual)
  
          # 2d
          ds = Dataset({"a": (("x", "y"), [[0, 1], [2, 3]])})
          expected = Dataset({"a": (("x", "y"), [[np.nan, 1], [2, 3]])})
          actual = ds.where(ds > 0)
          assert_identical(expected, actual)
  
          # groupby
          ds = Dataset({"a": ("x", range(5))}, {"c": ("x", [0, 0, 1, 1, 1])})
          cond = Dataset({"a": ("c", [True, False])})
          expected = ds.copy(deep=True)
          expected["a"].values = [0, 1] + [np.nan] * 3
          actual = ds.groupby("c").where(cond)
          assert_identical(expected, actual)
  
          # attrs with groupby
          ds.attrs["attr"] = "ds"
          ds.a.attrs["attr"] = "da"
          actual = ds.groupby("c").where(cond)
          assert actual.attrs == ds.attrs
          assert actual.a.name == "a"
          assert actual.a.attrs == ds.a.attrs
  
          # attrs
          da = DataArray(range(5), name="a", attrs={"attr": "da"})
          actual = da.where(da.values > 1)
          assert actual.name == "a"
          assert actual.attrs == da.attrs
  
          ds = Dataset({"a": da}, attrs={"attr": "ds"})
          actual = ds.where(ds > 0)
          assert actual.attrs == ds.attrs
          assert actual.a.name == "a"
          assert actual.a.attrs == ds.a.attrs
  
          # lambda
          ds = Dataset({"a": ("x", range(5))})
          expected = Dataset({"a": ("x", [np.nan, np.nan, 2, 3, 4])})
          actual = ds.where(lambda x: x > 1)
          assert_identical(expected, actual)
  
      def test_where_other(self):
          ds = Dataset({"a": ("x", range(5))}, {"x": range(5)})
          expected = Dataset({"a": ("x", [-1, -1, 2, 3, 4])}, {"x": range(5)})
          actual = ds.where(ds > 1, -1)
          assert_equal(expected, actual)
          assert actual.a.dtype == int
  
          actual = ds.where(lambda x: x > 1, -1)
          assert_equal(expected, actual)
  
          with raises_regex(ValueError, "cannot set"):
              ds.where(ds > 1, other=0, drop=True)
  
          with raises_regex(ValueError, "indexes .* are not equal"):
              ds.where(ds > 1, ds.isel(x=slice(3)))
  
          with raises_regex(ValueError, "exact match required"):
              ds.where(ds > 1, ds.assign(b=2))
  
      def test_where_drop(self):
          # if drop=True
  
          # 1d
          # data array case
          array = DataArray(range(5), coords=[range(5)], dims=["x"])
          expected = DataArray(range(5)[2:], coords=[range(5)[2:]], dims=["x"])
          actual = array.where(array > 1, drop=True)
          assert_identical(expected, actual)
  
          # dataset case
          ds = Dataset({"a": array})
          expected = Dataset({"a": expected})
  
          actual = ds.where(ds > 1, drop=True)
          assert_identical(expected, actual)
  
          actual = ds.where(ds.a > 1, drop=True)
          assert_identical(expected, actual)
  
          with raises_regex(TypeError, "must be a"):
              ds.where(np.arange(5) > 1, drop=True)
  
          # 1d with odd coordinates
          array = DataArray(
              np.array([2, 7, 1, 8, 3]), coords=[np.array([3, 1, 4, 5, 9])], dims=["x"]
          )
          expected = DataArray(
              np.array([7, 8, 3]), coords=[np.array([1, 5, 9])], dims=["x"]
          )
          actual = array.where(array > 2, drop=True)
          assert_identical(expected, actual)
  
          # 1d multiple variables
          ds = Dataset({"a": (("x"), [0, 1, 2, 3]), "b": (("x"), [4, 5, 6, 7])})
          expected = Dataset(
              {"a": (("x"), [np.nan, 1, 2, 3]), "b": (("x"), [4, 5, 6, np.nan])}
          )
          actual = ds.where((ds > 0) & (ds < 7), drop=True)
          assert_identical(expected, actual)
  
          # 2d
          ds = Dataset({"a": (("x", "y"), [[0, 1], [2, 3]])})
          expected = Dataset({"a": (("x", "y"), [[np.nan, 1], [2, 3]])})
          actual = ds.where(ds > 0, drop=True)
          assert_identical(expected, actual)
  
          # 2d with odd coordinates
          ds = Dataset(
              {"a": (("x", "y"), [[0, 1], [2, 3]])},
              coords={
                  "x": [4, 3],
                  "y": [1, 2],
                  "z": (["x", "y"], [[np.e, np.pi], [np.pi * np.e, np.pi * 3]]),
              },
          )
          expected = Dataset(
              {"a": (("x", "y"), [[3]])},
              coords={"x": [3], "y": [2], "z": (["x", "y"], [[np.pi * 3]])},
          )
          actual = ds.where(ds > 2, drop=True)
          assert_identical(expected, actual)
  
          # 2d multiple variables
          ds = Dataset(
              {"a": (("x", "y"), [[0, 1], [2, 3]]), "b": (("x", "y"), [[4, 5], [6, 7]])}
          )
          expected = Dataset(
              {
                  "a": (("x", "y"), [[np.nan, 1], [2, 3]]),
                  "b": (("x", "y"), [[4, 5], [6, 7]]),
              }
          )
          actual = ds.where(ds > 0, drop=True)
          assert_identical(expected, actual)
  
      def test_where_drop_empty(self):
          # regression test for GH1341
          array = DataArray(np.random.rand(100, 10), dims=["nCells", "nVertLevels"])
          mask = DataArray(np.zeros((100,), dtype="bool"), dims="nCells")
          actual = array.where(mask, drop=True)
          expected = DataArray(np.zeros((0, 10)), dims=["nCells", "nVertLevels"])
          assert_identical(expected, actual)
  
      def test_where_drop_no_indexes(self):
          ds = Dataset({"foo": ("x", [0.0, 1.0])})
          expected = Dataset({"foo": ("x", [1.0])})
          actual = ds.where(ds == 1, drop=True)
          assert_identical(expected, actual)
  
      def test_reduce(self):
          data = create_test_data()
  
          assert len(data.mean().coords) == 0
  
          actual = data.max()
          expected = Dataset({k: v.max() for k, v in data.data_vars.items()})
          assert_equal(expected, actual)
  
          assert_equal(data.min(dim=["dim1"]), data.min(dim="dim1"))
  
          for reduct, expected in [
              ("dim2", ["dim1", "dim3", "time"]),
              (["dim2", "time"], ["dim1", "dim3"]),
              (("dim2", "time"), ["dim1", "dim3"]),
              ((), ["dim1", "dim2", "dim3", "time"]),
          ]:
              actual = list(data.min(dim=reduct).dims)
              assert actual == expected
  
          assert_equal(data.mean(dim=[]), data)
  
      def test_reduce_coords(self):
          # regression test for GH1470
          data = xr.Dataset({"a": ("x", [1, 2, 3])}, coords={"b": 4})
          expected = xr.Dataset({"a": 2}, coords={"b": 4})
          actual = data.mean("x")
          assert_identical(actual, expected)
  
          # should be consistent
          actual = data["a"].mean("x").to_dataset()
          assert_identical(actual, expected)
  
      def test_mean_uint_dtype(self):
          data = xr.Dataset(
              {
                  "a": (("x", "y"), np.arange(6).reshape(3, 2).astype("uint")),
                  "b": (("x",), np.array([0.1, 0.2, np.nan])),
              }
          )
          actual = data.mean("x", skipna=True)
          expected = xr.Dataset(
              {"a": data["a"].mean("x"), "b": data["b"].mean("x", skipna=True)}
          )
          assert_identical(actual, expected)
  
      def test_reduce_bad_dim(self):
          data = create_test_data()
          with raises_regex(ValueError, "Dataset does not contain"):
              data.mean(dim="bad_dim")
  
      def test_reduce_cumsum(self):
          data = xr.Dataset(
              {"a": 1, "b": ("x", [1, 2]), "c": (("x", "y"), [[np.nan, 3], [0, 4]])}
          )
          assert_identical(data.fillna(0), data.cumsum("y"))
  
          expected = xr.Dataset(
              {"a": 1, "b": ("x", [1, 3]), "c": (("x", "y"), [[0, 3], [0, 7]])}
          )
          assert_identical(expected, data.cumsum())
  
      def test_reduce_cumsum_test_dims(self):
          data = create_test_data()
          for cumfunc in ["cumsum", "cumprod"]:
              with raises_regex(ValueError, "Dataset does not contain"):
                  getattr(data, cumfunc)(dim="bad_dim")
  
              # ensure dimensions are correct
              for reduct, expected in [
                  ("dim1", ["dim1", "dim2", "dim3", "time"]),
                  ("dim2", ["dim1", "dim2", "dim3", "time"]),
                  ("dim3", ["dim1", "dim2", "dim3", "time"]),
                  ("time", ["dim1", "dim2", "dim3"]),
              ]:
                  actual = getattr(data, cumfunc)(dim=reduct).dims
                  assert list(actual) == expected
  
      def test_reduce_non_numeric(self):
          data1 = create_test_data(seed=44)
          data2 = create_test_data(seed=44)
          add_vars = {"var4": ["dim1", "dim2"]}
          for v, dims in sorted(add_vars.items()):
              size = tuple(data1.dims[d] for d in dims)
              data = np.random.randint(0, 100, size=size).astype(np.str_)
              data1[v] = (dims, data, {"foo": "variable"})
  
          assert "var4" not in data1.mean()
          assert_equal(data1.mean(), data2.mean())
          assert_equal(data1.mean(dim="dim1"), data2.mean(dim="dim1"))
  
      def test_reduce_strings(self):
          expected = Dataset({"x": "a"})
          ds = Dataset({"x": ("y", ["a", "b"])})
          actual = ds.min()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": "b"})
          actual = ds.max()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": 0})
          actual = ds.argmin()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": 1})
          actual = ds.argmax()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": b"a"})
          ds = Dataset({"x": ("y", np.array(["a", "b"], "S1"))})
          actual = ds.min()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": "a"})
          ds = Dataset({"x": ("y", np.array(["a", "b"], "U1"))})
          actual = ds.min()
          assert_identical(expected, actual)
  
      def test_reduce_dtypes(self):
          # regression test for GH342
          expected = Dataset({"x": 1})
          actual = Dataset({"x": True}).sum()
          assert_identical(expected, actual)
  
          # regression test for GH505
          expected = Dataset({"x": 3})
          actual = Dataset({"x": ("y", np.array([1, 2], "uint16"))}).sum()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": 1 + 1j})
          actual = Dataset({"x": ("y", [1, 1j])}).sum()
          assert_identical(expected, actual)
  
      def test_reduce_keep_attrs(self):
          data = create_test_data()
          _attrs = {"attr1": "value1", "attr2": 2929}
  
          attrs = dict(_attrs)
          data.attrs = attrs
  
          # Test dropped attrs
          ds = data.mean()
          assert ds.attrs == {}
          for v in ds.data_vars.values():
              assert v.attrs == {}
  
          # Test kept attrs
          ds = data.mean(keep_attrs=True)
          assert ds.attrs == attrs
          for k, v in ds.data_vars.items():
              assert v.attrs == data[k].attrs
  
      def test_reduce_argmin(self):
          # regression test for #205
          ds = Dataset({"a": ("x", [0, 1])})
          expected = Dataset({"a": ([], 0)})
          actual = ds.argmin()
          assert_identical(expected, actual)
  
          actual = ds.argmin("x")
          assert_identical(expected, actual)
  
      def test_reduce_scalars(self):
          ds = Dataset({"x": ("a", [2, 2]), "y": 2, "z": ("b", [2])})
          expected = Dataset({"x": 0, "y": 0, "z": 0})
          actual = ds.var()
          assert_identical(expected, actual)
  
          expected = Dataset({"x": 0, "y": 0, "z": ("b", [0])})
          actual = ds.var("a")
          assert_identical(expected, actual)
  
      def test_reduce_only_one_axis(self):
          def mean_only_one_axis(x, axis):
              if not isinstance(axis, integer_types):
                  raise TypeError("non-integer axis")
              return x.mean(axis)
  
          ds = Dataset({"a": (["x", "y"], [[0, 1, 2, 3, 4]])})
          expected = Dataset({"a": ("x", [2])})
          actual = ds.reduce(mean_only_one_axis, "y")
          assert_identical(expected, actual)
  
          with raises_regex(
              TypeError, "missing 1 required positional argument: " "'axis'"
          ):
              ds.reduce(mean_only_one_axis)
  
          with raises_regex(TypeError, "non-integer axis"):
              ds.reduce(mean_only_one_axis, axis=["x", "y"])
  
      def test_reduce_no_axis(self):
          def total_sum(x):
              return np.sum(x.flatten())
  
          ds = Dataset({"a": (["x", "y"], [[0, 1, 2, 3, 4]])})
          expected = Dataset({"a": ((), 10)})
          actual = ds.reduce(total_sum)
          assert_identical(expected, actual)
  
          with raises_regex(TypeError, "unexpected keyword argument 'axis'"):
              ds.reduce(total_sum, axis=0)
  
          with raises_regex(TypeError, "unexpected keyword argument 'axis'"):
              ds.reduce(total_sum, dim="x")
  
      def test_reduce_keepdims(self):
          ds = Dataset(
              {"a": (["x", "y"], [[0, 1, 2, 3, 4]])},
              coords={
                  "y": [0, 1, 2, 3, 4],
                  "x": [0],
                  "lat": (["x", "y"], [[0, 1, 2, 3, 4]]),
                  "c": -999.0,
              },
          )
  
          # Shape should match behaviour of numpy reductions with keepdims=True
          # Coordinates involved in the reduction should be removed
          actual = ds.mean(keepdims=True)
          expected = Dataset(
              {"a": (["x", "y"], np.mean(ds.a, keepdims=True))}, coords={"c": ds.c}
          )
          assert_identical(expected, actual)
  
          actual = ds.mean("x", keepdims=True)
          expected = Dataset(
              {"a": (["x", "y"], np.mean(ds.a, axis=0, keepdims=True))},
              coords={"y": ds.y, "c": ds.c},
          )
          assert_identical(expected, actual)
  
      @pytest.mark.parametrize("skipna", [True, False])
      @pytest.mark.parametrize("q", [0.25, [0.50], [0.25, 0.75]])
      def test_quantile(self, q, skipna):
          ds = create_test_data(seed=123)
  
          for dim in [None, "dim1", ["dim1"]]:
              ds_quantile = ds.quantile(q, dim=dim, skipna=skipna)
              if is_scalar(q):
                  assert "quantile" not in ds_quantile.dims
              else:
                  assert "quantile" in ds_quantile.dims
  
              for var, dar in ds.data_vars.items():
                  assert var in ds_quantile
                  assert_identical(
                      ds_quantile[var], dar.quantile(q, dim=dim, skipna=skipna)
                  )
          dim = ["dim1", "dim2"]
          ds_quantile = ds.quantile(q, dim=dim, skipna=skipna)
          assert "dim3" in ds_quantile.dims
          assert all(d not in ds_quantile.dims for d in dim)
  
      @pytest.mark.parametrize("skipna", [True, False])
      def test_quantile_skipna(self, skipna):
          q = 0.1
          dim = "time"
          ds = Dataset({"a": ([dim], np.arange(0, 11))})
          ds = ds.where(ds >= 1)
  
          result = ds.quantile(q=q, dim=dim, skipna=skipna)
  
          value = 1.9 if skipna else np.nan
          expected = Dataset({"a": value}, coords={"quantile": q})
          assert_identical(result, expected)
  
      @requires_bottleneck
      def test_rank(self):
          ds = create_test_data(seed=1234)
          # only ds.var3 depends on dim3
          z = ds.rank("dim3")
          assert ["var3"] == list(z.data_vars)
          # same as dataarray version
          x = z.var3
          y = ds.var3.rank("dim3")
          assert_equal(x, y)
          # coordinates stick
          assert list(z.coords) == list(ds.coords)
          assert list(x.coords) == list(y.coords)
          # invalid dim
          with raises_regex(ValueError, "does not contain"):
              x.rank("invalid_dim")
  
      def test_count(self):
          ds = Dataset({"x": ("a", [np.nan, 1]), "y": 0, "z": np.nan})
          expected = Dataset({"x": 1, "y": 1, "z": 0})
          actual = ds.count()
          assert_identical(expected, actual)
  
      def test_map(self):
          data = create_test_data()
          data.attrs["foo"] = "bar"
  
          assert_identical(data.map(np.mean), data.mean())
  
          expected = data.mean(keep_attrs=True)
          actual = data.map(lambda x: x.mean(keep_attrs=True), keep_attrs=True)
          assert_identical(expected, actual)
  
          assert_identical(data.map(lambda x: x, keep_attrs=True), data.drop_vars("time"))
  
          def scale(x, multiple=1):
              return multiple * x
  
          actual = data.map(scale, multiple=2)
          assert_equal(actual["var1"], 2 * data["var1"])
          assert_identical(actual["numbers"], data["numbers"])
  
          actual = data.map(np.asarray)
          expected = data.drop_vars("time")  # time is not used on a data var
          assert_equal(expected, actual)
  
      def test_apply_pending_deprecated_map(self):
          data = create_test_data()
          data.attrs["foo"] = "bar"
  
          with pytest.warns(PendingDeprecationWarning):
              assert_identical(data.apply(np.mean), data.mean())
  
      def make_example_math_dataset(self):
          variables = {
              "bar": ("x", np.arange(100, 400, 100)),
              "foo": (("x", "y"), 1.0 * np.arange(12).reshape(3, 4)),
          }
          coords = {"abc": ("x", ["a", "b", "c"]), "y": 10 * np.arange(4)}
          ds = Dataset(variables, coords)
          ds["foo"][0, 0] = np.nan
          return ds
  
      def test_dataset_number_math(self):
          ds = self.make_example_math_dataset()
  
          assert_identical(ds, +ds)
          assert_identical(ds, ds + 0)
          assert_identical(ds, 0 + ds)
          assert_identical(ds, ds + np.array(0))
          assert_identical(ds, np.array(0) + ds)
  
          actual = ds.copy(deep=True)
          actual += 0
          assert_identical(ds, actual)
  
      def test_unary_ops(self):
          ds = self.make_example_math_dataset()
  
          assert_identical(ds.map(abs), abs(ds))
          assert_identical(ds.map(lambda x: x + 4), ds + 4)
  
          for func in [
              lambda x: x.isnull(),
              lambda x: x.round(),
              lambda x: x.astype(int),
          ]:
              assert_identical(ds.map(func), func(ds))
  
          assert_identical(ds.isnull(), ~ds.notnull())
  
          # don't actually patch these methods in
          with pytest.raises(AttributeError):
              ds.item
          with pytest.raises(AttributeError):
              ds.searchsorted
  
      def test_dataset_array_math(self):
          ds = self.make_example_math_dataset()
  
          expected = ds.map(lambda x: x - ds["foo"])
          assert_identical(expected, ds - ds["foo"])
          assert_identical(expected, -ds["foo"] + ds)
          assert_identical(expected, ds - ds["foo"].variable)
          assert_identical(expected, -ds["foo"].variable + ds)
          actual = ds.copy(deep=True)
          actual -= ds["foo"]
          assert_identical(expected, actual)
  
          expected = ds.map(lambda x: x + ds["bar"])
          assert_identical(expected, ds + ds["bar"])
          actual = ds.copy(deep=True)
          actual += ds["bar"]
          assert_identical(expected, actual)
  
          expected = Dataset({"bar": ds["bar"] + np.arange(3)})
          assert_identical(expected, ds[["bar"]] + np.arange(3))
          assert_identical(expected, np.arange(3) + ds[["bar"]])
  
      def test_dataset_dataset_math(self):
          ds = self.make_example_math_dataset()
  
          assert_identical(ds, ds + 0 * ds)
          assert_identical(ds, ds + {"foo": 0, "bar": 0})
  
          expected = ds.map(lambda x: 2 * x)
          assert_identical(expected, 2 * ds)
          assert_identical(expected, ds + ds)
          assert_identical(expected, ds + ds.data_vars)
          assert_identical(expected, ds + dict(ds.data_vars))
  
          actual = ds.copy(deep=True)
          expected_id = id(actual)
          actual += ds
          assert_identical(expected, actual)
          assert expected_id == id(actual)
  
          assert_identical(ds == ds, ds.notnull())
  
          subsampled = ds.isel(y=slice(2))
          expected = 2 * subsampled
          assert_identical(expected, subsampled + ds)
          assert_identical(expected, ds + subsampled)
  
      def test_dataset_math_auto_align(self):
          ds = self.make_example_math_dataset()
          subset = ds.isel(y=[1, 3])
          expected = 2 * subset
          actual = ds + subset
          assert_identical(expected, actual)
  
          actual = ds.isel(y=slice(1)) + ds.isel(y=slice(1, None))
          expected = 2 * ds.drop_sel(y=ds.y)
          assert_equal(actual, expected)
  
          actual = ds + ds[["bar"]]
          expected = (2 * ds[["bar"]]).merge(ds.coords)
          assert_identical(expected, actual)
  
          assert_identical(ds + Dataset(), ds.coords.to_dataset())
          assert_identical(Dataset() + Dataset(), Dataset())
  
          ds2 = Dataset(coords={"bar": 42})
          assert_identical(ds + ds2, ds.coords.merge(ds2))
  
          # maybe unary arithmetic with empty datasets should raise instead?
          assert_identical(Dataset() + 1, Dataset())
  
          actual = ds.copy(deep=True)
          other = ds.isel(y=slice(2))
          actual += other
          expected = ds + other.reindex_like(ds)
          assert_identical(expected, actual)
  
      def test_dataset_math_errors(self):
          ds = self.make_example_math_dataset()
  
          with pytest.raises(TypeError):
              ds["foo"] += ds
          with pytest.raises(TypeError):
              ds["foo"].variable += ds
          with raises_regex(ValueError, "must have the same"):
              ds += ds[["bar"]]
  
          # verify we can rollback in-place operations if something goes wrong
          # nb. inplace datetime64 math actually will work with an integer array
          # but not floats thanks to numpy's inconsistent handling
          other = DataArray(np.datetime64("2000-01-01"), coords={"c": 2})
          actual = ds.copy(deep=True)
          with pytest.raises(TypeError):
              actual += other
          assert_identical(actual, ds)
  
      def test_dataset_transpose(self):
          ds = Dataset(
              {
                  "a": (("x", "y"), np.random.randn(3, 4)),
                  "b": (("y", "x"), np.random.randn(4, 3)),
              },
              coords={
                  "x": range(3),
                  "y": range(4),
                  "xy": (("x", "y"), np.random.randn(3, 4)),
              },
          )
  
          actual = ds.transpose()
          expected = Dataset(
              {"a": (("y", "x"), ds.a.values.T), "b": (("x", "y"), ds.b.values.T)},
              coords={
                  "x": ds.x.values,
                  "y": ds.y.values,
                  "xy": (("y", "x"), ds.xy.values.T),
              },
          )
          assert_identical(expected, actual)
  
          actual = ds.transpose(...)
          expected = ds
          assert_identical(expected, actual)
  
          actual = ds.transpose("x", "y")
          expected = ds.map(lambda x: x.transpose("x", "y", transpose_coords=True))
          assert_identical(expected, actual)
  
          ds = create_test_data()
          actual = ds.transpose()
          for k in ds.variables:
              assert actual[k].dims[::-1] == ds[k].dims
  
          new_order = ("dim2", "dim3", "dim1", "time")
          actual = ds.transpose(*new_order)
          for k in ds.variables:
              expected_dims = tuple(d for d in new_order if d in ds[k].dims)
              assert actual[k].dims == expected_dims
  
          # same as above but with ellipsis
          new_order = ("dim2", "dim3", "dim1", "time")
          actual = ds.transpose("dim2", "dim3", ...)
          for k in ds.variables:
              expected_dims = tuple(d for d in new_order if d in ds[k].dims)
              assert actual[k].dims == expected_dims
  
          with raises_regex(ValueError, "permuted"):
              ds.transpose("dim1", "dim2", "dim3")
          with raises_regex(ValueError, "permuted"):
              ds.transpose("dim1", "dim2", "dim3", "time", "extra_dim")
  
          assert "T" not in dir(ds)
  
      def test_dataset_ellipsis_transpose_different_ordered_vars(self):
          # https://github.com/pydata/xarray/issues/1081#issuecomment-544350457
          ds = Dataset(
              dict(
                  a=(("w", "x", "y", "z"), np.ones((2, 3, 4, 5))),
                  b=(("x", "w", "y", "z"), np.zeros((3, 2, 4, 5))),
              )
          )
          result = ds.transpose(..., "z", "y")
          assert list(result["a"].dims) == list("wxzy")
          assert list(result["b"].dims) == list("xwzy")
  
      def test_dataset_retains_period_index_on_transpose(self):
  
          ds = create_test_data()
          ds["time"] = pd.period_range("2000-01-01", periods=20)
  
          transposed = ds.transpose()
  
          assert isinstance(transposed.time.to_index(), pd.PeriodIndex)
  
      def test_dataset_diff_n1_simple(self):
          ds = Dataset({"foo": ("x", [5, 5, 6, 6])})
          actual = ds.diff("x")
          expected = Dataset({"foo": ("x", [0, 1, 0])})
          assert_equal(expected, actual)
  
      def test_dataset_diff_n1_label(self):
          ds = Dataset({"foo": ("x", [5, 5, 6, 6])}, {"x": [0, 1, 2, 3]})
          actual = ds.diff("x", label="lower")
          expected = Dataset({"foo": ("x", [0, 1, 0])}, {"x": [0, 1, 2]})
          assert_equal(expected, actual)
  
          actual = ds.diff("x", label="upper")
          expected = Dataset({"foo": ("x", [0, 1, 0])}, {"x": [1, 2, 3]})
          assert_equal(expected, actual)
  
      def test_dataset_diff_n1(self):
          ds = create_test_data(seed=1)
          actual = ds.diff("dim2")
          expected = {}
          expected["var1"] = DataArray(
              np.diff(ds["var1"].values, axis=1),
              {"dim2": ds["dim2"].values[1:]},
              ["dim1", "dim2"],
          )
          expected["var2"] = DataArray(
              np.diff(ds["var2"].values, axis=1),
              {"dim2": ds["dim2"].values[1:]},
              ["dim1", "dim2"],
          )
          expected["var3"] = ds["var3"]
          expected = Dataset(expected, coords={"time": ds["time"].values})
          expected.coords["numbers"] = ("dim3", ds["numbers"].values)
          assert_equal(expected, actual)
  
      def test_dataset_diff_n2(self):
          ds = create_test_data(seed=1)
          actual = ds.diff("dim2", n=2)
          expected = {}
          expected["var1"] = DataArray(
              np.diff(ds["var1"].values, axis=1, n=2),
              {"dim2": ds["dim2"].values[2:]},
              ["dim1", "dim2"],
          )
          expected["var2"] = DataArray(
              np.diff(ds["var2"].values, axis=1, n=2),
              {"dim2": ds["dim2"].values[2:]},
              ["dim1", "dim2"],
          )
          expected["var3"] = ds["var3"]
          expected = Dataset(expected, coords={"time": ds["time"].values})
          expected.coords["numbers"] = ("dim3", ds["numbers"].values)
          assert_equal(expected, actual)
  
      def test_dataset_diff_exception_n_neg(self):
          ds = create_test_data(seed=1)
          with raises_regex(ValueError, "must be non-negative"):
              ds.diff("dim2", n=-1)
  
      def test_dataset_diff_exception_label_str(self):
          ds = create_test_data(seed=1)
          with raises_regex(ValueError, "'label' argument has to"):
              ds.diff("dim2", label="raise_me")
  
      @pytest.mark.parametrize("fill_value", [dtypes.NA, 2, 2.0])
      def test_shift(self, fill_value):
          coords = {"bar": ("x", list("abc")), "x": [-4, 3, 2]}
          attrs = {"meta": "data"}
          ds = Dataset({"foo": ("x", [1, 2, 3])}, coords, attrs)
          actual = ds.shift(x=1, fill_value=fill_value)
          if fill_value == dtypes.NA:
              # if we supply the default, we expect the missing value for a
              # float array
              fill_value = np.nan
          expected = Dataset({"foo": ("x", [fill_value, 1, 2])}, coords, attrs)
          assert_identical(expected, actual)
  
          with raises_regex(ValueError, "dimensions"):
              ds.shift(foo=123)
  
      def test_roll_coords(self):
          coords = {"bar": ("x", list("abc")), "x": [-4, 3, 2]}
          attrs = {"meta": "data"}
          ds = Dataset({"foo": ("x", [1, 2, 3])}, coords, attrs)
          actual = ds.roll(x=1, roll_coords=True)
  
          ex_coords = {"bar": ("x", list("cab")), "x": [2, -4, 3]}
          expected = Dataset({"foo": ("x", [3, 1, 2])}, ex_coords, attrs)
          assert_identical(expected, actual)
  
          with raises_regex(ValueError, "dimensions"):
              ds.roll(foo=123, roll_coords=True)
  
      def test_roll_no_coords(self):
          coords = {"bar": ("x", list("abc")), "x": [-4, 3, 2]}
          attrs = {"meta": "data"}
          ds = Dataset({"foo": ("x", [1, 2, 3])}, coords, attrs)
          actual = ds.roll(x=1, roll_coords=False)
  
          expected = Dataset({"foo": ("x", [3, 1, 2])}, coords, attrs)
          assert_identical(expected, actual)
  
          with raises_regex(ValueError, "dimensions"):
              ds.roll(abc=321, roll_coords=False)
  
      def test_roll_coords_none(self):
          coords = {"bar": ("x", list("abc")), "x": [-4, 3, 2]}
          attrs = {"meta": "data"}
          ds = Dataset({"foo": ("x", [1, 2, 3])}, coords, attrs)
  
          with pytest.warns(FutureWarning):
              actual = ds.roll(x=1, roll_coords=None)
  
          ex_coords = {"bar": ("x", list("cab")), "x": [2, -4, 3]}
          expected = Dataset({"foo": ("x", [3, 1, 2])}, ex_coords, attrs)
          assert_identical(expected, actual)
  
      def test_roll_multidim(self):
          # regression test for 2445
          arr = xr.DataArray(
              [[1, 2, 3], [4, 5, 6]],
              coords={"x": range(3), "y": range(2)},
              dims=("y", "x"),
          )
          actual = arr.roll(x=1, roll_coords=True)
          expected = xr.DataArray(
              [[3, 1, 2], [6, 4, 5]], coords=[("y", [0, 1]), ("x", [2, 0, 1])]
          )
          assert_identical(expected, actual)
  
      def test_real_and_imag(self):
          attrs = {"foo": "bar"}
          ds = Dataset({"x": ((), 1 + 2j, attrs)}, attrs=attrs)
  
          expected_re = Dataset({"x": ((), 1, attrs)}, attrs=attrs)
          assert_identical(ds.real, expected_re)
  
          expected_im = Dataset({"x": ((), 2, attrs)}, attrs=attrs)
          assert_identical(ds.imag, expected_im)
  
      def test_setattr_raises(self):
          ds = Dataset({}, coords={"scalar": 1}, attrs={"foo": "bar"})
          with raises_regex(AttributeError, "cannot set attr"):
              ds.scalar = 2
          with raises_regex(AttributeError, "cannot set attr"):
              ds.foo = 2
          with raises_regex(AttributeError, "cannot set attr"):
              ds.other = 2
  
      def test_filter_by_attrs(self):
          precip = dict(standard_name="convective_precipitation_flux")
          temp0 = dict(standard_name="air_potential_temperature", height="0 m")
          temp10 = dict(standard_name="air_potential_temperature", height="10 m")
          ds = Dataset(
              {
                  "temperature_0": (["t"], [0], temp0),
                  "temperature_10": (["t"], [0], temp10),
                  "precipitation": (["t"], [0], precip),
              },
              coords={"time": (["t"], [0], dict(axis="T", long_name="time_in_seconds"))},
          )
  
          # Test return empty Dataset.
          ds.filter_by_attrs(standard_name="invalid_standard_name")
          new_ds = ds.filter_by_attrs(standard_name="invalid_standard_name")
          assert not bool(new_ds.data_vars)
  
          # Test return one DataArray.
          new_ds = ds.filter_by_attrs(standard_name="convective_precipitation_flux")
          assert new_ds["precipitation"].standard_name == "convective_precipitation_flux"
  
          assert_equal(new_ds["precipitation"], ds["precipitation"])
  
          # Test filter coordinates
          new_ds = ds.filter_by_attrs(long_name="time_in_seconds")
          assert new_ds["time"].long_name == "time_in_seconds"
          assert not bool(new_ds.data_vars)
  
          # Test return more than one DataArray.
          new_ds = ds.filter_by_attrs(standard_name="air_potential_temperature")
          assert len(new_ds.data_vars) == 2
          for var in new_ds.data_vars:
              assert new_ds[var].standard_name == "air_potential_temperature"
  
          # Test callable.
          new_ds = ds.filter_by_attrs(height=lambda v: v is not None)
          assert len(new_ds.data_vars) == 2
          for var in new_ds.data_vars:
              assert new_ds[var].standard_name == "air_potential_temperature"
  
          new_ds = ds.filter_by_attrs(height="10 m")
          assert len(new_ds.data_vars) == 1
          for var in new_ds.data_vars:
              assert new_ds[var].height == "10 m"
  
          # Test return empty Dataset due to conflicting filters
          new_ds = ds.filter_by_attrs(
              standard_name="convective_precipitation_flux", height="0 m"
          )
          assert not bool(new_ds.data_vars)
  
          # Test return one DataArray with two filter conditions
          new_ds = ds.filter_by_attrs(
              standard_name="air_potential_temperature", height="0 m"
          )
          for var in new_ds.data_vars:
              assert new_ds[var].standard_name == "air_potential_temperature"
              assert new_ds[var].height == "0 m"
              assert new_ds[var].height != "10 m"
  
          # Test return empty Dataset due to conflicting callables
          new_ds = ds.filter_by_attrs(
              standard_name=lambda v: False, height=lambda v: True
          )
          assert not bool(new_ds.data_vars)
  
      def test_binary_op_propagate_indexes(self):
          ds = Dataset(
              {"d1": DataArray([1, 2, 3], dims=["x"], coords={"x": [10, 20, 30]})}
          )
          expected = ds.indexes["x"]
          actual = (ds * 2).indexes["x"]
          assert expected is actual
  
      def test_binary_op_join_setting(self):
          # arithmetic_join applies to data array coordinates
          missing_2 = xr.Dataset({"x": [0, 1]})
          missing_0 = xr.Dataset({"x": [1, 2]})
          with xr.set_options(arithmetic_join="outer"):
              actual = missing_2 + missing_0
          expected = xr.Dataset({"x": [0, 1, 2]})
          assert_equal(actual, expected)
  
          # arithmetic join also applies to data_vars
          ds1 = xr.Dataset({"foo": 1, "bar": 2})
          ds2 = xr.Dataset({"bar": 2, "baz": 3})
          expected = xr.Dataset({"bar": 4})  # default is inner joining
          actual = ds1 + ds2
          assert_equal(actual, expected)
  
          with xr.set_options(arithmetic_join="outer"):
              expected = xr.Dataset({"foo": np.nan, "bar": 4, "baz": np.nan})
              actual = ds1 + ds2
              assert_equal(actual, expected)
  
          with xr.set_options(arithmetic_join="left"):
              expected = xr.Dataset({"foo": np.nan, "bar": 4})
              actual = ds1 + ds2
              assert_equal(actual, expected)
  
          with xr.set_options(arithmetic_join="right"):
              expected = xr.Dataset({"bar": 4, "baz": np.nan})
              actual = ds1 + ds2
              assert_equal(actual, expected)
  
      def test_full_like(self):
          # For more thorough tests, see test_variable.py
          # Note: testing data_vars with mismatched dtypes
          ds = Dataset(
              {
                  "d1": DataArray([1, 2, 3], dims=["x"], coords={"x": [10, 20, 30]}),
                  "d2": DataArray([1.1, 2.2, 3.3], dims=["y"]),
              },
              attrs={"foo": "bar"},
          )
          actual = full_like(ds, 2)
  
          expect = ds.copy(deep=True)
          expect["d1"].values = [2, 2, 2]
          expect["d2"].values = [2.0, 2.0, 2.0]
          assert expect["d1"].dtype == int
          assert expect["d2"].dtype == float
          assert_identical(expect, actual)
  
          # override dtype
          actual = full_like(ds, fill_value=True, dtype=bool)
          expect = ds.copy(deep=True)
          expect["d1"].values = [True, True, True]
          expect["d2"].values = [True, True, True]
          assert expect["d1"].dtype == bool
          assert expect["d2"].dtype == bool
          assert_identical(expect, actual)
  
      def test_combine_first(self):
          dsx0 = DataArray([0, 0], [("x", ["a", "b"])]).to_dataset(name="dsx0")
          dsx1 = DataArray([1, 1], [("x", ["b", "c"])]).to_dataset(name="dsx1")
  
          actual = dsx0.combine_first(dsx1)
          expected = Dataset(
              {"dsx0": ("x", [0, 0, np.nan]), "dsx1": ("x", [np.nan, 1, 1])},
              coords={"x": ["a", "b", "c"]},
          )
          assert_equal(actual, expected)
          assert_equal(actual, xr.merge([dsx0, dsx1]))
  
          # works just like xr.merge([self, other])
          dsy2 = DataArray([2, 2, 2], [("x", ["b", "c", "d"])]).to_dataset(name="dsy2")
          actual = dsx0.combine_first(dsy2)
          expected = xr.merge([dsy2, dsx0])
          assert_equal(actual, expected)
  
      def test_sortby(self):
          ds = Dataset(
              {
                  "A": DataArray(
                      [[1, 2], [3, 4], [5, 6]], [("x", ["c", "b", "a"]), ("y", [1, 0])]
                  ),
                  "B": DataArray([[5, 6], [7, 8], [9, 10]], dims=["x", "y"]),
              }
          )
  
          sorted1d = Dataset(
              {
                  "A": DataArray(
                      [[5, 6], [3, 4], [1, 2]], [("x", ["a", "b", "c"]), ("y", [1, 0])]
                  ),
                  "B": DataArray([[9, 10], [7, 8], [5, 6]], dims=["x", "y"]),
              }
          )
  
          sorted2d = Dataset(
              {
                  "A": DataArray(
                      [[6, 5], [4, 3], [2, 1]], [("x", ["a", "b", "c"]), ("y", [0, 1])]
                  ),
                  "B": DataArray([[10, 9], [8, 7], [6, 5]], dims=["x", "y"]),
              }
          )
  
          expected = sorted1d
          dax = DataArray([100, 99, 98], [("x", ["c", "b", "a"])])
          actual = ds.sortby(dax)
          assert_equal(actual, expected)
  
          # test descending order sort
          actual = ds.sortby(dax, ascending=False)
          assert_equal(actual, ds)
  
          # test alignment (fills in nan for 'c')
          dax_short = DataArray([98, 97], [("x", ["b", "a"])])
          actual = ds.sortby(dax_short)
          assert_equal(actual, expected)
  
          # test 1-D lexsort
          # dax0 is sorted first to give indices of [1, 2, 0]
          # and then dax1 would be used to move index 2 ahead of 1
          dax0 = DataArray([100, 95, 95], [("x", ["c", "b", "a"])])
          dax1 = DataArray([0, 1, 0], [("x", ["c", "b", "a"])])
          actual = ds.sortby([dax0, dax1])  # lexsort underneath gives [2, 1, 0]
          assert_equal(actual, expected)
  
          expected = sorted2d
          # test multi-dim sort by 1D dataarray values
          day = DataArray([90, 80], [("y", [1, 0])])
          actual = ds.sortby([day, dax])
          assert_equal(actual, expected)
  
          # test exception-raising
          with pytest.raises(KeyError) as excinfo:
              actual = ds.sortby("z")
  
          with pytest.raises(ValueError) as excinfo:
              actual = ds.sortby(ds["A"])
          assert "DataArray is not 1-D" in str(excinfo.value)
  
          expected = sorted1d
          actual = ds.sortby("x")
          assert_equal(actual, expected)
  
          # test pandas.MultiIndex
          indices = (("b", 1), ("b", 0), ("a", 1), ("a", 0))
          midx = pd.MultiIndex.from_tuples(indices, names=["one", "two"])
          ds_midx = Dataset(
              {
                  "A": DataArray(
                      [[1, 2], [3, 4], [5, 6], [7, 8]], [("x", midx), ("y", [1, 0])]
                  ),
                  "B": DataArray([[5, 6], [7, 8], [9, 10], [11, 12]], dims=["x", "y"]),
              }
          )
          actual = ds_midx.sortby("x")
          midx_reversed = pd.MultiIndex.from_tuples(
              tuple(reversed(indices)), names=["one", "two"]
          )
          expected = Dataset(
              {
                  "A": DataArray(
                      [[7, 8], [5, 6], [3, 4], [1, 2]],
                      [("x", midx_reversed), ("y", [1, 0])],
                  ),
                  "B": DataArray([[11, 12], [9, 10], [7, 8], [5, 6]], dims=["x", "y"]),
              }
          )
          assert_equal(actual, expected)
  
          # multi-dim sort by coordinate objects
          expected = sorted2d
          actual = ds.sortby(["x", "y"])
          assert_equal(actual, expected)
  
          # test descending order sort
          actual = ds.sortby(["x", "y"], ascending=False)
          assert_equal(actual, ds)
  
      def test_attribute_access(self):
          ds = create_test_data(seed=1)
          for key in ["var1", "var2", "var3", "time", "dim1", "dim2", "dim3", "numbers"]:
              assert_equal(ds[key], getattr(ds, key))
              assert key in dir(ds)
  
          for key in ["dim3", "dim1", "numbers"]:
              assert_equal(ds["var3"][key], getattr(ds.var3, key))
              assert key in dir(ds["var3"])
          # attrs
          assert ds["var3"].attrs["foo"] == ds.var3.foo
          assert "foo" in dir(ds["var3"])
  
      def test_ipython_key_completion(self):
          ds = create_test_data(seed=1)
          actual = ds._ipython_key_completions_()
          expected = ["var1", "var2", "var3", "time", "dim1", "dim2", "dim3", "numbers"]
          for item in actual:
              ds[item]  # should not raise
          assert sorted(actual) == sorted(expected)
  
          # for dataarray
          actual = ds["var3"]._ipython_key_completions_()
          expected = ["dim3", "dim1", "numbers"]
          for item in actual:
              ds["var3"][item]  # should not raise
          assert sorted(actual) == sorted(expected)
  
          # MultiIndex
          ds_midx = ds.stack(dim12=["dim1", "dim2"])
          actual = ds_midx._ipython_key_completions_()
          expected = [
              "var1",
              "var2",
              "var3",
              "time",
              "dim1",
              "dim2",
              "dim3",
              "numbers",
              "dim12",
          ]
          for item in actual:
              ds_midx[item]  # should not raise
          assert sorted(actual) == sorted(expected)
  
          # coords
          actual = ds.coords._ipython_key_completions_()
          expected = ["time", "dim1", "dim2", "dim3", "numbers"]
          for item in actual:
              ds.coords[item]  # should not raise
          assert sorted(actual) == sorted(expected)
  
          actual = ds["var3"].coords._ipython_key_completions_()
          expected = ["dim1", "dim3", "numbers"]
          for item in actual:
              ds["var3"].coords[item]  # should not raise
          assert sorted(actual) == sorted(expected)
  
          # data_vars
          actual = ds.data_vars._ipython_key_completions_()
          expected = ["var1", "var2", "var3", "dim1"]
          for item in actual:
              ds.data_vars[item]  # should not raise
          assert sorted(actual) == sorted(expected)
  
  
  # Py.test tests
  
  
  @pytest.fixture(params=[None])
  def data_set(request):
      return create_test_data(request.param)
  
  
  @pytest.mark.parametrize("test_elements", ([1, 2], np.array([1, 2]), DataArray([1, 2])))
  def test_isin(test_elements):
      expected = Dataset(
          data_vars={
              "var1": (("dim1",), [0, 1]),
              "var2": (("dim1",), [1, 1]),
              "var3": (("dim1",), [0, 1]),
          }
      ).astype("bool")
  
      result = Dataset(
          data_vars={
              "var1": (("dim1",), [0, 1]),
              "var2": (("dim1",), [1, 2]),
              "var3": (("dim1",), [0, 1]),
          }
      ).isin(test_elements)
  
      assert_equal(result, expected)
  
  
  @pytest.mark.skipif(not has_dask, reason="requires dask")
  @pytest.mark.parametrize("test_elements", ([1, 2], np.array([1, 2]), DataArray([1, 2])))
  def test_isin_dask(test_elements):
      expected = Dataset(
          data_vars={
              "var1": (("dim1",), [0, 1]),
              "var2": (("dim1",), [1, 1]),
              "var3": (("dim1",), [0, 1]),
          }
      ).astype("bool")
  
      result = (
          Dataset(
              data_vars={
                  "var1": (("dim1",), [0, 1]),
                  "var2": (("dim1",), [1, 2]),
                  "var3": (("dim1",), [0, 1]),
              }
          )
          .chunk(1)
          .isin(test_elements)
          .compute()
      )
  
      assert_equal(result, expected)
  
  
  def test_isin_dataset():
      ds = Dataset({"x": [1, 2]})
      with pytest.raises(TypeError):
          ds.isin(ds)
  
  
  @pytest.mark.parametrize(
      "unaligned_coords",
      (
          {"x": [2, 1, 0]},
          {"x": (["x"], np.asarray([2, 1, 0]))},
          {"x": (["x"], np.asarray([1, 2, 0]))},
          {"x": pd.Index([2, 1, 0])},
          {"x": Variable(dims="x", data=[0, 2, 1])},
          {"x": IndexVariable(dims="x", data=[0, 1, 2])},
          {"y": 42},
          {"y": ("x", [2, 1, 0])},
          {"y": ("x", np.asarray([2, 1, 0]))},
          {"y": (["x"], np.asarray([2, 1, 0]))},
      ),
  )
  @pytest.mark.parametrize("coords", ({"x": ("x", [0, 1, 2])}, {"x": [0, 1, 2]}))
  def test_dataset_constructor_aligns_to_explicit_coords(unaligned_coords, coords):
  
      a = xr.DataArray([1, 2, 3], dims=["x"], coords=unaligned_coords)
  
      expected = xr.Dataset(coords=coords)
      expected["a"] = a
  
      result = xr.Dataset({"a": a}, coords=coords)
  
      assert_equal(expected, result)
  
  
  def test_error_message_on_set_supplied():
      with pytest.raises(TypeError, match="has invalid type <class 'set'>"):
          xr.Dataset(dict(date=[1, 2, 3], sec={4}))
  
  
  @pytest.mark.parametrize("unaligned_coords", ({"y": ("b", np.asarray([2, 1, 0]))},))
  def test_constructor_raises_with_invalid_coords(unaligned_coords):
  
      with pytest.raises(ValueError, match="not a subset of the DataArray dimensions"):
          xr.DataArray([1, 2, 3], dims=["x"], coords=unaligned_coords)
  
  
  def test_dir_expected_attrs(data_set):
  
      some_expected_attrs = {"pipe", "mean", "isnull", "var1", "dim2", "numbers"}
      result = dir(data_set)
      assert set(result) >= some_expected_attrs
  
  
  def test_dir_non_string(data_set):
      # add a numbered key to ensure this doesn't break dir
      data_set[5] = "foo"
      result = dir(data_set)
      assert 5 not in result
  
      # GH2172
      sample_data = np.random.uniform(size=[2, 2000, 10000])
      x = xr.Dataset({"sample_data": (sample_data.shape, sample_data)})
      x2 = x["sample_data"]
      dir(x2)
  
  
  def test_dir_unicode(data_set):
      data_set["unicode"] = "uni"
      result = dir(data_set)
      assert "unicode" in result
  
  
  @pytest.fixture(params=[1])
  def ds(request):
      if request.param == 1:
          return Dataset(
              {
                  "z1": (["y", "x"], np.random.randn(2, 8)),
                  "z2": (["time", "y"], np.random.randn(10, 2)),
              },
              {
                  "x": ("x", np.linspace(0, 1.0, 8)),
                  "time": ("time", np.linspace(0, 1.0, 10)),
                  "c": ("y", ["a", "b"]),
                  "y": range(2),
              },
          )
  
      if request.param == 2:
          return Dataset(
              {
                  "z1": (["time", "y"], np.random.randn(10, 2)),
                  "z2": (["time"], np.random.randn(10)),
                  "z3": (["x", "time"], np.random.randn(8, 10)),
              },
              {
                  "x": ("x", np.linspace(0, 1.0, 8)),
                  "time": ("time", np.linspace(0, 1.0, 10)),
                  "c": ("y", ["a", "b"]),
                  "y": range(2),
              },
          )
  
  
  def test_coarsen_absent_dims_error(ds):
      with raises_regex(ValueError, "not found in Dataset."):
          ds.coarsen(foo=2)
  
  
  @pytest.mark.parametrize("dask", [True, False])
  @pytest.mark.parametrize(("boundary", "side"), [("trim", "left"), ("pad", "right")])
  def test_coarsen(ds, dask, boundary, side):
      if dask and has_dask:
          ds = ds.chunk({"x": 4})
  
      actual = ds.coarsen(time=2, x=3, boundary=boundary, side=side).max()
      assert_equal(
          actual["z1"], ds["z1"].coarsen(x=3, boundary=boundary, side=side).max()
      )
      # coordinate should be mean by default
      assert_equal(
          actual["time"], ds["time"].coarsen(time=2, boundary=boundary, side=side).mean()
      )
  
  
  @pytest.mark.parametrize("dask", [True, False])
  def test_coarsen_coords(ds, dask):
      if dask and has_dask:
          ds = ds.chunk({"x": 4})
  
      # check if coord_func works
      actual = ds.coarsen(time=2, x=3, boundary="trim", coord_func={"time": "max"}).max()
      assert_equal(actual["z1"], ds["z1"].coarsen(x=3, boundary="trim").max())
      assert_equal(actual["time"], ds["time"].coarsen(time=2, boundary="trim").max())
  
      # raise if exact
      with pytest.raises(ValueError):
          ds.coarsen(x=3).mean()
      # should be no error
      ds.isel(x=slice(0, 3 * (len(ds["x"]) // 3))).coarsen(x=3).mean()
  
      # working test with pd.time
      da = xr.DataArray(
          np.linspace(0, 365, num=364),
          dims="time",
          coords={"time": pd.date_range("15/12/1999", periods=364)},
      )
      actual = da.coarsen(time=2).mean()
  
  
  @requires_cftime
  def test_coarsen_coords_cftime():
      times = xr.cftime_range("2000", periods=6)
      da = xr.DataArray(range(6), [("time", times)])
      actual = da.coarsen(time=3).mean()
      expected_times = xr.cftime_range("2000-01-02", freq="3D", periods=2)
      np.testing.assert_array_equal(actual.time, expected_times)
  
  
  def test_coarsen_keep_attrs():
      _attrs = {"units": "test", "long_name": "testing"}
  
      var1 = np.linspace(10, 15, 100)
      var2 = np.linspace(5, 10, 100)
      coords = np.linspace(1, 10, 100)
  
      ds = Dataset(
          data_vars={"var1": ("coord", var1), "var2": ("coord", var2)},
          coords={"coord": coords},
          attrs=_attrs,
      )
  
      # Test dropped attrs
      dat = ds.coarsen(coord=5).mean()
      assert dat.attrs == {}
  
      # Test kept attrs using dataset keyword
      dat = ds.coarsen(coord=5, keep_attrs=True).mean()
      assert dat.attrs == _attrs
  
      # Test kept attrs using global option
      with set_options(keep_attrs=True):
          dat = ds.coarsen(coord=5).mean()
      assert dat.attrs == _attrs
  
  
  def test_rolling_keep_attrs():
      _attrs = {"units": "test", "long_name": "testing"}
  
      var1 = np.linspace(10, 15, 100)
      var2 = np.linspace(5, 10, 100)
      coords = np.linspace(1, 10, 100)
  
      ds = Dataset(
          data_vars={"var1": ("coord", var1), "var2": ("coord", var2)},
          coords={"coord": coords},
          attrs=_attrs,
      )
  
      # Test dropped attrs
      dat = ds.rolling(dim={"coord": 5}, min_periods=None, center=False).mean()
      assert dat.attrs == {}
  
      # Test kept attrs using dataset keyword
      dat = ds.rolling(
          dim={"coord": 5}, min_periods=None, center=False, keep_attrs=True
      ).mean()
      assert dat.attrs == _attrs
  
      # Test kept attrs using global option
      with set_options(keep_attrs=True):
          dat = ds.rolling(dim={"coord": 5}, min_periods=None, center=False).mean()
      assert dat.attrs == _attrs
  
  
  def test_rolling_properties(ds):
      # catching invalid args
      with pytest.raises(ValueError, match="exactly one dim/window should"):
          ds.rolling(time=7, x=2)
      with pytest.raises(ValueError, match="window must be > 0"):
          ds.rolling(time=-2)
      with pytest.raises(ValueError, match="min_periods must be greater than zero"):
          ds.rolling(time=2, min_periods=0)
      with pytest.raises(KeyError, match="time2"):
          ds.rolling(time2=2)
  
  
  @pytest.mark.parametrize("name", ("sum", "mean", "std", "var", "min", "max", "median"))
  @pytest.mark.parametrize("center", (True, False, None))
  @pytest.mark.parametrize("min_periods", (1, None))
  @pytest.mark.parametrize("key", ("z1", "z2"))
  def test_rolling_wrapped_bottleneck(ds, name, center, min_periods, key):
      bn = pytest.importorskip("bottleneck", minversion="1.1")
  
      # Test all bottleneck functions
      rolling_obj = ds.rolling(time=7, min_periods=min_periods)
  
      func_name = f"move_{name}"
      actual = getattr(rolling_obj, name)()
      if key == "z1":  # z1 does not depend on 'Time' axis. Stored as it is.
          expected = ds[key]
      elif key == "z2":
          expected = getattr(bn, func_name)(
              ds[key].values, window=7, axis=0, min_count=min_periods
          )
      assert_array_equal(actual[key].values, expected)
  
      # Test center
      rolling_obj = ds.rolling(time=7, center=center)
      actual = getattr(rolling_obj, name)()["time"]
      assert_equal(actual, ds["time"])
  
  
  @requires_numbagg
  def test_rolling_exp(ds):
  
      result = ds.rolling_exp(time=10, window_type="span").mean()
      assert isinstance(result, Dataset)
  
  
  @pytest.mark.parametrize("center", (True, False))
  @pytest.mark.parametrize("min_periods", (None, 1, 2, 3))
  @pytest.mark.parametrize("window", (1, 2, 3, 4))
  def test_rolling_pandas_compat(center, window, min_periods):
      df = pd.DataFrame(
          {
              "x": np.random.randn(20),
              "y": np.random.randn(20),
              "time": np.linspace(0, 1, 20),
          }
      )
      ds = Dataset.from_dataframe(df)
  
      if min_periods is not None and window < min_periods:
          min_periods = window
  
      df_rolling = df.rolling(window, center=center, min_periods=min_periods).mean()
      ds_rolling = ds.rolling(index=window, center=center, min_periods=min_periods).mean()
  
      np.testing.assert_allclose(df_rolling["x"].values, ds_rolling["x"].values)
      np.testing.assert_allclose(df_rolling.index, ds_rolling["index"])
  
  
  @pytest.mark.parametrize("center", (True, False))
  @pytest.mark.parametrize("window", (1, 2, 3, 4))
  def test_rolling_construct(center, window):
      df = pd.DataFrame(
          {
              "x": np.random.randn(20),
              "y": np.random.randn(20),
              "time": np.linspace(0, 1, 20),
          }
      )
  
      ds = Dataset.from_dataframe(df)
      df_rolling = df.rolling(window, center=center, min_periods=1).mean()
      ds_rolling = ds.rolling(index=window, center=center)
  
      ds_rolling_mean = ds_rolling.construct("window").mean("window")
      np.testing.assert_allclose(df_rolling["x"].values, ds_rolling_mean["x"].values)
      np.testing.assert_allclose(df_rolling.index, ds_rolling_mean["index"])
  
      # with stride
      ds_rolling_mean = ds_rolling.construct("window", stride=2).mean("window")
      np.testing.assert_allclose(df_rolling["x"][::2].values, ds_rolling_mean["x"].values)
      np.testing.assert_allclose(df_rolling.index[::2], ds_rolling_mean["index"])
      # with fill_value
      ds_rolling_mean = ds_rolling.construct("window", stride=2, fill_value=0.0).mean(
          "window"
      )
      assert (ds_rolling_mean.isnull().sum() == 0).to_array(dim="vars").all()
      assert (ds_rolling_mean["x"] == 0.0).sum() >= 0
  
  
  @pytest.mark.slow
  @pytest.mark.parametrize("ds", (1, 2), indirect=True)
  @pytest.mark.parametrize("center", (True, False))
  @pytest.mark.parametrize("min_periods", (None, 1, 2, 3))
  @pytest.mark.parametrize("window", (1, 2, 3, 4))
  @pytest.mark.parametrize("name", ("sum", "mean", "std", "var", "min", "max", "median"))
  def test_rolling_reduce(ds, center, min_periods, window, name):
  
      if min_periods is not None and window < min_periods:
          min_periods = window
  
      if name == "std" and window == 1:
          pytest.skip("std with window == 1 is unstable in bottleneck")
  
      rolling_obj = ds.rolling(time=window, center=center, min_periods=min_periods)
  
      # add nan prefix to numpy methods to get similar behavior as bottleneck
      actual = rolling_obj.reduce(getattr(np, "nan%s" % name))
      expected = getattr(rolling_obj, name)()
      assert_allclose(actual, expected)
      assert ds.dims == actual.dims
      # make sure the order of data_var are not changed.
      assert list(ds.data_vars.keys()) == list(actual.data_vars.keys())
  
      # Make sure the dimension order is restored
      for key, src_var in ds.data_vars.items():
          assert src_var.dims == actual[key].dims
  
  
  def test_raise_no_warning_for_nan_in_binary_ops():
      with pytest.warns(None) as record:
          Dataset(data_vars={"x": ("y", [1, 2, np.NaN])}) > 0
      assert len(record) == 0
  
  
  @pytest.mark.parametrize("dask", [True, False])
  @pytest.mark.parametrize("edge_order", [1, 2])
  def test_differentiate(dask, edge_order):
      rs = np.random.RandomState(42)
      coord = [0.2, 0.35, 0.4, 0.6, 0.7, 0.75, 0.76, 0.8]
  
      da = xr.DataArray(
          rs.randn(8, 6),
          dims=["x", "y"],
          coords={"x": coord, "z": 3, "x2d": (("x", "y"), rs.randn(8, 6))},
      )
      if dask and has_dask:
          da = da.chunk({"x": 4})
  
      ds = xr.Dataset({"var": da})
  
      # along x
      actual = da.differentiate("x", edge_order)
      expected_x = xr.DataArray(
          np.gradient(da, da["x"], axis=0, edge_order=edge_order),
          dims=da.dims,
          coords=da.coords,
      )
      assert_equal(expected_x, actual)
      assert_equal(
          ds["var"].differentiate("x", edge_order=edge_order),
          ds.differentiate("x", edge_order=edge_order)["var"],
      )
      # coordinate should not change
      assert_equal(da["x"], actual["x"])
  
      # along y
      actual = da.differentiate("y", edge_order)
      expected_y = xr.DataArray(
          np.gradient(da, da["y"], axis=1, edge_order=edge_order),
          dims=da.dims,
          coords=da.coords,
      )
      assert_equal(expected_y, actual)
      assert_equal(actual, ds.differentiate("y", edge_order=edge_order)["var"])
      assert_equal(
          ds["var"].differentiate("y", edge_order=edge_order),
          ds.differentiate("y", edge_order=edge_order)["var"],
      )
  
      with pytest.raises(ValueError):
          da.differentiate("x2d")
  
  
  @pytest.mark.parametrize("dask", [True, False])
  def test_differentiate_datetime(dask):
      rs = np.random.RandomState(42)
      coord = np.array(
          [
              "2004-07-13",
              "2006-01-13",
              "2010-08-13",
              "2010-09-13",
              "2010-10-11",
              "2010-12-13",
              "2011-02-13",
              "2012-08-13",
          ],
          dtype="datetime64",
      )
  
      da = xr.DataArray(
          rs.randn(8, 6),
          dims=["x", "y"],
          coords={"x": coord, "z": 3, "x2d": (("x", "y"), rs.randn(8, 6))},
      )
      if dask and has_dask:
          da = da.chunk({"x": 4})
  
      # along x
      actual = da.differentiate("x", edge_order=1, datetime_unit="D")
      expected_x = xr.DataArray(
          np.gradient(
              da, da["x"].variable._to_numeric(datetime_unit="D"), axis=0, edge_order=1
          ),
          dims=da.dims,
          coords=da.coords,
      )
      assert_equal(expected_x, actual)
  
      actual2 = da.differentiate("x", edge_order=1, datetime_unit="h")
      assert np.allclose(actual, actual2 * 24)
  
      # for datetime variable
      actual = da["x"].differentiate("x", edge_order=1, datetime_unit="D")
      assert np.allclose(actual, 1.0)
  
      # with different date unit
      da = xr.DataArray(coord.astype("datetime64[ms]"), dims=["x"], coords={"x": coord})
      actual = da.differentiate("x", edge_order=1)
      assert np.allclose(actual, 1.0)
  
  
  @pytest.mark.skipif(not has_cftime, reason="Test requires cftime.")
  @pytest.mark.parametrize("dask", [True, False])
  def test_differentiate_cftime(dask):
      rs = np.random.RandomState(42)
      coord = xr.cftime_range("2000", periods=8, freq="2M")
  
      da = xr.DataArray(
          rs.randn(8, 6),
          coords={"time": coord, "z": 3, "t2d": (("time", "y"), rs.randn(8, 6))},
          dims=["time", "y"],
      )
  
      if dask and has_dask:
          da = da.chunk({"time": 4})
  
      actual = da.differentiate("time", edge_order=1, datetime_unit="D")
      expected_data = np.gradient(
          da, da["time"].variable._to_numeric(datetime_unit="D"), axis=0, edge_order=1
      )
      expected = xr.DataArray(expected_data, coords=da.coords, dims=da.dims)
      assert_equal(expected, actual)
  
      actual2 = da.differentiate("time", edge_order=1, datetime_unit="h")
      assert_allclose(actual, actual2 * 24)
  
      # Test the differentiation of datetimes themselves
      actual = da["time"].differentiate("time", edge_order=1, datetime_unit="D")
      assert_allclose(actual, xr.ones_like(da["time"]).astype(float))
  
  
  @pytest.mark.parametrize("dask", [True, False])
  def test_integrate(dask):
      rs = np.random.RandomState(42)
      coord = [0.2, 0.35, 0.4, 0.6, 0.7, 0.75, 0.76, 0.8]
  
      da = xr.DataArray(
          rs.randn(8, 6),
          dims=["x", "y"],
          coords={
              "x": coord,
              "x2": (("x",), rs.randn(8)),
              "z": 3,
              "x2d": (("x", "y"), rs.randn(8, 6)),
          },
      )
      if dask and has_dask:
          da = da.chunk({"x": 4})
  
      ds = xr.Dataset({"var": da})
  
      # along x
      actual = da.integrate("x")
      # coordinate that contains x should be dropped.
      expected_x = xr.DataArray(
          np.trapz(da, da["x"], axis=0),
          dims=["y"],
          coords={k: v for k, v in da.coords.items() if "x" not in v.dims},
      )
      assert_allclose(expected_x, actual.compute())
      assert_equal(ds["var"].integrate("x"), ds.integrate("x")["var"])
  
      # make sure result is also a dask array (if the source is dask array)
      assert isinstance(actual.data, type(da.data))
  
      # along y
      actual = da.integrate("y")
      expected_y = xr.DataArray(
          np.trapz(da, da["y"], axis=1),
          dims=["x"],
          coords={k: v for k, v in da.coords.items() if "y" not in v.dims},
      )
      assert_allclose(expected_y, actual.compute())
      assert_equal(actual, ds.integrate("y")["var"])
      assert_equal(ds["var"].integrate("y"), ds.integrate("y")["var"])
  
      # along x and y
      actual = da.integrate(("y", "x"))
      assert actual.ndim == 0
  
      with pytest.raises(ValueError):
          da.integrate("x2d")
  
  
  @pytest.mark.parametrize("dask", [True, False])
  @pytest.mark.parametrize("which_datetime", ["np", "cftime"])
  def test_trapz_datetime(dask, which_datetime):
      rs = np.random.RandomState(42)
      if which_datetime == "np":
          coord = np.array(
              [
                  "2004-07-13",
                  "2006-01-13",
                  "2010-08-13",
                  "2010-09-13",
                  "2010-10-11",
                  "2010-12-13",
                  "2011-02-13",
                  "2012-08-13",
              ],
              dtype="datetime64",
          )
      else:
          if not has_cftime:
              pytest.skip("Test requires cftime.")
          coord = xr.cftime_range("2000", periods=8, freq="2D")
  
      da = xr.DataArray(
          rs.randn(8, 6),
          coords={"time": coord, "z": 3, "t2d": (("time", "y"), rs.randn(8, 6))},
          dims=["time", "y"],
      )
  
      if dask and has_dask:
          da = da.chunk({"time": 4})
  
      actual = da.integrate("time", datetime_unit="D")
      expected_data = np.trapz(
          da.data,
          duck_array_ops.datetime_to_numeric(da["time"].data, datetime_unit="D"),
          axis=0,
      )
      expected = xr.DataArray(
          expected_data,
          dims=["y"],
          coords={k: v for k, v in da.coords.items() if "time" not in v.dims},
      )
      assert_allclose(expected, actual.compute())
  
      # make sure result is also a dask array (if the source is dask array)
      assert isinstance(actual.data, type(da.data))
  
      actual2 = da.integrate("time", datetime_unit="h")
      assert_allclose(actual, actual2 / 24.0)
  
  
  def test_no_dict():
      d = Dataset()
      with pytest.raises(AttributeError):
          d.__dict__
  
  
  def test_subclass_slots():
      """Test that Dataset subclasses must explicitly define ``__slots__``.
  
      .. note::
         As of 0.13.0, this is actually mitigated into a FutureWarning for any class
         defined outside of the xarray package.
      """
      with pytest.raises(AttributeError) as e:
  
          class MyDS(Dataset):
              pass
  
      assert str(e.value) == "MyDS must explicitly define __slots__"
  
  
  def test_weakref():
      """Classes with __slots__ are incompatible with the weakref module unless they
      explicitly state __weakref__ among their slots
      """
      from weakref import ref
  
      ds = Dataset()
      r = ref(ds)
      assert r() is ds
  