57

在对某些功能进行单元测试的情况下,我正在尝试使用 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针对整个 of 测试整个 of df,包括NaN位置,我做错了什么?

比较包括 s 在内的 Series/DataFrames 相等性的最简单方法是NaN什么?

4

5 回答 5

53

您可以将 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)
于 2013-10-11T16:17:55.753 回答
30

的属性之一NaN是。NaN != NaNTrue

查看这个答案,了解使用numexpr.

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

说这个(在伪代码中):

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

所以,要么等于ab要么两者a兼而有之bNaN

如果你有小框架,那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
于 2013-10-11T16:16:34.310 回答
10

喜欢@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
于 2013-10-11T16:19:12.167 回答
2

任何使用 == 与 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)

于 2015-08-20T16:32:44.557 回答
2
df.fillna(0) == df2.fillna(0)

您可以使用fillna(). 文档在这里

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)
于 2015-10-01T18:08:20.510 回答