colonnes groupby avec valeurs NaN (manquantes)

J’ai un DataFrame avec beaucoup de valeurs manquantes dans les colonnes que je souhaite grouper par:

import pandas as pd import numpy as np df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']}) In [4]: df.groupby('b').groups Out[4]: {'4': [0], '6': [2]} 

voir que Pandas a supprimé les lignes avec les valeurs cibles NaN. (Je veux inclure ces lignes!)

Étant donné que j’ai besoin de nombreuses opérations de ce type (beaucoup de colonnes ont des valeurs manquantes) et utilisent des fonctions plus complexes que les simples médianes (généralement des forêts aléatoires), je veux éviter d’écrire des parties de code trop compliquées.

Aucune suggestion? Dois-je écrire une fonction pour cela ou existe-t-il une solution simple?

Ceci est mentionné dans la section Données manquantes de la documentation :

Les groupes NA dans GroupBy sont automatiquement exclus. Ce comportement est compatible avec R, par exemple.

Une solution consiste à utiliser un espace réservé avant de faire le groupby (par exemple, -1):

 In [11]: df.fillna(-1) Out[11]: ab 0 1 4 1 2 -1 2 3 6 In [12]: df.fillna(-1).groupby('b').sum() Out[12]: a b -1 2 4 1 6 3 

Cela dit, cela semble être un hack terrible … peut-être devrait-il y avoir une option pour inclure NaN dans groupby (voir ce problème de github – qui utilise le même hack d’espace réservé).

Ancien sujet, si quelqu’un trébuche encore à ce sujet – une autre solution consiste à convertir via .astype (str) en chaîne avant le regroupement. Cela permettra de conserver les NaN.

 in: 

df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']}) df.astype(str).groupby(['b']).sum()

 out: a b 4 1 6 3 nan 2 

Je ne suis pas en mesure d’append un commentaire à M. Kiewisch car je n’ai pas assez de points de réputation (je n’ai que 41 mais il en faut plus de 50 pour commenter).

Quoi qu’il en soit, je tiens simplement à souligner que la solution de M. Kiewisch ne fonctionne pas telle quelle et peut nécessiter plus de modifications. Prenons par exemple

 >>> df = pd.DataFrame({'a': [1, 2, 3, 5], 'b': [4, np.NaN, 6, 4]}) >>> df ab 0 1 4.0 1 2 NaN 2 3 6.0 3 5 4.0 >>> df.groupby(['b']).sum() a b 4.0 6 6.0 3 >>> df.astype(str).groupby(['b']).sum() a b 4.0 15 6.0 3 nan 2 

ce qui montre que pour le groupe b = 4.0, la valeur correspondante est 15 au lieu de 6. Ici, il suffit de concaténer 1 et 5 en tant que chaînes au lieu de les append en tant que nombres.

Un petit point à la solution d’Andy Hayden – ça ne marche plus (plus?) Parce que np.nan == np.nan cède False , donc la fonction replace ne fait rien.

Ce qui a fonctionné pour moi était ceci:

 df['b'] = df['b'].apply(lambda x: x if not np.isnan(x) else -1) 

(Au moins, c’est le comportement de Pandas 0.19.2. Désolé de l’append en tant que réponse différente, je n’ai pas assez de réputation pour commenter.)

J’ai déjà répondu à cette question, mais pour une raison quelconque, la réponse a été convertie en commentaire. Néanmoins, c’est la solution la plus efficace:

Le fait de ne pas pouvoir inclure (et propager) les réseaux NaN dans les groupes est tout à fait aggravant. Citing R n’est pas convaincant, car ce comportement n’est pas compatible avec beaucoup d’autres choses. Quoi qu’il en soit, le piratage est également très mauvais. Cependant, la taille (inclut NaN) et le nombre (ignore les NaN) d’un groupe diffèrent s’il y a des NaN.

 dfgrouped = df.groupby(['b']).a.agg(['sum','size','count']) dfgrouped['sum'][dfgrouped['size']!=dfgrouped['count']] = None 

Lorsque ceux-ci diffèrent, vous pouvez redéfinir la valeur sur Aucun pour le résultat de la fonction d’agrégation pour ce groupe.

Bien que par défaut il n’y ait pas de fonctionnalité skipna dans les agrégations de groupe, il existe un moyen simple et propre de le faire:

 def custom_mean(df): return df.mean(skipna=False) group.agg({"your_col_name_to_be_aggregated":custom_mean}) 

C’est tout!

Notez que je ne l’ai pas évalué, mais je m’attends à ce qu’il soit plus rapide que ce qui a été discuté dans les réponses précédentes.

Réponse trouvée dans le doc .