descartando valores infinitos de quadros de dados em pandas?

219

qual é a maneira mais rápida / simples de eliminar os valores nan e inf / -inf de um DataFrame do pandas sem redefinir mode.use_inf_as_null? Eu gostaria de poder usar os argumentos subsete , exceto com valores considerados ausentes, como:howdropnainf

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

Isso é possível? Existe uma maneira de dizer dropnapara incluir infem sua definição de valores ausentes?


fonte

Respostas:

416

A maneira mais simples seria primeiro replacecriar infs para NaN:

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

e depois use o dropna:

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

Por exemplo:

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

O mesmo método funcionaria para uma série.

Andy Hayden
fonte
2
Como se pode "trocar" os infvalores por valores predefinidos int, como 0em uma determinada coluna?
3kstc 11/0418
4
@ 3kstc use .replace(..., 0). Apenas para fazer em colunas de atualizar essas colunas ou sejadf[cols] = df[cols].replace(..., 0)
Andy Hayden
3
Talvez valha a pena especificar que replacenão funciona no local, para que um novo DataFrameseja retornado
Marco
36

Com o contexto da opção, isso é possível sem definir permanentemente use_inf_as_na. Por exemplo:

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

Claro que pode ser configurado para tratar infcomo NaNpermanentemente com

pd.set_option('use_inf_as_na', True)

Para versões mais antigas, substitua use_inf_as_napor use_inf_as_null.

ayhan
fonte
6
Essa é a resposta mais legível e, consequentemente, a melhor, embora viole em letra (mas não em espírito) a pergunta original.
ijoseph
2
Pandas a partir de (pelo menos) 0,24: use_inf_as_nullfoi descontinuado e será removido em uma versão futura. Use em use_inf_as_navez disso. Adicionar / atualizar resposta?
Håkon T.
1
Essa é uma opção melhor para tratar infcomo nulos nos níveis de configuração global em vez de no nível operacional. Isso pode potencialmente economizar tempo imputando os valores primeiro.
TaoPR 01/10/19
15

Aqui está outro método usado .locpara substituir inf por nan em uma série:

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

Então, em resposta à pergunta original:

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
fonte
11

Use (rápido e simples):

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

Essa resposta é baseada na resposta da DougR em outra pergunta. Aqui está um código de exemplo:

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

Resultado:

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

Outra solução seria usar o isinmétodo. Use-o para determinar se cada valor é infinito ou ausente e, em seguida, encadeie o allmétodo para determinar se todos os valores nas linhas são infinitos ou ausentes.

Por fim, use a negação desse resultado para selecionar as linhas que não possuem todos os valores infinitos ou ausentes por meio de indexação booleana.

all_inf_or_nan = df.isin([np.inf, -np.inf, np.nan]).all(axis='columns')
df[~all_inf_or_nan]
Ted Petrou
fonte
7

A solução acima modificará os infs que não estão nas colunas de destino. Para remediar isso,

lst = [np.inf, -np.inf]
to_replace = {v: lst for v in ['col1', 'col2']}
df.replace(to_replace, np.nan)
has2k1
fonte
3
pitão 2,7 e superiores comprehensions suporte de dicionários:{v: lst for v in cols}
Aryeh Leib Taurog
4

Você pode usar pd.DataFrame.maskcom np.isinf. Você deve garantir primeiro que sua série de quadros de dados seja do tipo float. Em seguida, use dropnacom sua lógica existente.

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
fonte