35
35
)
36
36
37
37
38
+ class AlignmentError (ValueError ):
39
+ """Error class for alignment failures due to incompatible arguments."""
40
+
41
+
38
42
def reindex_variables (
39
43
variables : Mapping [Any , Variable ],
40
44
dim_pos_indexers : Mapping [Any , Any ],
@@ -196,7 +200,7 @@ def _normalize_indexes(
196
200
for k , idx in indexes .items ():
197
201
if not isinstance (idx , Index ):
198
202
if getattr (idx , "dims" , (k ,)) != (k ,):
199
- raise ValueError (
203
+ raise AlignmentError (
200
204
f"Indexer has dimensions { idx .dims } that are different "
201
205
f"from that to be indexed along '{ k } '"
202
206
)
@@ -227,7 +231,7 @@ def _normalize_indexes(
227
231
elif exclude_dims :
228
232
excl_dims_str = ", " .join (str (d ) for d in exclude_dims )
229
233
incl_dims_str = ", " .join (str (d ) for d in all_dims - exclude_dims )
230
- raise ValueError (
234
+ raise AlignmentError (
231
235
f"cannot exclude dimension(s) { excl_dims_str } from alignment because "
232
236
"these are used by an index together with non-excluded dimensions "
233
237
f"{ incl_dims_str } "
@@ -268,7 +272,7 @@ def find_matching_indexes(self) -> None:
268
272
for dim_sizes in all_indexes_dim_sizes .values ():
269
273
for dim , sizes in dim_sizes .items ():
270
274
if len (sizes ) > 1 :
271
- raise ValueError (
275
+ raise AlignmentError (
272
276
"cannot align objects with join='override' with matching indexes "
273
277
f"along dimension { dim !r} that don't have the same size"
274
278
)
@@ -283,47 +287,6 @@ def find_matching_unindexed_dims(self) -> None:
283
287
284
288
self .unindexed_dim_sizes = unindexed_dim_sizes
285
289
286
- def assert_no_index_conflict (self ) -> None :
287
- """Check for uniqueness of both coordinate and dimension names across all sets
288
- of matching indexes.
289
-
290
- We need to make sure that all indexes used for re-indexing or alignment
291
- are fully compatible and do not conflict each other.
292
-
293
- Note: perhaps we could choose less restrictive constraints and instead
294
- check for conflicts among the dimension (position) indexers returned by
295
- `Index.reindex_like()` for each matching pair of object index / aligned
296
- index?
297
- (ref: https://github.com/pydata/xarray/issues/1603#issuecomment-442965602)
298
-
299
- """
300
- matching_keys = set (self .all_indexes ) | set (self .indexes )
301
-
302
- coord_count : dict [Hashable , int ] = defaultdict (int )
303
- dim_count : dict [Hashable , int ] = defaultdict (int )
304
- for coord_names_dims , _ in matching_keys :
305
- dims_set : set [Hashable ] = set ()
306
- for name , dims in coord_names_dims :
307
- coord_count [name ] += 1
308
- dims_set .update (dims )
309
- for dim in dims_set :
310
- dim_count [dim ] += 1
311
-
312
- for count , msg in [(coord_count , "coordinates" ), (dim_count , "dimensions" )]:
313
- dup = {k : v for k , v in count .items () if v > 1 }
314
- if dup :
315
- items_msg = ", " .join (
316
- f"{ k !r} ({ v } conflicting indexes)" for k , v in dup .items ()
317
- )
318
- raise ValueError (
319
- "cannot re-index or align objects with conflicting indexes found for "
320
- f"the following { msg } : { items_msg } \n "
321
- "Conflicting indexes may occur when\n "
322
- "- they relate to different sets of coordinate and/or dimension names\n "
323
- "- they don't have the same type\n "
324
- "- they may be used to reindex data along common dimensions"
325
- )
326
-
327
290
def _need_reindex (self , dim , cmp_indexes ) -> bool :
328
291
"""Whether or not we need to reindex variables for a set of
329
292
matching indexes.
@@ -383,11 +346,33 @@ def _get_index_joiner(self, index_cls) -> Callable:
383
346
def align_indexes (self ) -> None :
384
347
"""Compute all aligned indexes and their corresponding coordinate variables."""
385
348
386
- aligned_indexes = {}
387
- aligned_index_vars = {}
388
- reindex = {}
389
- new_indexes = {}
390
- new_index_vars = {}
349
+ aligned_indexes : dict [MatchingIndexKey , Index ] = {}
350
+ aligned_index_vars : dict [MatchingIndexKey , dict [Hashable , Variable ]] = {}
351
+ reindex : dict [MatchingIndexKey , bool ] = {}
352
+ new_indexes : dict [Hashable , Index ] = {}
353
+ new_index_vars : dict [Hashable , Variable ] = {}
354
+
355
+ def update_dicts (
356
+ key : MatchingIndexKey ,
357
+ idx : Index ,
358
+ idx_vars : dict [Hashable , Variable ],
359
+ need_reindex : bool ,
360
+ ):
361
+ reindex [key ] = need_reindex
362
+ aligned_indexes [key ] = idx
363
+ aligned_index_vars [key ] = idx_vars
364
+
365
+ for name , var in idx_vars .items ():
366
+ if name in new_indexes :
367
+ other_idx = new_indexes [name ]
368
+ other_var = new_index_vars [name ]
369
+ raise AlignmentError (
370
+ f"cannot align objects on coordinate { name !r} because of conflicting indexes\n "
371
+ f"first index: { idx !r} \n second index: { other_idx !r} \n "
372
+ f"first variable: { var !r} \n second variable: { other_var !r} \n "
373
+ )
374
+ new_indexes [name ] = idx
375
+ new_index_vars [name ] = var
391
376
392
377
for key , matching_indexes in self .all_indexes .items ():
393
378
matching_index_vars = self .all_index_vars [key ]
@@ -419,7 +404,7 @@ def align_indexes(self) -> None:
419
404
need_reindex = False
420
405
if need_reindex :
421
406
if self .join == "exact" :
422
- raise ValueError (
407
+ raise AlignmentError (
423
408
"cannot align objects with join='exact' where "
424
409
"index/labels/sizes are not equal along "
425
410
"these coordinates (dimensions): "
@@ -437,25 +422,14 @@ def align_indexes(self) -> None:
437
422
joined_index = matching_indexes [0 ]
438
423
joined_index_vars = matching_index_vars [0 ]
439
424
440
- reindex [key ] = need_reindex
441
- aligned_indexes [key ] = joined_index
442
- aligned_index_vars [key ] = joined_index_vars
443
-
444
- for name , var in joined_index_vars .items ():
445
- new_indexes [name ] = joined_index
446
- new_index_vars [name ] = var
425
+ update_dicts (key , joined_index , joined_index_vars , need_reindex )
447
426
448
427
# Explicitly provided indexes that are not found in objects to align
449
428
# may relate to unindexed dimensions so we add them too
450
429
for key , idx in self .indexes .items ():
451
430
if key not in aligned_indexes :
452
431
index_vars = self .index_vars [key ]
453
- reindex [key ] = False
454
- aligned_indexes [key ] = idx
455
- aligned_index_vars [key ] = index_vars
456
- for name , var in index_vars .items ():
457
- new_indexes [name ] = idx
458
- new_index_vars [name ] = var
432
+ update_dicts (key , idx , index_vars , False )
459
433
460
434
self .aligned_indexes = aligned_indexes
461
435
self .aligned_index_vars = aligned_index_vars
@@ -474,7 +448,7 @@ def assert_unindexed_dim_sizes_equal(self) -> None:
474
448
else :
475
449
add_err_msg = ""
476
450
if len (sizes ) > 1 :
477
- raise ValueError (
451
+ raise AlignmentError (
478
452
f"cannot reindex or align along dimension { dim !r} "
479
453
f"because of conflicting dimension sizes: { sizes !r} " + add_err_msg
480
454
)
@@ -502,14 +476,25 @@ def _get_dim_pos_indexers(
502
476
self ,
503
477
matching_indexes : dict [MatchingIndexKey , Index ],
504
478
) -> dict [Hashable , Any ]:
505
- dim_pos_indexers = {}
479
+ dim_pos_indexers : dict [Hashable , Any ] = {}
480
+ dim_index : dict [Hashable , Index ] = {}
506
481
507
482
for key , aligned_idx in self .aligned_indexes .items ():
508
483
obj_idx = matching_indexes .get (key )
509
484
if obj_idx is not None :
510
485
if self .reindex [key ]:
511
486
indexers = obj_idx .reindex_like (aligned_idx , ** self .reindex_kwargs )
512
- dim_pos_indexers .update (indexers )
487
+ for dim , idxer in indexers .items ():
488
+ if dim in dim_pos_indexers and not np .array_equal (
489
+ idxer , dim_pos_indexers [dim ]
490
+ ):
491
+ raise AlignmentError (
492
+ f"cannot reindex or align along dimension { dim !r} because "
493
+ "of conflicting re-indexers returned by multiple indexes\n "
494
+ f"first index: { obj_idx !r} \n second index: { dim_index [dim ]!r} \n "
495
+ )
496
+ dim_pos_indexers [dim ] = idxer
497
+ dim_index [dim ] = obj_idx
513
498
514
499
return dim_pos_indexers
515
500
@@ -571,7 +556,6 @@ def align(self) -> None:
571
556
572
557
self .find_matching_indexes ()
573
558
self .find_matching_unindexed_dims ()
574
- self .assert_no_index_conflict ()
575
559
self .align_indexes ()
576
560
self .assert_unindexed_dim_sizes_equal ()
577
561
@@ -735,7 +719,7 @@ def align(
735
719
736
720
Raises
737
721
------
738
- ValueError
722
+ AlignmentError
739
723
If any dimensions without labels on the arguments have different sizes,
740
724
or a different size than the size of the aligned dimension labels.
741
725
@@ -853,7 +837,7 @@ def align(
853
837
>>> a, b = xr.align(x, y, join="exact")
854
838
Traceback (most recent call last):
855
839
...
856
- ValueError : cannot align objects with join='exact' ...
840
+ xarray.structure.alignment.AlignmentError : cannot align objects with join='exact' ...
857
841
858
842
>>> a, b = xr.align(x, y, join="override")
859
843
>>> a
0 commit comments