Repository: pydata/xarray

Issue:
Test for variable name in coords True after xr.merge with compat="minimal"
### What happened?

Merging two `DataArray`s with `compat="minimal"` drops the conflicting variable from coords but the test if that variable is in coords results to `True` even though the variable gets dropped.

### What did you expect to happen?

The test should be `False`

### Minimal Complete Verifiable Example

```Python
import numpy as np
import xarray as xr

da1 = xr.DataArray(np.zeros((2, 2)), dims=("y", "x"), name="1")
c1 = ("y", ["1", "2"])  
da1["c1"] = c1

da2 = xr.DataArray(np.zeros((2, 2)), dims=("y", "x"), name="2")
c2 = ("y", ["3", "4"])
da2["c1"] = c2

xrds = xr.merge([da1, da2], compat="minimal")

"c1" in xrds.coords # results to True but should be False
```


### MVCE confirmation

- [X] Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
- [X] Complete example — the example is self-contained, including all data and the text of any traceback.
- [X] Verifiable example — the example copy & pastes into an IPython prompt or [Binder notebook](https://mybinder.org/v2/gh/pydata/xarray/main?urlpath=lab/tree/doc/examples/blank_template.ipynb), returning the result.
- [X] New issue — a search of GitHub Issues suggests this is not a duplicate.

### Relevant log output

_No response_

### Anything else we need to know?

_No response_

### Environment

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.10.5 | packaged by conda-forge | (main, Jun 14 2022, 07:06:46) [GCC 10.3.0]
python-bits: 64
OS: Linux
OS-release: 5.10.0-1049-oem
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: ('en_US', 'UTF-8')
libhdf5: 1.12.1
libnetcdf: 4.8.1

xarray: 2022.12.0
pandas: 1.4.2
numpy: 1.22.3
scipy: 1.8.0
netCDF4: 1.5.8
pydap: None
h5netcdf: 1.0.0
h5py: 3.6.0
Nio: None
zarr: 2.11.3
cftime: 1.6.0
nc_time_axis: None
PseudoNetCDF: None
rasterio: 1.2.10
cfgrib: None
iris: None
bottleneck: 1.3.5
dask: 2022.05.0
distributed: 2022.5.0
matplotlib: 3.5.2
cartopy: 0.20.2
seaborn: None
numbagg: None
fsspec: 2022.8.2
cupy: None
pint: 0.19.2
sparse: None
flox: 0.6.5
numpy_groupies: 0.9.20
setuptools: 62.2.0
pip: 22.1
conda: None
pytest: 7.1.2
mypy: None
IPython: 8.3.0
sphinx: 4.5.0
</details>


Discussion:

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 3eacbce2..3d20af80 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -43,6 +43,10 @@ Bug fixes
 - Improved handling of multi-coordinate indexes when updating coordinates, including bug fixes
   (and improved warnings for deprecated features) for pandas multi-indexes (:pull:`8094`).
   By `Benoît Bovy <https://github.com/benbovy>`_.
+- Fixed a bug in :py:func:`merge` with ``compat='minimal'`` where the coordinate
+  names were not updated properly internally (:issue:`7405`, :issue:`7588`,
+  :pull:`8104`).
+  By `Benoît Bovy <https://github.com/benbovy>`_.
 
 Documentation
 ~~~~~~~~~~~~~
diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py
index 9a53f24c..f1a0cb9d 100644
--- a/xarray/core/dataset.py
+++ b/xarray/core/dataset.py
@@ -438,7 +438,9 @@ class DataVariables(Mapping[Any, "DataArray"]):
         )
 
     def __len__(self) -> int:
-        return len(self._dataset._variables) - len(self._dataset._coord_names)
+        length = len(self._dataset._variables) - len(self._dataset._coord_names)
+        assert length >= 0, "something is wrong with Dataset._coord_names"
+        return length
 
     def __contains__(self, key: Hashable) -> bool:
         return key in self._dataset._variables and key not in self._dataset._coord_names
@@ -4698,7 +4700,9 @@ class Dataset(
             if len(var_names) == 1 and (not append or dim not in self._indexes):
                 var_name = var_names[0]
                 var = self._variables[var_name]
-                if var.dims != (dim,):
+                # an error with a better message will be raised for scalar variables
+                # when creating the PandasIndex
+                if var.ndim > 0 and var.dims != (dim,):
                     raise ValueError(
                         f"dimension mismatch: try setting an index for dimension {dim!r} with "
                         f"variable {var_name!r} that has dimensions {var.dims}"
diff --git a/xarray/core/indexes.py b/xarray/core/indexes.py
index 7de290f4..b5e39696 100644
--- a/xarray/core/indexes.py
+++ b/xarray/core/indexes.py
@@ -616,7 +616,14 @@ class PandasIndex(Index):
 
         name, var = next(iter(variables.items()))
 
-        if var.ndim != 1:
+        if var.ndim == 0:
+            raise ValueError(
+                f"cannot set a PandasIndex from the scalar variable {name!r}, "
+                "only 1-dimensional variables are supported. "
+                f"Note: you might want to use `obj.expand_dims({name!r})` to create a "
+                f"new dimension and turn {name!r} as an indexed dimension coordinate."
+            )
+        elif var.ndim != 1:
             raise ValueError(
                 "PandasIndex only accepts a 1-dimensional variable, "
                 f"variable {name!r} has {var.ndim} dimensions"
diff --git a/xarray/core/merge.py b/xarray/core/merge.py
index 24b6ed0b..3475db4a 100644
--- a/xarray/core/merge.py
+++ b/xarray/core/merge.py
@@ -723,6 +723,9 @@ def merge_core(
     dims = calculate_dimensions(variables)
 
     coord_names, noncoord_names = determine_coords(coerced)
+    if compat == "minimal":
+        # coordinates may be dropped in merged results
+        coord_names.intersection_update(variables)
     if explicit_coords is not None:
         assert_valid_explicit_coords(variables, dims, explicit_coords)
         coord_names.update(explicit_coords)


Test patch:
diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py
index 3110c4e2..c31f3821 100644
--- a/xarray/tests/test_dataset.py
+++ b/xarray/tests/test_dataset.py
@@ -1046,6 +1046,17 @@ class TestDataset:
             "bar": np.dtype("float64"),
         }
 
+        # len
+        ds.coords["x"] = [1]
+        assert len(ds.data_vars) == 2
+
+        # https://github.com/pydata/xarray/issues/7588
+        with pytest.raises(
+            AssertionError, match="something is wrong with Dataset._coord_names"
+        ):
+            ds._coord_names = {"w", "x", "y", "z"}
+            len(ds.data_vars)
+
     def test_equals_and_identical(self) -> None:
         data = create_test_data(seed=42)
         assert data.equals(data)
@@ -3397,6 +3408,12 @@ class TestDataset:
         with pytest.raises(ValueError, match=r"dimension mismatch.*"):
             ds.set_index(y="x_var")
 
+        ds = Dataset(coords={"x": 1})
+        with pytest.raises(
+            ValueError, match=r".*cannot set a PandasIndex.*scalar variable.*"
+        ):
+            ds.set_index(x="x")
+
     def test_set_index_deindexed_coords(self) -> None:
         # test de-indexed coordinates are converted to base variable
         # https://github.com/pydata/xarray/issues/6969
diff --git a/xarray/tests/test_indexes.py b/xarray/tests/test_indexes.py
index ebe9f3fb..05d74854 100644
--- a/xarray/tests/test_indexes.py
+++ b/xarray/tests/test_indexes.py
@@ -145,6 +145,11 @@ class TestPandasIndex:
         with pytest.raises(ValueError, match=r".*only accepts one variable.*"):
             PandasIndex.from_variables({"x": var, "foo": var2}, options={})
 
+        with pytest.raises(
+            ValueError, match=r".*cannot set a PandasIndex.*scalar variable.*"
+        ):
+            PandasIndex.from_variables({"foo": xr.Variable((), 1)}, options={})
+
         with pytest.raises(
             ValueError, match=r".*only accepts a 1-dimensional variable.*"
         ):
diff --git a/xarray/tests/test_merge.py b/xarray/tests/test_merge.py
index 63449708..c6597d5a 100644
--- a/xarray/tests/test_merge.py
+++ b/xarray/tests/test_merge.py
@@ -382,6 +382,16 @@ class TestMergeMethod:
 
         assert ds1.identical(ds1.merge(ds2, compat="override"))
 
+    def test_merge_compat_minimal(self) -> None:
+        # https://github.com/pydata/xarray/issues/7405
+        # https://github.com/pydata/xarray/issues/7588
+        ds1 = xr.Dataset(coords={"foo": [1, 2, 3], "bar": 4})
+        ds2 = xr.Dataset(coords={"foo": [1, 2, 3], "bar": 5})
+
+        actual = xr.merge([ds1, ds2], compat="minimal")
+        expected = xr.Dataset(coords={"foo": [1, 2, 3]})
+        assert_identical(actual, expected)
+
     def test_merge_auto_align(self):
         ds1 = xr.Dataset({"a": ("x", [1, 2]), "x": [0, 1]})
         ds2 = xr.Dataset({"b": ("x", [3, 4]), "x": [1, 2]})