Skip to content

Commit 7a7437e

Browse files
committed
If join='exact', raise an error for non-aligned objects
`align` now supports ``join='exact'``, which raises `xarray.AlignmentError` instead of aligning when indexes to be aligned are not equal. This is useful for asserting that objects are identical instead of aligning in xarray operations. For example: ds1 = xarray.Dataset({'x': [0, 1]}) ds2 = xarray.Dataset({'x': [1, 2]}) xarray.merge([ds1, ds2], join='exact') # AlignmentError: indexes along dimension 'x' are not equal
1 parent 31b46dc commit 7a7437e

10 files changed

Lines changed: 61 additions & 4 deletions

File tree

‎doc/api.rst‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,15 @@ Testing
474474
testing.assert_identical
475475
testing.assert_allclose
476476

477+
Exceptions
478+
==========
479+
480+
.. autosummary::
481+
:toctree: generated/
482+
483+
AlignmentError
484+
MergeError
485+
477486
Advanced API
478487
============
479488

‎doc/whats-new.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ Enhancements
3030
specified with `x=` or `y=`, the other dimension is now guessed. By
3131
`Vincent Noel <https://github.com/vnoel>`_.
3232

33+
- :py:func:`~xarray.align` now supports ``join='exact'``, which raises
34+
:py:class:`~xarray.AlignmentError` instead of aligning when indexes to be
35+
aligned are not equal.
36+
By `Stephan Hoyer <https://github.com/shoyer>`_.
37+
3338
Added new method :py:meth:`~Dataset.assign_attrs` to ``DataArray`` and
3439
``Dataset``, a chained-method compatible implementation of the
3540
``dict.update`` method on attrs (:issue:`1281`).

‎xarray/__init__.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
# flake8: noqa
12
from __future__ import absolute_import
23
from __future__ import division
34
from __future__ import print_function
4-
from .core.alignment import align, broadcast, broadcast_arrays
5+
from .core.alignment import AlignmentError, align, broadcast, broadcast_arrays
56
from .core.common import full_like, zeros_like, ones_like
67
from .core.combine import concat, auto_combine
78
from .core.extensions import (register_dataarray_accessor,

‎xarray/core/alignment.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
from .variable import Variable, IndexVariable
1616

1717

18+
class AlignmentError(Exception):
19+
"""Error for when an operation cannot be done with non-aligned objects."""
20+
21+
1822
def _get_joiner(join):
1923
if join == 'outer':
2024
return functools.partial(functools.reduce, operator.or_)
@@ -24,6 +28,10 @@ def _get_joiner(join):
2428
return operator.itemgetter(0)
2529
elif join == 'right':
2630
return operator.itemgetter(-1)
31+
elif join == 'exact':
32+
# We cannot return a function to "align" in this case, because it needs
33+
# access to the dimension name to give a good error message.
34+
return None
2735
else:
2836
raise ValueError('invalid value for join: %s' % join)
2937

@@ -47,13 +55,15 @@ def align(*objects, **kwargs):
4755
----------
4856
*objects : Dataset or DataArray
4957
Objects to align.
50-
join : {'outer', 'inner', 'left', 'right'}, optional
58+
join : {'outer', 'inner', 'left', 'right', 'exact'}, optional
5159
Method for joining the indexes of the passed objects along each
5260
dimension:
5361
- 'outer': use the union of object indexes
5462
- 'inner': use the intersection of object indexes
5563
- 'left': use indexes from the first object with each dimension
5664
- 'right': use indexes from the last object with each dimension
65+
- 'exact': raise `xarray.AlignmentError` instead of aligning when
66+
indexes to be aligned are not equal
5767
copy : bool, optional
5868
If ``copy=True``, data in the return values is always copied. If
5969
``copy=False`` and reindexing is unnecessary, or can be performed with
@@ -120,6 +130,10 @@ def align(*objects, **kwargs):
120130
if (any(not matching_indexes[0].equals(other)
121131
for other in matching_indexes[1:]) or
122132
dim in unlabeled_dim_sizes):
133+
if join == 'exact':
134+
raise AlignmentError(
135+
'indexes along dimension {!r} are not equal'
136+
.format(dim))
123137
index = joiner(matching_indexes)
124138
joined_indexes[dim] = index
125139
else:

‎xarray/core/computation.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,8 @@ def apply_ufunc(func, *args, **kwargs):
599599
- 'inner': use the intersection of object indexes
600600
- 'left': use indexes from the first object with each dimension
601601
- 'right': use indexes from the last object with each dimension
602+
- 'exact': raise `xarray.AlignmentError` instead of aligning when
603+
indexes to be aligned are not equal
602604
dataset_join : {'outer', 'inner', 'left', 'right'}, optional
603605
Method for joining variables of Dataset objects with mismatched
604606
data variables.

‎xarray/core/dataset.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1833,13 +1833,14 @@ def merge(self, other, inplace=False, overwrite_vars=frozenset(),
18331833
- 'no_conflicts': only values which are not null in both datasets
18341834
must be equal. The returned dataset then contains the combination
18351835
of all non-null values.
1836-
join : {'outer', 'inner', 'left', 'right'}, optional
1836+
join : {'outer', 'inner', 'left', 'right', 'exact'}, optional
18371837
Method for joining ``self`` and ``other`` along shared dimensions:
18381838
18391839
- 'outer': use the union of the indexes
18401840
- 'inner': use the intersection of the indexes
18411841
- 'left': use indexes from ``self``
18421842
- 'right': use indexes from ``other``
1843+
- 'exact': error instead of aligning non-equal indexes
18431844
18441845
Returns
18451846
-------

‎xarray/core/merge.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def merge(objects, compat='no_conflicts', join='outer'):
457457
- 'no_conflicts': only values which are not null in both datasets
458458
must be equal. The returned dataset then contains the combination
459459
of all non-null values.
460-
join : {'outer', 'inner', 'left', 'right'}, optional
460+
join : {'outer', 'inner', 'left', 'right', 'exact'}, optional
461461
How to combine objects with different indexes.
462462
463463
Returns

‎xarray/core/ops.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,20 +286,28 @@ def fillna(data, other, join="left", dataset_join="left"):
286286
287287
Parameters
288288
----------
289+
other : Dataset, dict, DataArray, Variable, numpy/dask array or scalar
290+
Source of fill values to use to replace NaN.
289291
join : {'outer', 'inner', 'left', 'right'}, optional
290292
Method for joining the indexes of the passed objects along each
291293
dimension
292294
- 'outer': use the union of object indexes
293295
- 'inner': use the intersection of object indexes
294296
- 'left': use indexes from the first object with each dimension
295297
- 'right': use indexes from the last object with each dimension
298+
- 'exact': raise `xarray.AlignmentError` instead of aligning when
299+
indexes to be aligned are not equal
296300
dataset_join : {'outer', 'inner', 'left', 'right'}, optional
297301
Method for joining variables of Dataset objects with mismatched
298302
data variables.
299303
- 'outer': take variables from both Dataset objects
300304
- 'inner': take only overlapped variables
301305
- 'left': take only variables from the first object
302306
- 'right': take only variables from the last object
307+
308+
Returns
309+
-------
310+
Object of same type as this object with missing values filled.
303311
"""
304312
from .computation import apply_ufunc
305313

‎xarray/tests/test_dataset.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,17 @@ def test_align(self):
12391239
with self.assertRaises(TypeError):
12401240
align(left, right, foo='bar')
12411241

1242+
def test_align_exact(self):
1243+
left = xr.Dataset(coords={'x': [0, 1]})
1244+
right = xr.Dataset(coords={'x': [1, 2]})
1245+
1246+
left1, left2 = xr.align(left, left, join='exact')
1247+
self.assertDatasetIdentical(left1, left)
1248+
self.assertDatasetIdentical(left2, left)
1249+
1250+
with self.assertRaises(xr.AlignmentError):
1251+
xr.align(left, right, join='exact')
1252+
12421253
def test_align_exclude(self):
12431254
x = Dataset({'foo': DataArray([[1, 2], [3, 4]], dims=['x', 'y'],
12441255
coords={'x': [1, 2], 'y': [3, 4]})})

‎xarray/tests/test_merge.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ def test_merge_error(self):
6363
with self.assertRaises(xr.MergeError):
6464
xr.merge([ds, ds + 1])
6565

66+
def test_merge_alignment_error(self):
67+
ds = xr.Dataset(coords={'x': [1, 2]})
68+
other = xr.Dataset(coords={'x': [2, 3]})
69+
with self.assertRaises(xr.AlignmentError):
70+
xr.merge([ds, other], join='exact')
71+
6672
def test_merge_no_conflicts_single_var(self):
6773
ds1 = xr.Dataset({'a': ('x', [1, 2]), 'x': [0, 1]})
6874
ds2 = xr.Dataset({'a': ('x', [2, 3]), 'x': [1, 2]})

0 commit comments

Comments
 (0)