Skip to content

Commit 9ac19d4

Browse files
amilbourneukarroum
authored andcommitted
ENH: Fix output of assert_frame_equal if indexes differ and check_like=True (#37479)
1 parent 2baddc8 commit 9ac19d4

File tree

4 files changed

+46
-7
lines changed

4 files changed

+46
-7
lines changed

doc/source/whatsnew/v1.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ Other enhancements
228228
- :class:`Rolling` now supports the ``closed`` argument for fixed windows (:issue:`34315`)
229229
- :class:`DatetimeIndex` and :class:`Series` with ``datetime64`` or ``datetime64tz`` dtypes now support ``std`` (:issue:`37436`)
230230
- :class:`Window` now supports all Scipy window types in ``win_type`` with flexible keyword argument support (:issue:`34556`)
231+
- :meth:`testing.assert_index_equal` now has a ``check_order`` parameter that allows indexes to be checked in an order-insensitive manner (:issue:`37478`)
231232

232233
.. _whatsnew_120.api_breaking.python:
233234

pandas/_testing.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ def assert_index_equal(
667667
check_less_precise: Union[bool, int] = no_default,
668668
check_exact: bool = True,
669669
check_categorical: bool = True,
670+
check_order: bool = True,
670671
rtol: float = 1.0e-5,
671672
atol: float = 1.0e-8,
672673
obj: str = "Index",
@@ -696,6 +697,12 @@ def assert_index_equal(
696697
Whether to compare number exactly.
697698
check_categorical : bool, default True
698699
Whether to compare internal Categorical exactly.
700+
check_order : bool, default True
701+
Whether to compare the order of index entries as well as their values.
702+
If True, both indexes must contain the same elements, in the same order.
703+
If False, both indexes must contain the same elements, but in any order.
704+
705+
.. versionadded:: 1.2.0
699706
rtol : float, default 1e-5
700707
Relative tolerance. Only used when check_exact is False.
701708
@@ -762,6 +769,11 @@ def _get_ilevel_values(index, level):
762769
msg3 = f"{len(right)}, {right}"
763770
raise_assert_detail(obj, msg1, msg2, msg3)
764771

772+
# If order doesn't matter then sort the index entries
773+
if not check_order:
774+
left = left.sort_values()
775+
right = right.sort_values()
776+
765777
# MultiIndex special comparison for little-friendly error messages
766778
if left.nlevels > 1:
767779
left = cast(MultiIndex, left)
@@ -1582,9 +1594,6 @@ def assert_frame_equal(
15821594
obj, f"{obj} shape mismatch", f"{repr(left.shape)}", f"{repr(right.shape)}"
15831595
)
15841596

1585-
if check_like:
1586-
left, right = left.reindex_like(right), right
1587-
15881597
if check_flags:
15891598
assert left.flags == right.flags, f"{repr(left.flags)} != {repr(right.flags)}"
15901599

@@ -1596,6 +1605,7 @@ def assert_frame_equal(
15961605
check_names=check_names,
15971606
check_exact=check_exact,
15981607
check_categorical=check_categorical,
1608+
check_order=not check_like,
15991609
rtol=rtol,
16001610
atol=atol,
16011611
obj=f"{obj}.index",
@@ -1609,11 +1619,15 @@ def assert_frame_equal(
16091619
check_names=check_names,
16101620
check_exact=check_exact,
16111621
check_categorical=check_categorical,
1622+
check_order=not check_like,
16121623
rtol=rtol,
16131624
atol=atol,
16141625
obj=f"{obj}.columns",
16151626
)
16161627

1628+
if check_like:
1629+
left, right = left.reindex_like(right), right
1630+
16171631
# compare by blocks
16181632
if by_blocks:
16191633
rblocks = right._to_dict_of_blocks()

pandas/tests/util/test_assert_frame_equal.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def test_empty_dtypes(check_dtype):
145145
tm.assert_frame_equal(df1, df2, **kwargs)
146146

147147

148-
def test_frame_equal_index_mismatch(obj_fixture):
148+
@pytest.mark.parametrize("check_like", [True, False])
149+
def test_frame_equal_index_mismatch(check_like, obj_fixture):
149150
msg = f"""{obj_fixture}\\.index are different
150151
151152
{obj_fixture}\\.index values are different \\(33\\.33333 %\\)
@@ -156,10 +157,11 @@ def test_frame_equal_index_mismatch(obj_fixture):
156157
df2 = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]}, index=["a", "b", "d"])
157158

158159
with pytest.raises(AssertionError, match=msg):
159-
tm.assert_frame_equal(df1, df2, obj=obj_fixture)
160+
tm.assert_frame_equal(df1, df2, check_like=check_like, obj=obj_fixture)
160161

161162

162-
def test_frame_equal_columns_mismatch(obj_fixture):
163+
@pytest.mark.parametrize("check_like", [True, False])
164+
def test_frame_equal_columns_mismatch(check_like, obj_fixture):
163165
msg = f"""{obj_fixture}\\.columns are different
164166
165167
{obj_fixture}\\.columns values are different \\(50\\.0 %\\)
@@ -170,7 +172,7 @@ def test_frame_equal_columns_mismatch(obj_fixture):
170172
df2 = DataFrame({"A": [1, 2, 3], "b": [4, 5, 6]}, index=["a", "b", "c"])
171173

172174
with pytest.raises(AssertionError, match=msg):
173-
tm.assert_frame_equal(df1, df2, obj=obj_fixture)
175+
tm.assert_frame_equal(df1, df2, check_like=check_like, obj=obj_fixture)
174176

175177

176178
def test_frame_equal_block_mismatch(by_blocks_fixture, obj_fixture):

pandas/tests/util/test_assert_index_equal.py

+22
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ def test_index_equal_values_too_far(check_exact, rtol):
115115
tm.assert_index_equal(idx1, idx2, **kwargs)
116116

117117

118+
@pytest.mark.parametrize("check_order", [True, False])
119+
def test_index_equal_value_oder_mismatch(check_exact, rtol, check_order):
120+
idx1 = Index([1, 2, 3])
121+
idx2 = Index([3, 2, 1])
122+
123+
msg = """Index are different
124+
125+
Index values are different \\(66\\.66667 %\\)
126+
\\[left\\]: Int64Index\\(\\[1, 2, 3\\], dtype='int64'\\)
127+
\\[right\\]: Int64Index\\(\\[3, 2, 1\\], dtype='int64'\\)"""
128+
129+
if check_order:
130+
with pytest.raises(AssertionError, match=msg):
131+
tm.assert_index_equal(
132+
idx1, idx2, check_exact=check_exact, rtol=rtol, check_order=True
133+
)
134+
else:
135+
tm.assert_index_equal(
136+
idx1, idx2, check_exact=check_exact, rtol=rtol, check_order=False
137+
)
138+
139+
118140
def test_index_equal_level_values_mismatch(check_exact, rtol):
119141
idx1 = MultiIndex.from_tuples([("A", 2), ("A", 2), ("B", 3), ("B", 4)])
120142
idx2 = MultiIndex.from_tuples([("A", 1), ("A", 2), ("B", 3), ("B", 4)])

0 commit comments

Comments
 (0)