Come eliminare righe di Pandas DataFrame il cui valore in una determinata colonna è NaN


754

Ho questo DataFramee voglio solo i record la cui EPScolonna non è 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

... vale a dire qualcosa come df.drop(....)ottenere questo frame di dati risultante:

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

Come lo faccio?



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

Risposte:


655

Non rilasciare, prendi solo le righe in cui EPS non è NA:

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

470
Consiglierei di usare pandas.notnullinvece dinp.isfinite
Wes McKinney il

11
C'è qualche vantaggio nell'indicizzazione e nella copia rispetto alla caduta?
Robert Muil,

9
Crea errore: TypeError: ufunc 'isfinite' non supportato per i tipi di input e gli input non possono essere forzati in modo sicuro su alcun tipo supportato in base alla regola di casting '' safe ''
Philipp Schwarz,

4
@ wes-mckinney potrebbe per favore farmi sapere se dropna () è una scelta migliore rispetto a pandas.notnull in questo caso? Se è così, allora perché?
Stormfield,

4
@PhilippSchwarz Questo errore si verifica se la colonna ( EPSnell'esempio) contiene stringhe o altri tipi che non possono essere digeriti np.isfinite(). Raccomando di usare pandas.notnull()che gestirà questo più generosamente.
normanio,

902

Questa domanda è già stata risolta, ma ...

... considera anche la soluzione suggerita da Wouter nel suo commento originale . La capacità di gestire i dati mancanti, incluso dropna(), è esplicitamente integrata nei panda. A parte le prestazioni potenzialmente migliorate rispetto a farlo manualmente, queste funzioni includono anche una varietà di opzioni che possono essere utili.

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

Esistono anche altre opzioni (vedere i documenti su http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html ), incluso il rilascio di colonne anziché di righe.

Molto utile!


282
puoi anche usare df.dropna(subset = ['column_name']). Spero che risparmi ad almeno una persona i 5 secondi in più di "cosa sto facendo di sbagliato". Ottima risposta, +1
James Tobin,

10
@JamesTobin, ho appena trascorso 20 minuti per scrivere una funzione per questo! La documentazione ufficiale era molto enigmatica: "Etichette lungo l'altro asse da considerare, ad esempio se si stanno lasciando cadere le righe, si tratterebbe di un elenco di colonne da includere". Non riuscivo a capire cosa significassero ...
osa,

df.dropna(subset = ['column_name'])è esattamente quello che stavo cercando! Grazie!
amalik2205,

123

So che è già stata data una risposta, ma solo per una soluzione puramente panda a questa domanda specifica rispetto alla descrizione generale di Aman (che è stata meravigliosa) e nel caso in cui qualcuno accada su questo:

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

10
In realtà, la risposta specifica sarebbe: df.dropna(subset=['EPS'])(sulla base della descrizione generale di Aman, ovviamente anche questo funziona)
joris

2
notnullè anche ciò che Wes (autore di Pandas) ha suggerito nel suo commento su un'altra risposta.
fantabolous,

Questa forse è una domanda noob. Ma quando faccio un df [pd.notnull (...) o df.dropna l'indice viene lasciato cadere. Quindi se ci fosse un valore nullo nell'indice di riga 10 in un df di lunghezza 200. Il frame di dati dopo aver eseguito la funzione di rilascio ha valori di indice da 1 a 9 e quindi da 11 a 200. In ogni caso per "
reindicizzare

potresti anche fare df[pd.notnull(df[df.columns[INDEX]])]dove INDEXsarebbe la colonna numerata se non conosci il nome
ocean800

60

Puoi usare questo:

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

18
how='all'è ridondante qui, perché si esegue la subsetting di un solo frame di dati con un solo campo, quindi entrambi 'all'e 'any'avranno lo stesso effetto.
Anton Protopopov,

35

La più semplice di tutte le soluzioni:

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

La soluzione sopra è molto meglio dell'uso di np.isfinite ()


22

È possibile utilizzare il metodo dataframe notnull o inverse di isnull o 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

ancora un'altra soluzione che utilizza il fatto che 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

Un'altra versione:

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

Perché usarlo oltre Series.notna()?
AMC

2

Nei set di dati con un numero elevato di colonne è ancora meglio vedere quante colonne contengono valori null e quante no.

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))

Ad esempio nel mio frame di dati conteneva 82 colonne, di cui 19 contenevano almeno un valore null.

Inoltre puoi anche rimuovere automaticamente i col e le righe a seconda di quale ha più valori null
Ecco il codice che lo fa in modo intelligente:

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

Nota: il codice sopra rimuove tutti i valori null. Se desideri valori null, elaborali prima.


C'è un altro link alla
Pradeep Singh,

0

Si può aggiungere che '&' può essere usato per aggiungere ulteriori condizioni ad es

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

Si noti che quando si valutano le dichiarazioni, i panda hanno bisogno di parentesi.


2
Spiacente, ma OP vuole qualcos'altro. A proposito, il tuo codice è sbagliato, ritorna ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().. È necessario aggiungere parentesi - df = df[(df.EPS > 2.0) & (df.EPS <4.0)], ma anche non è una risposta a questa domanda.
jezrael

-1

Per qualche motivo nessuna delle risposte precedentemente inviate ha funzionato per me. Questa soluzione di base ha fatto:

df = df[df.EPS >= 0]

Anche se ovviamente questo lascerà cadere anche le righe con numeri negativi. Quindi, se vuoi quelli, probabilmente è intelligente aggiungere anche questo dopo.

df = df[df.EPS <= 0]

Questo fa qualcosa di completamente diverso, no?
AMC

-1

Una delle soluzioni può essere

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

Un altro modo può essere

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

Spero che siano utili.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.