@@ -8403,6 +8403,104 @@ def ranker(data):
8403
8403
8404
8404
return ranker (data )
8405
8405
8406
+ _shared_docs [
8407
+ "compare"
8408
+ ] = """
8409
+ Compare to another %(klass)s and show the differences.
8410
+
8411
+ .. versionadded:: 1.1.0
8412
+
8413
+ Parameters
8414
+ ----------
8415
+ other : %(klass)s
8416
+ Object to compare with.
8417
+
8418
+ align_axis : {0 or 'index', 1 or 'columns'}, default 1
8419
+ Determine which axis to align the comparison on.
8420
+
8421
+ * 0, or 'index' : Resulting differences are stacked vertically
8422
+ with rows drawn alternately from self and other.
8423
+ * 1, or 'columns' : Resulting differences are aligned horizontally
8424
+ with columns drawn alternately from self and other.
8425
+
8426
+ keep_shape : bool, default False
8427
+ If true, all rows and columns are kept.
8428
+ Otherwise, only the ones with different values are kept.
8429
+
8430
+ keep_equal : bool, default False
8431
+ If true, the result keeps values that are equal.
8432
+ Otherwise, equal values are shown as NaNs.
8433
+ """
8434
+
8435
+ @Appender (_shared_docs ["compare" ] % _shared_doc_kwargs )
8436
+ def compare (
8437
+ self ,
8438
+ other ,
8439
+ align_axis : Axis = 1 ,
8440
+ keep_shape : bool_t = False ,
8441
+ keep_equal : bool_t = False ,
8442
+ ):
8443
+ from pandas .core .reshape .concat import concat
8444
+
8445
+ if type (self ) is not type (other ):
8446
+ cls_self , cls_other = type (self ).__name__ , type (other ).__name__
8447
+ raise TypeError (
8448
+ f"can only compare '{ cls_self } ' (not '{ cls_other } ') with '{ cls_self } '"
8449
+ )
8450
+
8451
+ mask = ~ ((self == other ) | (self .isna () & other .isna ()))
8452
+ keys = ["self" , "other" ]
8453
+
8454
+ if not keep_equal :
8455
+ self = self .where (mask )
8456
+ other = other .where (mask )
8457
+
8458
+ if not keep_shape :
8459
+ if isinstance (self , ABCDataFrame ):
8460
+ cmask = mask .any ()
8461
+ rmask = mask .any (axis = 1 )
8462
+ self = self .loc [rmask , cmask ]
8463
+ other = other .loc [rmask , cmask ]
8464
+ else :
8465
+ self = self [mask ]
8466
+ other = other [mask ]
8467
+
8468
+ if align_axis in (1 , "columns" ): # This is needed for Series
8469
+ axis = 1
8470
+ else :
8471
+ axis = self ._get_axis_number (align_axis )
8472
+
8473
+ diff = concat ([self , other ], axis = axis , keys = keys )
8474
+
8475
+ if axis >= self .ndim :
8476
+ # No need to reorganize data if stacking on new axis
8477
+ # This currently applies for stacking two Series on columns
8478
+ return diff
8479
+
8480
+ ax = diff ._get_axis (axis )
8481
+ ax_names = np .array (ax .names )
8482
+
8483
+ # set index names to positions to avoid confusion
8484
+ ax .names = np .arange (len (ax_names ))
8485
+
8486
+ # bring self-other to inner level
8487
+ order = list (range (1 , ax .nlevels )) + [0 ]
8488
+ if isinstance (diff , ABCDataFrame ):
8489
+ diff = diff .reorder_levels (order , axis = axis )
8490
+ else :
8491
+ diff = diff .reorder_levels (order )
8492
+
8493
+ # restore the index names in order
8494
+ diff ._get_axis (axis = axis ).names = ax_names [order ]
8495
+
8496
+ # reorder axis to keep things organized
8497
+ indices = (
8498
+ np .arange (diff .shape [axis ]).reshape ([2 , diff .shape [axis ] // 2 ]).T .flatten ()
8499
+ )
8500
+ diff = diff .take (indices , axis = axis )
8501
+
8502
+ return diff
8503
+
8406
8504
@doc (** _shared_doc_kwargs )
8407
8505
def align (
8408
8506
self ,
0 commit comments