Si je calcule la moyenne d'un objet groupby et que dans l'un des groupes il y a un ou des NaN, les NaN sont ignorés. Même lors de l'application de np.mean, il ne renvoie que la moyenne de tous les nombres valides. Je m'attendrais à un comportement de retour de NaN dès qu'un NaN est dans le groupe. Voici un exemple simplifié du comportement
a b 1 1.5 2 NaN
Je souhaite recevoir le résultat suivant:
import pandas as pd import numpy as np c = pd.DataFrame({'a':[1,np.nan,2,3],'b':[1,2,1,2]}) c.groupby('b').mean() a b 1 1.5 2 3.0 c.groupby('b').agg(np.mean) a b 1 1.5 2 3.0
Je suis conscient que je peux remplacer les NaN au préalable et que je peux probablement écrire ma propre fonction d'agrégation pour renvoyer NaN dès que NaN est dans le groupe. Cette fonction ne serait cependant pas optimisée.
Connaissez-vous un argument pour obtenir le comportement souhaité avec les fonctions optimisées?
Btw, je pense que le comportement souhaité a été implémenté dans une version précédente de pandas.
6 Réponses :
Utilisez l'option skipna
-
c.groupby('b').apply(lambda g: g.mean(skipna=False))
Par défaut, pandas
ignore les valeurs Nan
. Vous pouvez le faire inclure Nan
en spécifiant skipna=False
:
In [215]: c.groupby('b').agg({'a': lambda x: x.mean(skipna=False)}) Out[215]: a b 1 1.5 2 NaN
oui, mais ce n'est pas vectorisé (lent).
Une autre approche consisterait à utiliser une valeur non ignorée par défaut, par exemple np.inf
:
>>> c = pd.DataFrame({'a':[1,np.inf,2,3],'b':[1,2,1,2]}) >>> c.groupby('b').mean() a b 1 1.500000 2 inf
Avant le calcul de la moyenne, vous pouvez utiliser fillna (np.inf)
et après la moyenne, vous pouvez utiliser .replace ([np.inf, -np.inf], np.nan) code> pour restaurer les valeurs nan.
Il existe trois méthodes différentes:
method3 = c.groupby('b').sum() nan_index = c[c['b'].isna()].index.to_list() method3.loc[method3.index.isin(nan_index)] = np.nan
c.groupby('b').agg({'a': lambda x: x.mean(skipna=False)})
c.groupby('b').apply(lambda g: g.mean(skipna=False))
mean (skipna = False)
, mais ça ne fonctionne pas Les méthodes d'agrégation GroupBy (min, max, mean, median, etc.) ont le code skipna >, qui est destiné à cette tâche exacte, mais il semble qu'actuellement (mai 2020) il existe un bug (problème ouvert le mars 2020), qui l'empêche de fonctionner correctement.
Exemple de travail complet basé sur ces commentaires: @Serge Ballesta , @ RoelAdriaans
>>> import pandas as pd >>> import numpy as np >>> c = pd.DataFrame({'a':[1,np.nan,2,3],'b':[1,2,1,2]}) >>> c.fillna(np.inf).groupby('b').mean().replace(np.inf, np.nan) a b 1 1.5 2 NaN
Pour plus d'informations et des mises à jour, suivez le lien ci-dessus.
J'ai atterri ici à la recherche d'un moyen rapide (vectorisé) de faire cela, mais je ne l'ai pas trouvé. De plus, dans le cas des nombres complexes, groupby
se comporte un peu étrangement: il n'aime pas mean ()
, et avec sum ()
il convertira les groupes où toutes les valeurs sont NaN
en 0 + 0j
.
Voici donc ce que j'ai trouvé:
Configuration :
cnt = gb.count() gb.sum() / cnt Out[]: b c d a 1 1.5 1.5 0.000000+2.000000j 2 3.0 NaN NaN
Comportement par défaut :
cnt = gb.count() gb.sum() * (cnt / cnt) out Out[]: b c d a 1 3.0 3.0 0.000000+2.000000j 2 3.0 NaN NaN
Un seul NaN
tue le groupe :
cnt = gb.count() siz = gb.size() mask = siz.values[:, None] == cnt.values gb.sum().where(mask) Out[]: b c d a 1 3.0 3.0 NaN 2 NaN NaN NaN
Seulement NaN
si toutes les valeurs du groupe sont NaN
gb.sum() Out[]: b c d a 1 3.0 3.0 0.000000+2.000000j 2 3.0 0.0 0.000000+0.000000j
Corollaire: moyenne du complexe :
df = pd.DataFrame({ 'a': [1, 2, 1, 2], 'b': [1, np.nan, 2, 3], 'c': [1, np.nan, 2, np.nan], 'd': np.array([np.nan, np.nan, 2, np.nan]) * 1j, }) gb = df.groupby('a')