Jak upuszczać wiersze Pandas DataFrame, których wartość w określonej kolumnie to NaN

751

Mam to DataFramei chcę tylko rekordy, których EPSkolumna nie jest 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

... czyli coś w rodzaju df.drop(....)uzyskania tej wynikowej ramki danych:

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

Jak mogę to zrobić?

wielki robak
źródło
176
df.dropna(subset = ['column1_name', 'column2_name', 'column3_name'])
osa

Odpowiedzi:

654

Nie upuszczaj, po prostu weź wiersze, w których EPS nie jest NA:

df = df[df['EPS'].notna()]
eumiro
źródło
470
Polecam użycie pandas.notnullzamiastnp.isfinite
Wes McKinney
11
Czy indeksowanie i kopiowanie ma przewagę nad upuszczaniem?
Robert Muil
9
Tworzy błąd: TypeError: ufunc 'isfinite' nie jest obsługiwany dla typów danych wejściowych, a danych wejściowych nie można bezpiecznie przekonwertować na dowolny obsługiwany typ zgodnie z zasadą rzutowania „bezpieczny”
Philipp Schwarz
4
@ wes-mckinney może dać mi znać, jeśli dropna () jest lepszym wyborem niż pandas.notnull w tym przypadku? Jeśli tak, to dlaczego?
Stormfield
4
@PhilippSchwarz Ten błąd występuje, jeśli kolumna ( EPSw tym przykładzie) zawiera ciągi znaków lub inne typy, których nie można przetrawić np.isfinite(). Polecam użyć pandas.notnull(), które poradzi sobie z tym hojniej.
normanius
902

To pytanie zostało już rozwiązane, ale ...

... rozważ także rozwiązanie zaproponowane przez Woutera w jego oryginalnym komentarzu . Możliwość obsługi brakujących danych, w tym dropna(), jest wbudowana w pandy jawnie. Oprócz potencjalnie lepszej wydajności niż robienie tego ręcznie, funkcje te zawierają również szereg opcji, które mogą być przydatne.

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

Istnieją również inne opcje (patrz dokumenty na http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html ), w tym usuwanie kolumn zamiast wierszy.

Bardzo przydatne!

Mężczyzna
źródło
281
możesz także użyć df.dropna(subset = ['column_name']). Mam nadzieję, że zaoszczędzi przynajmniej jednej osobie dodatkowe 5 sekund „co robię źle”. Świetna odpowiedź, +1
James Tobin
10
@JamesTobin, właśnie spędziłem 20 minut na napisanie funkcji do tego! Oficjalna dokumentacja była bardzo tajemnicza: „Etykiety wzdłuż innej osi do rozważenia, np. Jeśli upuszczasz wiersze, byłaby to lista kolumn do uwzględnienia”. Nie mogłem zrozumieć, co mieli na myśli ...
osa
df.dropna(subset = ['column_name'])jest dokładnie tym, czego szukałem! Dzięki!
amalik2205
123

Wiem, że już na nie odpowiedziano, ale tylko ze względu na czysto pandowe rozwiązanie tego konkretnego pytania, w przeciwieństwie do ogólnego opisu Amana (który był cudowny) i na wypadek, gdyby ktokolwiek zdarzył się na to:

import pandas as pd
df = df[pd.notnull(df['EPS'])]
Kirk Hadley
źródło
10
Właściwie konkretna odpowiedź brzmiałaby: df.dropna(subset=['EPS'])(w oparciu o ogólny opis Amana, oczywiście to również działa)
joris,
2
notnulljest również tym, co Wes (autor Pandas) zasugerował w swoim komentarzu do innej odpowiedzi.
fantastyczny
To może pytanie noob. Ale kiedy robię df [pd.notnull (...) lub df.dropna, indeks zostaje usunięty. Więc jeśli w indeksie wiersza 10 była wartość zerowa w df o długości 200. Ramka danych po uruchomieniu funkcji upuszczania ma wartości indeksu od 1 do 9, a następnie od 11 do 200. W każdym razie „
przeindeksować
możesz także zrobić, df[pd.notnull(df[df.columns[INDEX]])]gdzie INDEXbyłaby kolumna numerowana, jeśli nie znasz nazwy
ocean800
60

Możesz użyć tego:

df.dropna(subset=['EPS'], how='all', inplace=True)
Joe
źródło
18
how='all'tutaj jest zbędne, ponieważ podzbiorów dataframe tylko z jednej dziedzinie, tak jak 'all'i 'any'będzie miał ten sam efekt.
Anton Protopopov
35

Najprostsze ze wszystkich rozwiązań:

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

Powyższe rozwiązanie jest znacznie lepsze niż użycie np.isfinite ()

Gil Baggio
źródło
22

Możesz użyć metody dataframe notnull lub inverse of isnull lub 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
Anton Protopopov
źródło
10

jeszcze inne rozwiązanie, które wykorzystuje fakt, że 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
MaxU
źródło
2

Inna wersja:

df[~df['EPS'].isna()]
keramat
źródło
Po co z tego korzystać Series.notna()?
AMC
2

W zestawach danych o dużej liczbie kolumn jeszcze lepiej jest zobaczyć, ile kolumn zawiera wartości null, a ile nie.

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

Na przykład w mojej ramce danych zawierał 82 kolumny, z których 19 zawierało co najmniej jedną wartość null.

Ponadto możesz również automatycznie usuwać kolumny i wiersze w zależności od tego, która ma więcej wartości zerowych.
Oto kod, który robi to inteligentnie:

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

Uwaga: powyższy kod usuwa wszystkie wartości zerowe. Jeśli chcesz mieć wartości zerowe, przetworz je wcześniej.

Pradeep Singh
źródło
Jest jeszcze jedno pytanie Link
Pradeep Singh
0

Można dodać, że „&” może być użyte do dodania dodatkowych warunków, np

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

Zauważ, że podczas oceny instrukcji pandy potrzebują nawiasów.

David
źródło
2
Przepraszamy, ale OP chce czegoś innego. Przy okazji, twój kod jest zły, powróć ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().. Musisz dodać nawias - df = df[(df.EPS > 2.0) & (df.EPS <4.0)]ale także nie jest to odpowiedź na to pytanie.
jezrael
-1

Z jakiegoś powodu żadna z wcześniej przesłanych odpowiedzi nie działała dla mnie. To podstawowe rozwiązanie:

df = df[df.EPS >= 0]

Chociaż oczywiście spowoduje to również upuszczenie wierszy z liczbami ujemnymi. Więc jeśli chcesz tych, prawdopodobnie warto też dodać to później.

df = df[df.EPS <= 0]
samthebrand
źródło
To robi coś zupełnie innego, nie?
AMC
-1

Jednym z rozwiązań może być

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

Innym sposobem może być

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

Mam nadzieję, że są one przydatne.

Amit Gupta
źródło