diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 86d47cdb875..9b762ba0a58 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -19,7 +19,8 @@ from .dataset import Dataset from .pycompat import iteritems, basestring, OrderedDict, zip from .variable import (as_variable, Variable, as_compatible_data, Coordinate, - default_index_coordinate) + default_index_coordinate, + assert_unique_multiindex_level_names) from .formatting import format_item @@ -81,14 +82,7 @@ def _infer_coords_and_dims(shape, coords, dims): 'length %s on the data but length %s on ' 'coordinate %r' % (d, sizes[d], s, k)) - if v.ndim == 1: - idx_level_names = v.to_coord().level_names or [] - for n in idx_level_names: - if n in level_names: - raise ValueError('found duplicate MultiIndex level ' - 'name %r for coordinates %r and %r' - % (n, k, level_names[n])) - level_names[n] = k + assert_unique_multiindex_level_names(new_coords) return new_coords, dims diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index fd6fdbaff0c..c9cd2aee551 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -224,7 +224,6 @@ def __init__(self, data_vars=None, coords=None, attrs=None, coords = {} if data_vars is not None or coords is not None: self._set_init_vars_and_dims(data_vars, coords, compat) - self._check_multiindex_level_names() if attrs is not None: self.attrs = attrs self._initialized = True @@ -251,21 +250,6 @@ def _set_init_vars_and_dims(self, data_vars, coords, compat): self._coord_names = coord_names self._dims = dims - def _check_multiindex_level_names(self): - """Check for uniqueness of MultiIndex level names - """ - level_names = {} - for c in self._coord_names: - v = self._variables[c] - if v.ndim == 1 and v._in_memory: - idx_level_names = v.to_coord().level_names or [] - for n in idx_level_names: - if n in level_names: - raise ValueError('found duplicate MultiIndex level ' - 'name %r for coordinates %r and %r' - % (n, c, level_names[n])) - level_names[n] = c - @classmethod def load_store(cls, store, decoder=None): """Create a new dataset from the contents of a backends.*DataStore diff --git a/xarray/core/merge.py b/xarray/core/merge.py index 7c3dc196a21..96634bd3186 100644 --- a/xarray/core/merge.py +++ b/xarray/core/merge.py @@ -2,7 +2,8 @@ from .alignment import deep_align from .utils import Frozen -from .variable import as_variable, default_index_coordinate +from .variable import (as_variable, default_index_coordinate, + assert_unique_multiindex_level_names) from .pycompat import (basestring, OrderedDict) @@ -110,7 +111,7 @@ def merge_variables( If provided, variables are always taken from this dict in preference to the input variable dictionaries, without checking for conflicts. compat : {'identical', 'equals', 'broadcast_equals', 'minimal'}, optional - Type of equality check to use wben checking for conflicts. + Type of equality check to use when checking for conflicts. Returns ------- @@ -278,6 +279,7 @@ def merge_coords_only(objs, priority_vars=None): """ expanded = expand_variable_dicts(objs) variables = merge_variables(expanded, priority_vars) + assert_unique_multiindex_level_names(variables) return variables @@ -319,6 +321,7 @@ def align_and_merge_coords(objs, compat='minimal', join='outer', expanded = expand_variable_dicts(aligned) priority_vars = _get_priority_vars(aligned, priority_arg, compat=compat) variables = merge_variables(expanded, priority_vars, compat=compat) + assert_unique_multiindex_level_names(variables) return variables @@ -380,6 +383,7 @@ def merge_core(objs, compat='broadcast_equals', join='outer', priority_arg=None, priority_vars = _get_priority_vars(aligned, priority_arg, compat=compat) variables = merge_variables(expanded, priority_vars, compat=compat) + assert_unique_multiindex_level_names(variables) dims = calculate_dimensions(variables) diff --git a/xarray/core/variable.py b/xarray/core/variable.py index 7ee6f697a70..6f9f9fecea9 100644 --- a/xarray/core/variable.py +++ b/xarray/core/variable.py @@ -1,4 +1,5 @@ from datetime import timedelta +from collections import defaultdict import functools import itertools import warnings @@ -1297,3 +1298,28 @@ def concat(variables, dim='concat_dim', positions=None, shortcut=False): return Coordinate.concat(variables, dim, positions, shortcut) else: return Variable.concat(variables, dim, positions, shortcut) + + +def assert_unique_multiindex_level_names(variables): + """Check for uniqueness of MultiIndex level names in all given + variables. + + Not public API. Used for checking consistency of DataArray and Dataset + objects. + """ + level_names = defaultdict(list) + for var_name, var in variables.items(): + if isinstance(var._data, PandasIndexAdapter): + idx_level_names = var.to_coord().level_names + if idx_level_names is not None: + for n in idx_level_names: + level_names[n].append(var_name) + + duplicate_level_names = {k: v for k, v in level_names.items() + if len(v) > 1} + if duplicate_level_names: + duplicate_str = '\n'.join(['level %r found in %s' + % (k, ' and '.join(v)) + for k, v in duplicate_level_names.items()]) + raise ValueError('conflicting MultiIndex level names:\n%s' + % duplicate_str)