Repository: pydata/xarray

Issue:
Confusing behaviour with MultiIndex
`Dataset` allows assignment of new variables with dimension names that are used in a MultiIndex, even if the lengths do not match the existing coordinate.

```python
a = pd.DataFrame({'a': [1, 2], 'b': [3, 4]}).unstack('a')
a.index.names = ['dim0', 'dim1']
a.index.name = 'stacked_dim'

b = xr.Dataset(coords={'dim0': ['a', 'b'], 'dim1': [0, 1]})
b = b.stack(dim_stacked=['dim0', 'dim1'])
assert(len(b.dim0) == 4)

# This should raise an errors because the length is != 4
b['c'] = (('dim0',), [10, 11])
b
```
Instead, it reports `dim0` as a new dimension without coordinates:
```
<xarray.Dataset>
Dimensions:      (dim0: 2, dim_stacked: 4)
Coordinates:
  * dim_stacked  (dim_stacked) MultiIndex
  - dim0         (dim_stacked) object 'a' 'a' 'b' 'b'
  - dim1         (dim_stacked) int64 0 1 0 1
Dimensions without coordinates: dim0
Data variables:
    c            (dim0) int64 10 11
```

Similar cases of coordinates that aren't used do raise an error:
```python
ds = xr.Dataset()
ds.coords['a'] = [1, 2, 3]
ds = ds.sel(a=1)
ds['b'] = (('a',), [1, 2])
ds
```

#### Output of ``xr.show_versions()``

<details>

```
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.5.final.0
python-bits: 64
OS: Darwin
OS-release: 17.7.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: en_GB.UTF-8
LANG: None
LOCALE: en_GB.UTF-8

xarray: 0.10.7
pandas: 0.23.2
numpy: 1.14.5
scipy: 1.1.0
netCDF4: 1.4.0
h5netcdf: None
h5py: 2.8.0
Nio: None
zarr: None
bottleneck: 1.2.1
cyordereddict: None
dask: 0.18.1
distributed: 1.22.0
matplotlib: 2.2.2
cartopy: None
seaborn: 0.8.1
setuptools: 39.2.0
pip: 10.0.1
conda: 4.5.8
pytest: 3.6.2
IPython: 6.4.0
sphinx: 1.7.5
```

</details>


Discussion:

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 3641a072..0c62cca6 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -61,10 +61,16 @@ Bug fixes
   attribute being set.
   (:issue:`2201`)
   By `Thomas Voigt <https://github.com/tv3141>`_.
+
 - Tests can be run in parallel with pytest-xdist
-- Follow up the renamings in dask; from dask.ghost to dask.overlap
+  By `Tony Tung <https://github.com/ttung>`_.
+
+- Now raises a ValueError when there is a conflict between dimension names and
+  level names of MultiIndex. (:issue:`2299`)
   By `Keisuke Fujii <https://github.com/fujiisoup>`_.
 
+- Follow up the renamings in dask; from dask.ghost to dask.overlap
+  By `Keisuke Fujii <https://github.com/fujiisoup>`_.
 
 - Now :py:func:`xr.apply_ufunc` raises a ValueError when the size of
 ``input_core_dims`` is inconsistent with the number of arguments.
diff --git a/xarray/core/variable.py b/xarray/core/variable.py
index 8b6a3d3b..d9772407 100644
--- a/xarray/core/variable.py
+++ b/xarray/core/variable.py
@@ -1876,12 +1876,15 @@ def assert_unique_multiindex_level_names(variables):
     objects.
     """
     level_names = defaultdict(list)
+    all_level_names = set()
     for var_name, var in variables.items():
         if isinstance(var._data, PandasIndexAdapter):
             idx_level_names = var.to_index_variable().level_names
             if idx_level_names is not None:
                 for n in idx_level_names:
                     level_names[n].append('%r (%s)' % (n, var_name))
+            if idx_level_names:
+                all_level_names.update(idx_level_names)
 
     for k, v in level_names.items():
         if k in variables:
@@ -1892,3 +1895,9 @@ def assert_unique_multiindex_level_names(variables):
         conflict_str = '\n'.join([', '.join(v) for v in duplicate_names])
         raise ValueError('conflicting MultiIndex level name(s):\n%s'
                          % conflict_str)
+    # Check confliction between level names and dimensions GH:2299
+    for k, v in variables.items():
+        for d in v.dims:
+            if d in all_level_names:
+                raise ValueError('conflicting level / dimension names. {} '
+                                 'already exists as a level name.'.format(d))


Test patch:
diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py
index c0516ed7..08d71d46 100644
--- a/xarray/tests/test_dataset.py
+++ b/xarray/tests/test_dataset.py
@@ -2456,6 +2456,18 @@ class TestDataset(TestCase):
         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