Q&A library Python

How do I test pandas code and compare DataFrames in a test?

Python Advanced assert-frame-equalpytesttesting

Short answer

Use pandas.testing.assert_frame_equal, which compares values, dtypes, columns and index and reports exactly what differs. Comparing frames with == or assert does not work, because it returns a frame of booleans.

The short version

Use pandas.testing.assert_frame_equal to compare an actual DataFrame with an expected one. It checks values, dtypes, column names and the index, and raises a detailed error showing exactly what differs. A plain assert actual == expected does not work, because comparing frames returns a frame of booleans, not a single truth value.

The correct comparison

import pandas as pd
from pandas.testing import assert_frame_equal

def add_total(df):
    return df.assign(total=df["price"] * df["qty"])

def test_add_total():
    given = pd.DataFrame({"price": [2, 5], "qty": [3, 1]})
    expected = pd.DataFrame({"price": [2, 5], "qty": [3, 1], "total": [6, 5]})
    assert_frame_equal(add_total(given), expected)

If they match, the test passes silently. If not, the error message points to the exact column and row that differ, which saves a great deal of debugging.

Why == does not work

actual == expected     # a DataFrame of True/False, not one answer
assert actual == expected   # ValueError: truth value is ambiguous

Python cannot reduce a frame of booleans to a single True, so it refuses. assert_frame_equal exists precisely to do this comparison properly.

It checks dtypes too, which is stricter than you expect

assert_frame_equal(
    pd.DataFrame({"x": [1]}),      # int64
    pd.DataFrame({"x": [1.0]}),    # float64
)   # AssertionError: dtype mismatch

This strictness is a feature: an integer column that has quietly become a float usually means a missing value crept in somewhere. If the difference is genuinely irrelevant to your test, relax it with check_dtype=False, but consider first whether the dtype change is telling you about a real bug.

Useful relaxations

Real tests often need to ignore incidental differences.

assert_frame_equal(actual, expected, check_like=True)   # ignore column and row order
assert_frame_equal(actual, expected, check_exact=False) # allow tiny float differences

check_exact=False matters for floating point, where a computed 0.30000000000000004 will never exactly equal 0.3. Comparing floats exactly is the classic cause of a test that fails for no real reason.

The index is compared too

A frequent surprise is a test failing because the actual frame has an index of 0, 3, 7 after filtering, while the expected has 0, 1, 2. That is a real difference, and often you should fix the code with reset_index(drop=True) rather than the test, because a stale index can cause misalignment later.

Series and index versions

There are matching helpers for the other pandas objects: assert_series_equal and assert_index_equal. Use the one that matches what your function returns, so the error messages stay specific.

Why test data code at all

Data pipelines fail silently: a wrong join drops rows, a bad fill skews a mean, and nothing raises. Small tests on tiny, hand-written frames catch these regressions the moment they appear, which is far cheaper than discovering a wrong number in a report months later.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project