具有 NaN 相等性比较的 Pandas DataFrames

Posted

技术标签:

【中文标题】具有 NaN 相等性比较的 Pandas DataFrames【英文标题】:Pandas DataFrames with NaNs equality comparison 【发布时间】:2013-10-19 18:59:48 【问题描述】:

在对某些函数进行单元测试的情况下,我正在尝试使用 python pandas 建立 2 个 DataFrame 的相等性:

ipdb> expect
                            1   2
2012-01-01 00:00:00+00:00 NaN   3
2013-05-14 12:00:00+00:00   3 NaN

ipdb> df
identifier                  1   2
timestamp
2012-01-01 00:00:00+00:00 NaN   3
2013-05-14 12:00:00+00:00   3 NaN

ipdb> df[1][0]
nan

ipdb> df[1][0], expect[1][0]
(nan, nan)

ipdb> df[1][0] == expect[1][0]
False

ipdb> df[1][1] == expect[1][1]
True

ipdb> type(df[1][0])
<type 'numpy.float64'>

ipdb> type(expect[1][0])
<type 'numpy.float64'>

ipdb> (list(df[1]), list(expect[1]))
([nan, 3.0], [nan, 3.0])

ipdb> df1, df2 = (list(df[1]), list(expect[1])) ;; df1 == df2
False

鉴于我试图测试整个 expect 与整个 df,包括 NaN 位置,我做错了什么?

比较包括NaNs 在内的系列/数据帧是否相等的最简单方法是什么?

【问题讨论】:

【参考方案1】:

您可以将 assert_frame_equals 与 check_names=False 一起使用(以免检查索引/列名称),如果它们不相等,则会引发:

In [11]: from pandas.testing import assert_frame_equal

In [12]: assert_frame_equal(df, expected, check_names=False)

您可以将其包装在一个函数中,例如:

try:
    assert_frame_equal(df, expected, check_names=False)
    return True
except AssertionError:
    return False

在最近的 pandas 中,此功能已添加为 .equals

df.equals(expected)

【讨论】:

我接受了这个而不是其他的(这很有用!),因为我的问题的真正症结在于比较 DataFrames - 我已经对其进行了一些编辑以使其更清楚。 仅供参考,这是最慢的方法,因为它是递归完成的而不是矢量化的(我们用它来测试) @StevePike 不确定我是否理解这一点:“......因为我的问题的真正症结是关于比较 DataFrames”。目前存在的所有解决方案都显示了比较DataFrames 的方法。 验证它们是一个稍微不同的问题。也许我有点迂腐。 这会在对象不相等时给出FutureWarningprint assert_frame_equal(X, df) 给出输出None 和警告/usr/local/lib/python2.7/site-packages/numpy/core/numeric.py:2367: FutureWarning: numpy equal will not check object identity in the future. The comparison did not return the same result as suggested by the identity (is)) and will change. return bool(asarray(a1 == a2).all()) @FooBar 有趣,我希望这将在 pandas 0.16 中得到修复。 (即将推出)【参考方案2】:

NaN 的属性之一是NaN != NaNTrue

查看this answer,了解使用numexpr 的好方法。

(a == b) | ((a != a) & (b != b))

这样说(用伪代码):

a == b or (isnan(a) and isnan(b))

所以,要么a 等于b,要么ab 都是NaN

如果你有小框架,那么assert_frame_equal 就可以了。但是,对于大帧(10M 行)assert_frame_equal 几乎没用。我不得不打断它,它花了很长时间。

In [1]: df = DataFrame(rand(1e7, 15))

In [2]: df = df[df > 0.5]

In [3]: df2 = df.copy()

In [4]: df
Out[4]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 10000000 entries, 0 to 9999999
Columns: 15 entries, 0 to 14
dtypes: float64(15)

In [5]: timeit (df == df2) | ((df != df) & (df2 != df2))
1 loops, best of 3: 598 ms per loop

timeit(可能是)所需的单个bool 表示两个DataFrames 是否相等:

In [9]: timeit ((df == df2) | ((df != df) & (df2 != df2))).values.all()
1 loops, best of 3: 687 ms per loop

【讨论】:

大概之后你需要.all().all() ? 使用np.all() 要快得多,因为这种比较保证了布尔结果。它使运行时间又增加了约 100 毫秒。如果您使用 pandas 对象的 all() 方法,则性能大约是 3.5 倍。 “保证”的意思是我假设有人没有使用可以从 __eq__/__ne__ 返回 NaN 的子类。 你可以做((df == df2) | ((df != df) &amp; (df2 != df2))).values.ravel().all() 我会把它添加到timeit【参考方案3】:

类似于@PhillipCloud 的回答,但写得更多

In [26]: df1 = DataFrame([[np.nan,1],[2,np.nan]])

In [27]: df2 = df1.copy()

它们真的是等价的

In [28]: result = df1 == df2

In [29]: result[pd.isnull(df1) == pd.isnull(df2)] = True

In [30]: result
Out[30]: 
      0     1
0  True  True
1  True  True

df1 中不存在的 df2 中的 nan

In [31]: df2 = DataFrame([[np.nan,1],[np.nan,np.nan]])

In [32]: result = df1 == df2

In [33]: result[pd.isnull(df1) == pd.isnull(df2)] = True

In [34]: result
Out[34]: 
       0     1
0   True  True
1  False  True

你也可以填写一个你知道不在框架中的值

In [38]: df1.fillna(-999) == df1.fillna(-999)
Out[38]: 
      0     1
0  True  True
1  True  True

【讨论】:

如果你有两个不相等的值,我认为这将给出 True 因为它们都是非空的:s @Andy Hayden 是正确的:result[pd.isnull(df1) == pd.isnull(df2)] = True 应该是 result[pd.isnull(df1) &amp; pd.isnull(df2)] = True【参考方案4】:
df.fillna(0) == df2.fillna(0)

您可以使用fillna()。 Documenation here.

from pandas import DataFrame

# create a dataframe with NaNs
df = DataFrame(['a': 1, 'b': 2, 'a': 5, 'b': 10, 'c': 20])
df2 = df

# comparison fails!
print df == df2

# all is well 
print df.fillna(0) == df2.fillna(0)

【讨论】:

0 感觉一般来说是一件危险的事情,但用一些唯一的字符串或标识符填充听起来是个好策略。对于小型案例,此策略比公认的答案要快,但对于大型案例要慢得多【参考方案5】:

任何使用 == 与 np.NaN 的相等比较都是 False,甚至 np.NaN == np.NaN 也是 False。

简单地说,df1.fillna('NULL') == df2.fillna('NULL'),如果 'NULL' 不是原始数据中的值。

为了安全起见,请执行以下操作:

示例 a) 比较两个具有 NaN 值的数据帧

bools = (df1 == df2)
bools[pd.isnull(df1) & pd.isnull(df2)] = True
assert bools.all().all()

示例 b) 过滤 df1 中与 df2 不匹配的行

bools = (df1 != df2)
bools[pd.isnull(df1) & pd.isnull(df2)] = False
df_outlier = df1[bools.all(axis=1)]

(注意:这是错误的 - bools[pd.isnull(df1) == pd.isnull(df2)] = False)

【讨论】:

如果您在答案中添加更多文字,您的答案会看起来更好,也许可以解释您在做什么。 这种策略对于小案例来说比公认的答案要快,但对于大案例来说要慢得多

以上是关于具有 NaN 相等性比较的 Pandas DataFrames的主要内容,如果未能解决你的问题,请参考以下文章

在 Pandas 中将两列与 NaN 进行比较并获得差异

Pandas:无法根据字符串相等性进行过滤

如何检查NaN javascript的相等性[重复]

比较 NumPy 数组以便 NaN 比较相等

查找具有 NaN 值的 DataFrame 列表的索引 - Pandas

Pandas/Numpy NaN 无比较