upuszczasz nieskończone wartości z ramek danych w pandach?

220

jaki jest najszybszy / najprostszy sposób na usunięcie wartości nan i inf / inf z pandy DataFrame bez resetowania mode.use_inf_as_null? Chciałbym móc używać argumentów subseti howargumentów dropna, z wyjątkiem infwartości uważanych za brakujące, takich jak:

df.dropna(subset=["col1", "col2"], how="all", with_inf=True)

czy to możliwe? Czy istnieje sposób, aby powiedzieć, dropnaaby uwzględnić infw definicji brakujących wartości?


źródło

Odpowiedzi:

417

Najprostszym sposobem byłoby pierwsze replaceinformacje na NaN:

df.replace([np.inf, -np.inf], np.nan)

a następnie użyj dropna:

df.replace([np.inf, -np.inf], np.nan).dropna(subset=["col1", "col2"], how="all")

Na przykład:

In [11]: df = pd.DataFrame([1, 2, np.inf, -np.inf])

In [12]: df.replace([np.inf, -np.inf], np.nan)
Out[12]:
    0
0   1
1   2
2 NaN
3 NaN

Ta sama metoda działałaby dla serii.

Andy Hayden
źródło
2
Jak można „wymienić” infwartości na predefiniowane, inttakie jak 0w określonej kolumnie?
3kstc
4
@ 3kstc use .replace(..., 0). Aby to zrobić tylko w kolumnach, zaktualizuj te kolumny, tj.df[cols] = df[cols].replace(..., 0)
Andy Hayden,
3
Może warto sprecyzować, że replaceto nie działa w miejscu, więc DataFramepowrócił nowy
Marco
36

W kontekście opcji jest to możliwe bez stałego ustawienia use_inf_as_na. Na przykład:

with pd.option_context('mode.use_inf_as_na', True):
    df = df.dropna(subset=['col1', 'col2'], how='all')

Oczywiście można go ustawić tak, aby traktował go inftak samo jak na NaNstałe

pd.set_option('use_inf_as_na', True)

W starszych wersjach, wymienić use_inf_as_naz use_inf_as_null.

ayhan
źródło
6
Jest to najbardziej czytelna odpowiedź, a zatem najlepsza, mimo że narusza literowe (ale nie duchowe) pierwotne pytanie.
ijoseph
2
Pandy od (co najmniej) 0,24: use_inf_as_nullzostały wycofane i zostaną usunięte w przyszłej wersji. Użyj use_inf_as_nazamiast tego. Dodaj / zaktualizuj odpowiedź?
Håkon T.
1
Ten jest lepszym wyborem do traktowania infjako wartości zerowe na poziomach ustawień globalnych zamiast na poziomie operacyjnym. Może to potencjalnie zaoszczędzić czas, przypisując najpierw wartości.
TaoPR,
15

Oto kolejna metoda polegająca .locna zamianie inf na nan w serii:

s.loc[(~np.isfinite(s)) & s.notnull()] = np.nan

W odpowiedzi na pierwotne pytanie:

df = pd.DataFrame(np.ones((3, 3)), columns=list('ABC'))

for i in range(3): 
    df.iat[i, i] = np.inf

df
          A         B         C
0       inf  1.000000  1.000000
1  1.000000       inf  1.000000
2  1.000000  1.000000       inf

df.sum()
A    inf
B    inf
C    inf
dtype: float64

df.apply(lambda s: s[np.isfinite(s)].dropna()).sum()
A    2
B    2
C    2
dtype: float64
Alexander
źródło
11

Użyj (szybki i prosty):

df = df[np.isfinite(df).all(1)]

Ta odpowiedź opiera się na odpowiedzi DougR na inne pytanie. Oto przykładowy kod:

import pandas as pd
import numpy as np
df=pd.DataFrame([1,2,3,np.nan,4,np.inf,5,-np.inf,6])
print('Input:\n',df,sep='')
df = df[np.isfinite(df).all(1)]
print('\nDropped:\n',df,sep='')

Wynik:

Input:
    0
0  1.0000
1  2.0000
2  3.0000
3     NaN
4  4.0000
5     inf
6  5.0000
7    -inf
8  6.0000

Dropped:
     0
0  1.0
1  2.0
2  3.0
4  4.0
6  5.0
8  6.0
Markus Dutschke
źródło
7

Jeszcze innym rozwiązaniem byłoby użycie tej isinmetody. Użyj go, aby ustalić, czy każda wartość jest nieskończona, czy jej brakuje, a następnie połącz allmetodę, aby ustalić, czy wszystkie wartości w wierszach są nieskończone, czy ich brakuje.

Na koniec użyj negacji tego wyniku, aby wybrać wiersze, które nie mają wszystkich nieskończonych lub brakujących wartości, poprzez indeksowanie boolowskie.

all_inf_or_nan = df.isin([np.inf, -np.inf, np.nan]).all(axis='columns')
df[~all_inf_or_nan]
Ted Petrou
źródło
7

Powyższe rozwiązanie zmodyfikuje te inf, które nie znajdują się w kolumnach docelowych. Aby temu zaradzić,

lst = [np.inf, -np.inf]
to_replace = {v: lst for v in ['col1', 'col2']}
df.replace(to_replace, np.nan)
has2k1
źródło
3
Python 2.7 i wyższe rozumieją dyktando wsparcia:{v: lst for v in cols}
Aryeh Leib Taurog
4

Możesz używać pd.DataFrame.maskz np.isinf. Najpierw upewnij się, że wszystkie serie ramek danych są typu float. Następnie użyj dropnaz istniejącą logiką.

print(df)

       col1      col2
0 -0.441406       inf
1 -0.321105      -inf
2 -0.412857  2.223047
3 -0.356610  2.513048

df = df.mask(np.isinf(df))

print(df)

       col1      col2
0 -0.441406       NaN
1 -0.321105       NaN
2 -0.412857  2.223047
3 -0.356610  2.513048
jpp
źródło