Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 42 additions & 23 deletions pandas/_testing/asserters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,9 @@ def assert_frame_equal(
if check_like:
left = left.reindex_like(right)

column_errors = []
first_error_message = None

# compare by blocks
if by_blocks:
rblocks = right._to_dict_of_blocks()
Expand All @@ -1338,29 +1341,45 @@ def assert_frame_equal(
# use check_index=False, because we do not want to run
# assert_index_equal for each column,
# as we already checked it for the whole dataframe before.
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
message="the 'check_datetimelike_compat' keyword",
category=Pandas4Warning,
)
assert_series_equal(
lcol,
rcol,
check_dtype=check_dtype,
check_index_type=check_index_type,
check_exact=check_exact,
check_names=check_names,
check_datetimelike_compat=check_datetimelike_compat,
check_categorical=check_categorical,
check_freq=check_freq,
obj=f'{obj}.iloc[:, {i}] (column name="{col}")',
rtol=rtol,
atol=atol,
check_index=False,
check_flags=False,
)

try:
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
message="the 'check_datetimelike_compat' keyword",
category=Pandas4Warning,
)
assert_series_equal(
lcol,
rcol,
check_dtype=check_dtype,
check_index_type=check_index_type,
check_exact=check_exact,
check_names=check_names,
check_datetimelike_compat=check_datetimelike_compat,
check_categorical=check_categorical,
check_freq=check_freq,
obj=f'{obj}.iloc[:, {i}] (column name="{col}")',
rtol=rtol,
atol=atol,
check_index=False,
check_flags=False,
)
except AssertionError as e:
column_errors.append((i, col))
if first_error_message is None:
first_error_message = str(e)

if column_errors:
column_indices = [idx for idx, _ in column_errors]
column_names = [name for _, name in column_errors]

error_summary = f"{obj} are different\n\n"
error_summary += f"Columns with differences (positions {column_indices}):\n"
error_summary += f"{column_names}\n\n"
error_summary += f"First difference details:\n"
error_summary += first_error_message

raise AssertionError(error_summary)

def assert_equal(left, right, **kwargs) -> None:
"""
Expand Down
56 changes: 56 additions & 0 deletions pandas/tests/util/test_assert_frame_equal.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,3 +423,59 @@ def test_assert_frame_equal_nested_df_na(na_value):
df1 = DataFrame({"df": [inner]})
df2 = DataFrame({"df": [inner]})
tm.assert_frame_equal(df1, df2)

def test_assert_frame_equal_reports_all_different_columns():
df1 = pd.DataFrame({
"a": [1, 2, 3],
"b": [4, 5, 6],
"c": [7, 8, 9]
})
df2 = pd.DataFrame({
"a": [1, 99, 3],
"b": [4, 5, 6],
"c": [7, 8, 99]
})

with pytest.raises(AssertionError) as exc_info:
tm.assert_frame_equal(df1, df2)

error_msg = str(exc_info.value)

assert "Columns with differences" in error_msg

assert "'a'" in error_msg or '"a"' in error_msg
assert "'c'" in error_msg or '"c"' in error_msg

assert "[0, 2]" in error_msg or "0, 2" in error_msg

lines = error_msg.split('\n')
for i, line in enumerate(lines):
if "Columns with differences" in line:
if i + 1 < len(lines):
column_list_line = lines[i + 1]
assert "'a'" in column_list_line or '"a"' in column_list_line
assert "'c'" in column_list_line or '"c"' in column_list_line
assert not ("'b'" in column_list_line or '"b"' in column_list_line)

def test_assert_frame_equal_all_columns_different():
df1 = pd.DataFrame({
"a": [1, 2],
"b": [3, 4],
"c": [5, 6]
})
df2 = pd.DataFrame({
"a": [10, 20],
"b": [30, 40],
"c": [50, 60]
})

with pytest.raises(AssertionError) as exc_info:
tm.assert_frame_equal(df1, df2)

error_msg = str(exc_info.value)

assert "'a'" in error_msg or '"a"' in error_msg
assert "'b'" in error_msg or '"b"' in error_msg
assert "'c'" in error_msg or '"c"' in error_msg

assert "[0, 1, 2]" in error_msg or "0, 1, 2" in error_msg
Loading