Dans le cadre du test unitaire de certaines fonctions, j'essaie d'établir l'égalité de 2 DataFrames en utilisant 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
Étant donné que j'essaie de tester l'ensemble de expect
par rapport à l'ensemble de df
, y compris NaN
positions, que fais-je de mal?
Quel est le moyen le plus simple de comparer l'égalité des Series/DataFrames, y compris NaN
s?
Vous pouvez utiliser assert_frame_equals avec check_names = False (afin de ne pas vérifier les noms d'index/colonnes), ce qui augmentera s'ils ne sont pas égaux:
In [11]: from pandas.testing import assert_frame_equal
In [12]: assert_frame_equal(df, expected, check_names=False)
Vous pouvez envelopper cela dans une fonction avec quelque chose comme:
try:
assert_frame_equal(df, expected, check_names=False)
return True
except AssertionError:
return False
Dans les plus récents pandas cette fonctionnalité a été ajoutée sous la forme .equals
:
df.equals(expected)
L'une des propriétés de NaN
est que NaN != NaN
est True
.
Consultez cette réponse pour une belle façon de le faire en utilisant numexpr
.
(a == b) | ((a != a) & (b != b))
dit ceci (en pseudocode):
a == b or (isnan(a) and isnan(b))
Ainsi, soit a
est égal à b
, soit a
et b
sont NaN
.
Si vous avez de petits cadres, alors assert_frame_equal
ira bien. Cependant, pour les grands cadres (10 millions de lignes) assert_frame_equal
est à peu près inutile. J'ai dû l'interrompre, cela prenait tellement de temps.
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
du single (vraisemblablement) désiré bool
indiquant si les deux DataFrame
sont égaux:
In [9]: timeit ((df == df2) | ((df != df) & (df2 != df2))).values.all()
1 loops, best of 3: 687 ms per loop
Comme la réponse @PhillipCloud, mais plus écrite
In [26]: df1 = DataFrame([[np.nan,1],[2,np.nan]])
In [27]: df2 = df1.copy()
Ils sont vraiment équivalents
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
Un nan en df2 qui n'existe pas en 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 le cadre
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é utilisant == avec np.NaN est False, même np.NaN == np.NaN est False.
Simplement, df1.fillna('NULL') == df2.fillna('NULL')
, si 'NULL' n'est pas une valeur dans les données d'origine.
Pour être sûr, procédez comme suit:
Exemple a) Comparez deux trames de données avec des valeurs NaN
bools = (df1 == df2)
bools[pd.isnull(df1) & pd.isnull(df2)] = True
assert bools.all().all()
Exemple b) Filtrer les lignes dans df1 qui ne correspondent pas à df2
bools = (df1 != df2)
bools[pd.isnull(df1) & pd.isnull(df2)] = False
df_outlier = df1[bools.all(axis=1)]
(Remarque: c'est faux - bools [pd.isnull (df1) == pd.isnull (df2)] = False)