13
13
import numpy as np
14
14
import pandas as pd
15
15
16
+ from .duck_array_ops import array_equiv
16
17
from .options import OPTIONS
17
18
from .pycompat import (
18
19
PY2 , bytes_type , dask_array_type , unicode_type , zip_longest )
@@ -411,6 +412,15 @@ def short_dask_repr(array, show_dtype=True):
411
412
return 'dask.array<shape=%s, chunksize=%s>' % (array .shape , chunksize )
412
413
413
414
415
+ def short_data_repr (array ):
416
+ if isinstance (getattr (array , 'variable' , array )._data , dask_array_type ):
417
+ return short_dask_repr (array )
418
+ elif array ._in_memory or array .size < 1e5 :
419
+ return short_array_repr (array .values )
420
+ else :
421
+ return u'[%s values with dtype=%s]' % (array .size , array .dtype )
422
+
423
+
414
424
def array_repr (arr ):
415
425
# used for DataArray, Variable and IndexVariable
416
426
if hasattr (arr , 'name' ) and arr .name is not None :
@@ -421,12 +431,7 @@ def array_repr(arr):
421
431
summary = [u'<xarray.%s %s(%s)>'
422
432
% (type (arr ).__name__ , name_str , dim_summary (arr ))]
423
433
424
- if isinstance (getattr (arr , 'variable' , arr )._data , dask_array_type ):
425
- summary .append (short_dask_repr (arr ))
426
- elif arr ._in_memory or arr .size < 1e5 :
427
- summary .append (short_array_repr (arr .values ))
428
- else :
429
- summary .append (u'[%s values with dtype=%s]' % (arr .size , arr .dtype ))
434
+ summary .append (short_data_repr (arr ))
430
435
431
436
if hasattr (arr , 'coords' ):
432
437
if arr .coords :
@@ -463,3 +468,132 @@ def dataset_repr(ds):
463
468
summary .append (attrs_repr (ds .attrs ))
464
469
465
470
return u'\n ' .join (summary )
471
+
472
+
473
+ def diff_dim_summary (a , b ):
474
+ if a .dims != b .dims :
475
+ return "Differing dimensions:\n ({}) != ({})" .format (
476
+ dim_summary (a ), dim_summary (b ))
477
+ else :
478
+ return ""
479
+
480
+
481
+ def _diff_mapping_repr (a_mapping , b_mapping , compat ,
482
+ title , summarizer , col_width = None ):
483
+
484
+ def extra_items_repr (extra_keys , mapping , ab_side ):
485
+ extra_repr = [summarizer (k , mapping [k ], col_width ) for k in extra_keys ]
486
+ if extra_repr :
487
+ header = "{} only on the {} object:" .format (title , ab_side )
488
+ return [header ] + extra_repr
489
+ else :
490
+ return []
491
+
492
+ a_keys = set (a_mapping )
493
+ b_keys = set (b_mapping )
494
+
495
+ summary = []
496
+
497
+ diff_items = []
498
+
499
+ for k in a_keys & b_keys :
500
+ try :
501
+ # compare xarray variable
502
+ compatible = getattr (a_mapping [k ], compat )(b_mapping [k ])
503
+ is_variable = True
504
+ except AttributeError :
505
+ # compare attribute value
506
+ compatible = a_mapping [k ] == b_mapping [k ]
507
+ is_variable = False
508
+
509
+ if not compatible :
510
+ temp = [summarizer (k , vars [k ], col_width )
511
+ for vars in (a_mapping , b_mapping )]
512
+
513
+ if compat == 'identical' and is_variable :
514
+ attrs_summary = []
515
+
516
+ for m in (a_mapping , b_mapping ):
517
+ attr_s = "\n " .join ([summarize_attr (ak , av )
518
+ for ak , av in m [k ].attrs .items ()])
519
+ attrs_summary .append (attr_s )
520
+
521
+ temp = ["\n " .join ([var_s , attr_s ]) if attr_s else var_s
522
+ for var_s , attr_s in zip (temp , attrs_summary )]
523
+
524
+ diff_items += [ab_side + s [1 :]
525
+ for ab_side , s in zip (('L' , 'R' ), temp )]
526
+
527
+ if diff_items :
528
+ summary += ["Differing {}:" .format (title .lower ())] + diff_items
529
+
530
+ summary += extra_items_repr (a_keys - b_keys , a_mapping , "left" )
531
+ summary += extra_items_repr (b_keys - a_keys , b_mapping , "right" )
532
+
533
+ return "\n " .join (summary )
534
+
535
+
536
+ diff_coords_repr = functools .partial (_diff_mapping_repr ,
537
+ title = "Coordinates" ,
538
+ summarizer = summarize_coord )
539
+
540
+
541
+ diff_data_vars_repr = functools .partial (_diff_mapping_repr ,
542
+ title = "Data variables" ,
543
+ summarizer = summarize_datavar )
544
+
545
+
546
+ diff_attrs_repr = functools .partial (_diff_mapping_repr ,
547
+ title = "Attributes" ,
548
+ summarizer = summarize_attr )
549
+
550
+
551
+ def _compat_to_str (compat ):
552
+ if compat == "equals" :
553
+ return "equal"
554
+ else :
555
+ return compat
556
+
557
+
558
+ def diff_array_repr (a , b , compat ):
559
+ # used for DataArray, Variable and IndexVariable
560
+ summary = ["Left and right {} objects are not {}"
561
+ .format (type (a ).__name__ , _compat_to_str (compat ))]
562
+
563
+ summary .append (diff_dim_summary (a , b ))
564
+
565
+ if not array_equiv (a .data , b .data ):
566
+ temp = [wrap_indent (short_array_repr (obj ), start = ' ' )
567
+ for obj in (a , b )]
568
+ diff_data_repr = [ab_side + "\n " + ab_data_repr
569
+ for ab_side , ab_data_repr in zip (('L' , 'R' ), temp )]
570
+ summary += ["Differing values:" ] + diff_data_repr
571
+
572
+ if hasattr (a , 'coords' ):
573
+ col_width = _calculate_col_width (set (a .coords ) | set (b .coords ))
574
+ summary .append (diff_coords_repr (a .coords , b .coords , compat ,
575
+ col_width = col_width ))
576
+
577
+ if compat == 'identical' :
578
+ summary .append (diff_attrs_repr (a .attrs , b .attrs , compat ))
579
+
580
+ return "\n " .join (summary )
581
+
582
+
583
+ def diff_dataset_repr (a , b , compat ):
584
+ summary = ["Left and right {} objects are not {}"
585
+ .format (type (a ).__name__ , _compat_to_str (compat ))]
586
+
587
+ col_width = _calculate_col_width (
588
+ set (_get_col_items (a .variables ) + _get_col_items (b .variables )))
589
+
590
+ summary .append (diff_dim_summary (a , b ))
591
+ summary .append (diff_coords_repr (a .coords , b .coords , compat ,
592
+ col_width = col_width ))
593
+ summary .append (diff_data_vars_repr (a .data_vars , b .data_vars , compat ,
594
+ col_width = col_width ))
595
+
596
+ if compat == 'identical' :
597
+ summary .append (diff_attrs_repr (a .attrs , b .attrs , compat ))
598
+
599
+ return "\n " .join (summary )
0 commit comments