29

作为单元测试的一部分,我需要测试两个 DataFrame 是否相等。DataFrames 中列的顺序对我来说并不重要。然而,这对 Pandas 来说似乎很重要:

import pandas
df1 = pandas.DataFrame(index = [1,2,3,4])
df2 = pandas.DataFrame(index = [1,2,3,4])
df1['A'] = [1,2,3,4]
df1['B'] = [2,3,4,5]
df2['B'] = [2,3,4,5]
df2['A'] = [1,2,3,4]
df1 == df2

结果是:

Exception: Can only compare identically-labeled DataFrame objects

我相信表达式df1 == df2应该评估为包含所有True值的 DataFrame。==显然,在这种情况下,正确的功能应该是什么是有争议的。我的问题是:有没有一种 Pandas 方法可以满足我的要求?也就是说,有没有办法进行忽略列顺序的相等比较?

4

9 回答 9

36

最常见的意图是这样处理的:

def assertFrameEqual(df1, df2, **kwds ):
    """ Assert that two dataframes are equal, ignoring ordering of columns"""
    from pandas.util.testing import assert_frame_equal
    return assert_frame_equal(df1.sort_index(axis=1), df2.sort_index(axis=1), check_names=True, **kwds )

当然看看pandas.util.testing.assert_frame_equal你可以传递的其他参数

于 2014-01-08T16:04:29.927 回答
20

您可以使用以下方式对列进行排序sort_index

df1.sort_index(axis=1) == df2.sort_index(axis=1)

这将评估为所有True值的数据框。


正如@osa 评论的那样,这对于NaN 来说是失败的,并且也不是特别健壮,实际上可能建议使用类似于@quant 答案的东西(注意:如果有问题,我们想要一个布尔值而不是引发):

def my_equal(df1, df2):
    from pandas.util.testing import assert_frame_equal
    try:
        assert_frame_equal(df1.sort_index(axis=1), df2.sort_index(axis=1), check_names=True)
        return True
    except (AssertionError, ValueError, TypeError):  perhaps something else?
        return False
于 2013-01-08T21:38:58.643 回答
5
def equal( df1, df2 ):
    """ Check if two DataFrames are equal, ignoring nans """
    return df1.fillna(1).sort_index(axis=1).eq(df2.fillna(1).sort_index(axis=1)).all().all()
于 2013-10-10T19:30:53.717 回答
4

通常你会想要快速的测试,并且排序方法对于较大的索引可能会非常低效(比如如果你使用行而不是列来解决这个问题)。排序方法也容易受到非唯一索引的假阴性影响。

幸运的是,pandas.util.testing.assert_frame_equal已经更新了一个check_like选项。将此设置为 true 并且在测试中将不考虑排序。

使用非唯一索引,您将获得神秘的ValueError: cannot reindex from a duplicate axis. 这是由reindex_like重新排列其中一个 DataFrame 以匹配另一个顺序的幕后操作引发的。重新索引比排序快得多,如下所示

import pandas as pd
from pandas.util.testing import assert_frame_equal

df  = pd.DataFrame(np.arange(1e6))
df1 = df.sample(frac=1, random_state=42)
df2 = df.sample(frac=1, random_state=43)

%timeit -n 1 -r 5 assert_frame_equal(df1.sort_index(), df2.sort_index())
## 5.73 s ± 329 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)

%timeit -n 1 -r 5 assert_frame_equal(df1, df2, check_like=True)
## 1.04 s ± 237 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)

对于那些享受良好性能比较图的人:

重新索引与在 int 和 str 索引上排序(str 更加激烈)

于 2017-05-31T06:32:30.850 回答
2

仅当行和列标签在框架中匹配时,排序列才有效。假设您有 2 个数据框,单元格中的值相同但标签不同,那么排序解决方案将不起作用。我在使用 pandas 实现 k-modes 集群时遇到了这种情况。

我用一个简单的等于函数来检查单元格是否相等(下面的代码)

def frames_equal(df1,df2) :
    if not isinstance(df1,DataFrame) or not isinstance(df2,DataFrame) :
        raise Exception(
            "dataframes should be an instance of pandas.DataFrame")

    if df1.shape != df2.shape:
        return False

    num_rows,num_cols = df1.shape
    for i in range(num_rows):
       match = sum(df1.iloc[i] == df2.iloc[i])
       if match != num_cols :
          return False
   return True
于 2015-07-17T04:47:41.887 回答
1

您是否尝试过使用 df1.equals(df2)?我认为 df1==df2 更可靠,但我不确定它是否能解决您的列顺序问题。

http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.equals.html

于 2016-07-20T23:15:34.777 回答
1

可能您可能需要函数来比较忽略行和列顺序的 DataFrame?唯一的要求是有一些唯一的列将其用作索引。

f1 = pd.DataFrame([
    {"id": 1, "foo": "1", "bar": None},
    {"id": 2, "foo": "2", "bar": 2},
    {"id": 3, "foo": "3", "bar": 3},
    {"id": 4, "foo": "4", "bar": 4}
])
f2 = pd.DataFrame([
    {"id": 3, "foo": "3", "bar": 3},
    {"id": 1, "bar": None, "foo": "1",},
    {"id": 2, "foo": "2", "bar": 2},
    {"id": 4, "foo": "4", "bar": 4}
])

def comparable(df, index_col='id'):
    return df.fillna(value=0).set_index(index_col).to_dict('index')

comparable(f1) == comparable (f2)  # returns True
于 2019-07-11T08:06:07.123 回答
1

assert_frame_equalfrompandas.testing是一个检查帧是否相等的函数。如assert_frame_equal 文档中所述,如果将check_like参数设置为True,它将忽略索引和列的顺序。

于 2022-01-31T11:34:03.110 回答
0

当使用包含元组和列表等 python 对象的数据框时,这是df.eq(df2)不够df == df2的。即使每个数据帧中的相同单元格包含相同的对象,例如(0, 0),相等比较也会导致False。为了解决这个问题,在比较之前将所有列转换为字符串:

df.apply(lambda x: x.astype(str)).eq(df2.apply(lambda x: x.astype(str)))

于 2018-11-17T17:01:21.727 回答