【问题标题】:Equality in Pandas DataFrames - Column Order Matters?Pandas DataFrames 中的平等 - 列顺序很重要?
【发布时间】:2013-01-08 21:16:05
【问题描述】:

作为单元测试的一部分,我需要测试两个 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 方法可以满足我的要求?也就是说,有没有办法进行忽略列顺序的相等比较?

【问题讨论】:

  • 您可以使用以下命令强制列相同:df1 == df2.reindex(columns=df1.columns)
  • @Zelazny7 实际上并不总是这样做,例如如果 df2 对 df1 有额外的列。

标签: python pandas


【解决方案1】:

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

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其他参数你可以传递

【讨论】:

    【解决方案2】:

    您可以使用sort_index 对列进行排序:

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

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


    由于 @osa cmets 这对于 NaN 失败并且也不是特别健壮,在实践中可能建议使用类似于 @quant 的答案(注意:如果出现问题,我们想要一个 bool 而不是 raise):

    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
    

    【讨论】:

    • 不,它不适用于缺失值。然后您开始在不匹配的各个列上执行dropnafillna。然后你意识到它丢弃了一些你没有比较相等的东西,所以你用一些随机值做fillna,不同的列不同......整个事情变得一团糟。
    • @osa 你是对的,你想在之后使用 assert_frame_equal (我认为熊猫不会导出类似的东西),但要小心从 quant 的答案中使用它,因为它可以提高(而不是 return bool )。
    • @osa 我想你想做一些类似于 quants 但返回布尔值的事情,包括一个食谱。
    【解决方案3】:
    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()
    

    【讨论】:

    • fillna(1) 不是很可靠,因为人们可能在数据框中有1s。至少,考虑改用fillna(28347893),或者更好的是,上面建议的pandas.util.testing.assert_frame_equal
    【解决方案4】:

    通常您需要快速测试,并且排序方法对于较大的索引可能会非常低效(例如,如果您使用行而不是列来解决这个问题)。 sort 方法也容易在非唯一索引上出现误报。

    幸运的是,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)
    

    对于那些喜欢性能比较图的人:

    Reindexing vs sorting on int and str indices(更激烈)

    【讨论】:

      【解决方案5】:

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

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

      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
      

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

        • .equals 是否要求排序顺序相同?
        • 如果你想比较 2 个数据框。在 Pandas 中签出灵活比较,使用 .eq()、.nq()、gt() 等方法... --> 等于、不等于和大于。示例:df['new_col'] = df.gt(df_1)pandas.pydata.org/pandas-docs/stable/…
        • @danger89、.eq== 似乎也仅适用于已排序的 DataFrames
        【解决方案7】:

        您可能需要忽略行和列顺序来比较 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
        

        【讨论】:

          【解决方案8】:

          assert_frame_equal from pandas.testing 是一个检查帧是否相等的函数。 在assert_frame_equaldocumentation中提到,如果将check_like参数设置为True,它将忽略索引和列的顺序。

          【讨论】:

            【解决方案9】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-03-10
              • 2013-11-18
              • 2022-10-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-14
              • 2021-10-12
              相关资源
              最近更新 更多