From 5d0a0b1d22e16b1ebe99a9a4c44e1280bad7f8fb Mon Sep 17 00:00:00 2001 From: LandonB5 Date: Thu, 2 Oct 2025 03:16:32 -0400 Subject: [PATCH 1/3] BUG: assert_frame_equal(check_dtype=False) treats missing sentinels as equal across dtypes (GH#61473) --- pandas/tests/util/test_assert_frame_equal.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pandas/tests/util/test_assert_frame_equal.py b/pandas/tests/util/test_assert_frame_equal.py index 8711365a19214..bceb1af8b711c 100644 --- a/pandas/tests/util/test_assert_frame_equal.py +++ b/pandas/tests/util/test_assert_frame_equal.py @@ -5,6 +5,7 @@ import pandas as pd from pandas import DataFrame import pandas._testing as tm +from pandas.testing import assert_frame_equal @pytest.fixture(params=[True, False]) @@ -399,6 +400,7 @@ def test_assert_frame_equal_set_mismatch(): tm.assert_frame_equal(df1, df2) + def test_datetimelike_compat_deprecated(): # GH#55638 df = DataFrame({"a": [1]}) @@ -413,3 +415,11 @@ def test_datetimelike_compat_deprecated(): tm.assert_series_equal(df["a"], df["a"], check_datetimelike_compat=True) with tm.assert_produces_warning(Pandas4Warning, match=msg): tm.assert_series_equal(df["a"], df["a"], check_datetimelike_compat=False) + + +def test_assert_frame_equal_na_object_vs_int32_check_dtype_false(): + + df1 = pd.DataFrame({"x": pd.Series([pd.NA], dtype="Int32")}) + df2 = pd.DataFrame({"x": pd.Series([pd.NA], dtype="object")}) + assert_frame_equal(df1, df2, check_dtype=False) + From 9f7bcb394951ed5516d67cc3e25ffb00d625ceaf Mon Sep 17 00:00:00 2001 From: LandonB5 Date: Thu, 2 Oct 2025 03:19:04 -0400 Subject: [PATCH 2/3] BUG: assert_frame_equal(check_dtype=False) treats missing sentinels as equal across dtypes (GH#61473) --- pandas/_testing/asserters.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pandas/_testing/asserters.py b/pandas/_testing/asserters.py index 2e5f8c372f7ef..116c2f37de88d 100644 --- a/pandas/_testing/asserters.py +++ b/pandas/_testing/asserters.py @@ -1105,6 +1105,37 @@ def assert_series_equal( index_values=left.index, obj=str(obj), ) + elif not check_dtype: + # When dtype checks are off, treat all missing sentinels as equal. + left_na = np.asarray(left.isna()) + right_na = np.asarray(right.isna()) + assert_numpy_array_equal( + left_na, right_na, obj=f"{obj} NA mask", index_values=left.index + ) + + left_valid = left[~left_na].to_numpy(dtype=object) + right_valid = right[~right_na].to_numpy(dtype=object) + + _testing.assert_almost_equal( + left_valid, + right_valid, + check_dtype=False, + rtol=rtol, + atol=atol, + obj=str(obj), + index_values=left.index, + ) + return + else: + _testing.assert_almost_equal( + left._values, + right._values, + rtol=rtol, + atol=atol, + check_dtype=bool(check_dtype), + obj=str(obj), + index_values=left.index, + ) else: _testing.assert_almost_equal( left._values, From 4e5f1606fdcf8e31589ef2280d44aeefc8786e56 Mon Sep 17 00:00:00 2001 From: LandonB5 Date: Thu, 2 Oct 2025 03:33:40 -0400 Subject: [PATCH 3/3] TST/BUG: handle NA equivalence when check_dtype=False without skipping metadata (GH#61473) --- pandas/_testing/asserters.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pandas/_testing/asserters.py b/pandas/_testing/asserters.py index 116c2f37de88d..8833f265d5850 100644 --- a/pandas/_testing/asserters.py +++ b/pandas/_testing/asserters.py @@ -1106,7 +1106,6 @@ def assert_series_equal( obj=str(obj), ) elif not check_dtype: - # When dtype checks are off, treat all missing sentinels as equal. left_na = np.asarray(left.isna()) right_na = np.asarray(right.isna()) assert_numpy_array_equal( @@ -1125,17 +1124,6 @@ def assert_series_equal( obj=str(obj), index_values=left.index, ) - return - else: - _testing.assert_almost_equal( - left._values, - right._values, - rtol=rtol, - atol=atol, - check_dtype=bool(check_dtype), - obj=str(obj), - index_values=left.index, - ) else: _testing.assert_almost_equal( left._values,