Comment supprimer des lignes de Pandas DataFrame dont la valeur dans une certaine colonne est NaN


754

J'ai ceci DataFrameet je veux seulement les enregistrements dont la EPScolonne n'est pas NaN:

>>> df
                 STK_ID  EPS  cash
STK_ID RPT_Date                   
601166 20111231  601166  NaN   NaN
600036 20111231  600036  NaN    12
600016 20111231  600016  4.3   NaN
601009 20111231  601009  NaN   NaN
601939 20111231  601939  2.5   NaN
000001 20111231  000001  NaN   NaN

... c'est-à-dire quelque chose comme df.drop(....)pour obtenir cette trame de données résultante:

                  STK_ID  EPS  cash
STK_ID RPT_Date                   
600016 20111231  600016  4.3   NaN
601939 20111231  601939  2.5   NaN

Comment je fais ça?



177
df.dropna(subset = ['column1_name', 'column2_name', 'column3_name'])
osa

Réponses:


655

Ne laissez pas tomber, prenez simplement les lignes où EPS n'est pas NA:

df = df[df['EPS'].notna()]

470
Je recommanderais d'utiliser pandas.notnullau lieu denp.isfinite
Wes McKinney

11
Y a-t-il un avantage à indexer et à copier par rapport à la suppression?
Robert Muil

9
Crée une erreur: TypeError: ufunc 'isfinite' n'est pas pris en charge pour les types d'entrée, et les entrées n'ont pas pu être forcées en toute sécurité à tous les types pris en charge conformément à la règle de transtypage `` sûr ''
Philipp Schwarz

4
@ wes-mckinney pourrait me faire savoir si dropna () est un meilleur choix que pandas.notnull dans ce cas? Si oui, alors pourquoi?
stormfield

4
@PhilippSchwarz Cette erreur se produit si la colonne ( EPSdans l'exemple) contient des chaînes ou d'autres types qui ne peuvent pas être digérés par np.isfinite(). Je recommande d'utiliser une pandas.notnull()solution plus généreuse.
normanius

902

Cette question est déjà résolue, mais ...

... considérez également la solution suggérée par Wouter dans son commentaire d'origine . La capacité de gérer les données manquantes, y compris dropna(), est explicitement intégrée aux pandas. Outre les performances potentiellement améliorées par rapport à une exécution manuelle, ces fonctions sont également fournies avec une variété d'options qui peuvent être utiles.

In [24]: df = pd.DataFrame(np.random.randn(10,3))

In [25]: df.iloc[::2,0] = np.nan; df.iloc[::4,1] = np.nan; df.iloc[::3,2] = np.nan;

In [26]: df
Out[26]:
          0         1         2
0       NaN       NaN       NaN
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
4       NaN       NaN  0.050742
5 -1.250970  0.030561 -2.678622
6       NaN  1.036043       NaN
7  0.049896 -0.308003  0.823295
8       NaN       NaN  0.637482
9 -0.310130  0.078891       NaN

In [27]: df.dropna()     #drop all rows that have any NaN values
Out[27]:
          0         1         2
1  2.677677 -1.466923 -0.750366
5 -1.250970  0.030561 -2.678622
7  0.049896 -0.308003  0.823295

In [28]: df.dropna(how='all')     #drop only if ALL columns are NaN
Out[28]:
          0         1         2
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
4       NaN       NaN  0.050742
5 -1.250970  0.030561 -2.678622
6       NaN  1.036043       NaN
7  0.049896 -0.308003  0.823295
8       NaN       NaN  0.637482
9 -0.310130  0.078891       NaN

In [29]: df.dropna(thresh=2)   #Drop row if it does not have at least two values that are **not** NaN
Out[29]:
          0         1         2
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
5 -1.250970  0.030561 -2.678622
7  0.049896 -0.308003  0.823295
9 -0.310130  0.078891       NaN

In [30]: df.dropna(subset=[1])   #Drop only if NaN in specific column (as asked in the question)
Out[30]:
          0         1         2
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
5 -1.250970  0.030561 -2.678622
6       NaN  1.036043       NaN
7  0.049896 -0.308003  0.823295
9 -0.310130  0.078891       NaN

Il existe également d'autres options (voir la documentation à http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html ), notamment la suppression de colonnes au lieu de lignes.

Assez pratique!


282
vous pouvez également utiliser df.dropna(subset = ['column_name']). J'espère que cela sauve au moins une personne les 5 secondes supplémentaires de «qu'est-ce que je fais mal». Grande réponse, +1
James Tobin

10
@JamesTobin, je viens de passer 20 minutes pour écrire une fonction pour ça! La documentation officielle était très cryptique: "Étiquettes le long d'un autre axe à considérer, par exemple si vous supprimez des lignes, ce serait une liste de colonnes à inclure". Je n'ai pas pu comprendre ce qu'ils signifiaient ...
osa

df.dropna(subset = ['column_name'])c'est exactement ce que je cherchais! Merci!
amalik2205

123

Je sais que cela a déjà été répondu, mais juste pour une solution purement pandas à cette question spécifique par opposition à la description générale d'Aman (qui était merveilleuse) et au cas où quelqu'un d'autre arriverait à ce sujet:

import pandas as pd
df = df[pd.notnull(df['EPS'])]

10
En fait, la réponse spécifique serait: df.dropna(subset=['EPS'])(basé sur la description générale d'Aman, bien sûr, cela fonctionne aussi)
joris

2
notnullc'est aussi ce que Wes (auteur de Pandas) a suggéré dans son commentaire sur une autre réponse.
Fantabolous

C'est peut-être une question noob. Mais quand je fais un df [pd.notnull (...) ou df.dropna, l'index est supprimé. Donc, s'il y avait une valeur nulle dans l'index de ligne 10 dans un df de longueur 200. Le cadre de données après avoir exécuté la fonction drop a des valeurs d'index de 1 à 9, puis de 11 à 200. Quoi qu'il en soit, "ré-indexez"
Aakash Gupta

vous pouvez également faire df[pd.notnull(df[df.columns[INDEX]])]INDEXserait la colonne numérotée si vous ne connaissez pas le nom
ocean800

60

Vous pouvez utiliser ceci:

df.dropna(subset=['EPS'], how='all', inplace=True)

18
how='all'est redondant ici, car vous sous-définissez la trame de données avec un seul champ, donc les deux 'all'et 'any'auront le même effet.
Anton Protopopov

35

La solution la plus simple de toutes:

filtered_df = df[df['EPS'].notnull()]

La solution ci-dessus est bien meilleure que d'utiliser np.isfinite ()


22

Vous pouvez utiliser la méthode dataframe notnull ou inverse de isnull ou numpy.isnan :

In [332]: df[df.EPS.notnull()]
Out[332]:
   STK_ID  RPT_Date  STK_ID.1  EPS  cash
2  600016  20111231    600016  4.3   NaN
4  601939  20111231    601939  2.5   NaN


In [334]: df[~df.EPS.isnull()]
Out[334]:
   STK_ID  RPT_Date  STK_ID.1  EPS  cash
2  600016  20111231    600016  4.3   NaN
4  601939  20111231    601939  2.5   NaN


In [347]: df[~np.isnan(df.EPS)]
Out[347]:
   STK_ID  RPT_Date  STK_ID.1  EPS  cash
2  600016  20111231    600016  4.3   NaN
4  601939  20111231    601939  2.5   NaN


10

encore une autre solution qui utilise le fait que np.nan != np.nan:

In [149]: df.query("EPS == EPS")
Out[149]:
                 STK_ID  EPS  cash
STK_ID RPT_Date
600016 20111231  600016  4.3   NaN
601939 20111231  601939  2.5   NaN

2

Une autre version:

df[~df['EPS'].isna()]

Pourquoi l'utiliser plus Series.notna()?
AMC

2

Dans les ensembles de données ayant un grand nombre de colonnes, il est préférable de voir combien de colonnes contiennent des valeurs nulles et combien n'en contiennent pas.

print("No. of columns containing null values")
print(len(df.columns[df.isna().any()]))

print("No. of columns not containing null values")
print(len(df.columns[df.notna().all()]))

print("Total no. of columns in the dataframe")
print(len(df.columns))

Par exemple, dans ma trame de données, il contenait 82 colonnes, dont 19 contenaient au moins une valeur nulle.

De plus, vous pouvez également supprimer automatiquement les colonnes et les lignes en fonction de celles qui ont le plus de valeurs
nulles.Voici le code qui le fait intelligemment:

df = df.drop(df.columns[df.isna().sum()>len(df.columns)],axis = 1)
df = df.dropna(axis = 0).reset_index(drop=True)

Remarque: le code ci-dessus supprime toutes vos valeurs nulles. Si vous voulez des valeurs nulles, traitez-les avant.


Il y a un autre lien
Pradeep Singh

0

Il peut être ajouté à ce que «&» peut être utilisé pour ajouter des conditions supplémentaires, par exemple

df = df[(df.EPS > 2.0) & (df.EPS <4.0)]

Notez que lors de l'évaluation des déclarations, les pandas ont besoin de parenthèses.


2
Désolé, mais OP veut autre chose. Btw, votre code est erroné, revenez ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().. Vous devez ajouter des parenthèses - df = df[(df.EPS > 2.0) & (df.EPS <4.0)], mais ce n'est pas non plus la réponse à cette question.
jezrael

-1

Pour une raison quelconque, aucune des réponses soumises précédemment n'a fonctionné pour moi. Cette solution de base a permis:

df = df[df.EPS >= 0]

Bien sûr, cela supprimera également les lignes avec des nombres négatifs. Donc, si vous en voulez, il est probablement judicieux d'ajouter cela après.

df = df[df.EPS <= 0]

Cela fait quelque chose de complètement différent, non?
AMC

-1

L'une des solutions peut être

df = df[df.isnull().sum(axis=1) <= Cutoff Value]

Une autre façon peut être

df= df.dropna(thresh=(df.shape[1] - Cutoff_value))

J'espère que ces informations sont utiles.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.