Pandas DataFrames avec comparaison de L'égalité NaNs
Dans le cadre de l'unité de tester certaines fonctions, je suis en train d'établir l'égalité de 2 DataFrames à l'aide de python pandas:
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
etant Donné que je suis en train de tester l'ensemble des expect
contre la totalité de df
NaN
postes, ce que je fais mal?
Quelle est la façon la plus simple de comparer l'égalité des séries/DataFrames incluant NaN
s?
5 réponses
vous pouvez utiliser assert_frame_equals avec check_names=False (afin de ne pas vérifier les noms des index/colonnes), qui s'élèvera s'ils ne sont pas égaux:
In [11]: from pandas.util.testing import assert_frame_equal
In [12]: assert_frame_equal(df, expected, check_names=False)
Vous pouvez envelopper ce dans une fonction avec quelque chose comme:
try:
assert_frame_equal(df, expected, check_names=False)
return True
except AssertionError:
return False
dans les pandas plus récents cette fonctionnalité a été ajoutée comme .equals
:
df.equals(expected)
Une des propriétés de NaN
que NaN != NaN
True
.
découvrez cette réponse pour une belle façon de le faire à l'aide de numexpr
.
(a == b) | ((a != a) & (b != b))
dit (en pseudo-code):
a == b or (isnan(a) and isnan(b))
a
égale b
, ou les deux a
et b
NaN
.
Si vous avez de petits cadres, puis assert_frame_equal
va être correct. Toutefois, pour les cadres de grande taille (rangées de 10M)assert_frame_equal
est assez inutile. J'ai eu à l'interrompre, c'était aussi long.
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
de l' (probablement) souhaitée unique bool
indiquant si les deux DataFrame
s sont égaux:
In [9]: timeit ((df == df2) | ((df != df) & (df2 != df2))).values.all()
1 loops, best of 3: 687 ms per loop
Comme @PhillipCloud réponse, mais plus écrit
In [26]: df1 = DataFrame([[np.nan,1],[2,np.nan]])
In [27]: df2 = df1.copy()
Ils sont vraiment équivalent
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
nan en df2 qui n'existe pas dans df1
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
Vous pouvez également remplir avec une valeur que vous savez ne pas être dans l'image
In [38]: df1.fillna(-999) == df1.fillna(-999)
Out[38]:
0 1
0 True True
1 True True
df.fillna(0) == df2.fillna(0)
Vous pouvez utiliser fillna()
. Documenation ici.
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)
Toute comparaison d'égalité avec == np.NaN est fausse, même np.NaN = = np.NaN est fausse.
Simplement df1.fillna('NULL') == df2.fillna('NULL')
, si 'NULL' n'est pas une valeur dans les données originales.
Pour être sûr, procédez de la manière suivante:
exemple A) comparez deux images de données avec les valeurs de NaN
bools = (df1 == df2)
bools[pd.isnull(df1) & pd.isnull(df2)] = True
assert bools.all().all()
exemple b) filtrer les lignes de df1 qui ne correspondent pas à df2
bools = (df1 != df2)
bools[pd.isnull(df1) & pd.isnull(df2)] = False
df_outlier = df1[bools.all(axis=1)]
(Note: c'est faux - bool[pd.isnull (df1) = = pd.isnull (df2)] = False)