@@ -8403,6 +8403,104 @@ def ranker(data):
84038403
84048404 return ranker (data )
84058405
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+
84068504 @doc (** _shared_doc_kwargs )
84078505 def align (
84088506 self ,
0 commit comments